sync
[roojs1] / roojs-bootstrap-debug.js
index 9e39fb8..c421bb9 100644 (file)
@@ -95,6 +95,7 @@ Roo.Shadow.prototype = {
      * frame: Shadow displays equally on all four sides<br />
      * drop: Traditional bottom-right drop shadow (default)
      */
+    mode: false,
     /**
      * @cfg {String} offset
      * The number of pixels to offset the shadow from the element (defaults to 4)
@@ -7307,4638 +7308,4801 @@ Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
  * Fork - LGPL
  * <script type="text/javascript">
  */
+ /**
+ * @extends Roo.dd.DDProxy
+ * @class Roo.grid.SplitDragZone
+ * Support for Column Header resizing
+ * @constructor
+ * @param {Object} config
+ */
+// private
+// This is a support class used internally by the Grid components
+Roo.grid.SplitDragZone = function(grid, hd, hd2){
+    this.grid = grid;
+    this.view = grid.getView();
+    this.proxy = this.view.resizeProxy;
+    Roo.grid.SplitDragZone.superclass.constructor.call(
+        this,
+        hd, // ID
+        "gridSplitters" + this.grid.getGridEl().id, // SGROUP
+        {  // CONFIG
+            dragElId : Roo.id(this.proxy.dom),
+            resizeFrame:false
+        }
+    );
+    
+    this.setHandleElId(Roo.id(hd));
+    if (hd2 !== false) {
+        this.setOuterHandleElId(Roo.id(hd2));
+    }
+    
+    this.scroll = false;
+};
+Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
+    fly: Roo.Element.fly,
+
+    b4StartDrag : function(x, y){
+        this.view.headersDisabled = true;
+        var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
+                    this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
+        );
+        this.proxy.setHeight(h);
+        
+        // for old system colWidth really stored the actual width?
+        // in bootstrap we tried using xs/ms/etc.. to do % sizing?
+        // which in reality did not work.. - it worked only for fixed sizes
+        // for resizable we need to use actual sizes.
+        var w = this.cm.getColumnWidth(this.cellIndex);
+        if (!this.view.mainWrap) {
+            // bootstrap.
+            w = this.view.getHeaderIndex(this.cellIndex).getWidth();
+        }
+        
+        
+        
+        // this was w-this.grid.minColumnWidth;
+        // doesnt really make sense? - w = thie curren width or the rendered one?
+        var minw = Math.max(w-this.grid.minColumnWidth, 0);
+        this.resetConstraints();
+        this.setXConstraint(minw, 1000);
+        this.setYConstraint(0, 0);
+        this.minX = x - minw;
+        this.maxX = x + 1000;
+        this.startPos = x;
+        if (!this.view.mainWrap) { // this is Bootstrap code..
+            this.getDragEl().style.display='block';
+        }
+        
+        Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
+    },
+
+
+    handleMouseDown : function(e){
+        ev = Roo.EventObject.setEvent(e);
+        var t = this.fly(ev.getTarget());
+        if(t.hasClass("x-grid-split")){
+            this.cellIndex = this.view.getCellIndex(t.dom);
+            this.split = t.dom;
+            this.cm = this.grid.colModel;
+            if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
+                Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
+            }
+        }
+    },
+
+    endDrag : function(e){
+        this.view.headersDisabled = false;
+        var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
+        var diff = endX - this.startPos;
+        // 
+        var w = this.cm.getColumnWidth(this.cellIndex);
+        if (!this.view.mainWrap) {
+            w = 0;
+        }
+        this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
+    },
+
+    autoOffset : function(){
+        this.setDelta(0,0);
+    }
+});/*
+ * 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.ColumnModel
+ * @class Roo.grid.AbstractSelectionModel
  * @extends Roo.util.Observable
- * This is the default implementation of a ColumnModel used by the Grid. It defines
- * the columns in the grid.
- * <br>Usage:<br>
- <pre><code>
- var colModel = new Roo.grid.ColumnModel([
-       {header: "Ticker", width: 60, sortable: true, locked: true},
-       {header: "Company Name", width: 150, sortable: true},
-       {header: "Market Cap.", width: 100, sortable: true},
-       {header: "$ Sales", width: 100, sortable: true, renderer: money},
-       {header: "Employees", width: 100, sortable: true, resizable: false}
- ]);
- </code></pre>
- * <p>
- * The config options listed for this class are options which may appear in each
- * individual column definition.
- * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
+ * Abstract base class for grid SelectionModels.  It provides the interface that should be
+ * implemented by descendant classes.  This class should not be directly instantiated.
  * @constructor
- * @param {Object} config An Array of column config objects. See this class's
- * config objects for details.
-*/
-Roo.grid.ColumnModel = function(config){
-       /**
-     * The config passed into the constructor
-     */
-    this.config = []; //config;
-    this.lookup = {};
+ */
+Roo.grid.AbstractSelectionModel = function(){
+    this.locked = false;
+    Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
+};
 
-    // if no id, create one
-    // if the column does not have a dataIndex mapping,
-    // map it to the order it is in the config
-    for(var i = 0, len = config.length; i < len; i++){
-       this.addColumn(config[i]);
-       
-    }
+Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
+    /** @ignore Called by the grid automatically. Do not call directly. */
+    init : function(grid){
+        this.grid = grid;
+        this.initEvents();
+    },
 
     /**
-     * The width of columns which have no width specified (defaults to 100)
-     * @type Number
+     * Locks the selections.
      */
-    this.defaultWidth = 100;
+    lock : function(){
+        this.locked = true;
+    },
 
     /**
-     * Default sortable of columns which have no sortable specified (defaults to false)
-     * @type Boolean
+     * Unlocks the selections.
      */
-    this.defaultSortable = false;
+    unlock : function(){
+        this.locked = false;
+    },
+
+    /**
+     * Returns true if the selections are locked.
+     * @return {Boolean}
+     */
+    isLocked : function(){
+        return this.locked;
+    }
+});/*
+ * 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">
+ */
+/**
+ * @extends Roo.grid.AbstractSelectionModel
+ * @class Roo.grid.RowSelectionModel
+ * The default SelectionModel used by {@link Roo.grid.Grid}.
+ * It supports multiple selections and keyboard selection/navigation. 
+ * @constructor
+ * @param {Object} config
+ */
+Roo.grid.RowSelectionModel = function(config){
+    Roo.apply(this, config);
+    this.selections = new Roo.util.MixedCollection(false, function(o){
+        return o.id;
+    });
+
+    this.last = false;
+    this.lastActive = false;
 
     this.addEvents({
         /**
-            * @event widthchange
-            * Fires when the width of a column changes.
-            * @param {ColumnModel} this
-            * @param {Number} columnIndex The column index
-            * @param {Number} newWidth The new width
-            */
-           "widthchange": true,
-        /**
-            * @event headerchange
-            * Fires when the text of a header changes.
-            * @param {ColumnModel} this
-            * @param {Number} columnIndex The column index
-            * @param {Number} newText The new header text
-            */
-           "headerchange": true,
-        /**
-            * @event hiddenchange
-            * Fires when a column is hidden or "unhidden".
-            * @param {ColumnModel} this
-            * @param {Number} columnIndex The column index
-            * @param {Boolean} hidden true if hidden, false otherwise
-            */
-           "hiddenchange": true,
-           /**
-         * @event columnmoved
-         * Fires when a column is moved.
-         * @param {ColumnModel} this
-         * @param {Number} oldIndex
-         * @param {Number} newIndex
-         */
-        "columnmoved" : true,
-        /**
-         * @event columlockchange
-         * Fires when a column's locked state is changed
-         * @param {ColumnModel} this
-         * @param {Number} colIndex
-         * @param {Boolean} locked true if locked
-         */
-        "columnlockchange" : true
+        * @event selectionchange
+        * Fires when the selection changes
+        * @param {SelectionModel} this
+        */
+       "selectionchange" : true,
+       /**
+        * @event afterselectionchange
+        * Fires after the selection changes (eg. by key press or clicking)
+        * @param {SelectionModel} this
+        */
+       "afterselectionchange" : true,
+       /**
+        * @event beforerowselect
+        * Fires when a row is selected being selected, return false to cancel.
+        * @param {SelectionModel} this
+        * @param {Number} rowIndex The selected index
+        * @param {Boolean} keepExisting False if other selections will be cleared
+        */
+       "beforerowselect" : true,
+       /**
+        * @event rowselect
+        * Fires when a row is selected.
+        * @param {SelectionModel} this
+        * @param {Number} rowIndex The selected index
+        * @param {Roo.data.Record} r The record
+        */
+       "rowselect" : true,
+       /**
+        * @event rowdeselect
+        * Fires when a row is deselected.
+        * @param {SelectionModel} this
+        * @param {Number} rowIndex The selected index
+        */
+        "rowdeselect" : true
     });
-    Roo.grid.ColumnModel.superclass.constructor.call(this);
+    Roo.grid.RowSelectionModel.superclass.constructor.call(this);
+    this.locked = false;
 };
-Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
-    /**
-     * @cfg {String} header The header text to display in the Grid view.
-     */
-    /**
-     * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
-     * {@link Roo.data.Record} definition from which to draw the column's value. If not
-     * specified, the column's index is used as an index into the Record's data Array.
-     */
-    /**
-     * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
-     * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
-     */
-    /**
-     * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
-     * Defaults to the value of the {@link #defaultSortable} property.
-     * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
-     */
-    /**
-     * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
-     */
-    /**
-     * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
-     */
-    /**
-     * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
-     */
-    /**
-     * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
-     */
-    /**
-     * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
-     * given the cell's data value. See {@link #setRenderer}. If not specified, the
-     * default renderer returns the escaped data value. If an object is returned (bootstrap only)
-     * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
-     */
-       /**
-     * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
-     */
-    /**
-     * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
-     */
-    /**
-     * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
-     */
-    /**
-     * @cfg {String} cursor (Optional)
-     */
-    /**
-     * @cfg {String} tooltip (Optional)
-     */
-    /**
-     * @cfg {Number} xs (Optional)
-     */
-    /**
-     * @cfg {Number} sm (Optional)
-     */
-    /**
-     * @cfg {Number} md (Optional)
-     */
-    /**
-     * @cfg {Number} lg (Optional)
-     */
-    /**
-     * Returns the id of the column at the specified index.
-     * @param {Number} index The column index
-     * @return {String} the id
-     */
-    getColumnId : function(index){
-        return this.config[index].id;
-    },
 
+Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
     /**
-     * Returns the column for a specified id.
-     * @param {String} id The column id
-     * @return {Object} the column
+     * @cfg {Boolean} singleSelect
+     * True to allow selection of only one row at a time (defaults to false)
      */
-    getColumnById : function(id){
-        return this.lookup[id];
-    },
+    singleSelect : false,
 
-    
-    /**
-     * Returns the column Object for a specified dataIndex.
-     * @param {String} dataIndex The column dataIndex
-     * @return {Object|Boolean} the column or false if not found
-     */
-    getColumnByDataIndex: function(dataIndex){
-        var index = this.findColumnIndex(dataIndex);
-        return index > -1 ? this.config[index] : false;
-    },
-    
-    /**
-     * Returns the index for a specified column id.
-     * @param {String} id The column id
-     * @return {Number} the index, or -1 if not found
-     */
-    getIndexById : function(id){
-        for(var i = 0, len = this.config.length; i < len; i++){
-            if(this.config[i].id == id){
-                return i;
-            }
+    // private
+    initEvents : function(){
+
+        if(!this.grid.enableDragDrop && !this.grid.enableDrag){
+            this.grid.on("mousedown", this.handleMouseDown, this);
+        }else{ // allow click to work like normal
+            this.grid.on("rowclick", this.handleDragableRowClick, this);
         }
-        return -1;
+        // bootstrap does not have a view..
+        var view = this.grid.view ? this.grid.view : this.grid;
+        this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
+            "up" : function(e){
+                if(!e.shiftKey){
+                    this.selectPrevious(e.shiftKey);
+                }else if(this.last !== false && this.lastActive !== false){
+                    var last = this.last;
+                    this.selectRange(this.last,  this.lastActive-1);
+                    view.focusRow(this.lastActive);
+                    if(last !== false){
+                        this.last = last;
+                    }
+                }else{
+                    this.selectFirstRow();
+                }
+                this.fireEvent("afterselectionchange", this);
+            },
+            "down" : function(e){
+                if(!e.shiftKey){
+                    this.selectNext(e.shiftKey);
+                }else if(this.last !== false && this.lastActive !== false){
+                    var last = this.last;
+                    this.selectRange(this.last,  this.lastActive+1);
+                    view.focusRow(this.lastActive);
+                    if(last !== false){
+                        this.last = last;
+                    }
+                }else{
+                    this.selectFirstRow();
+                }
+                this.fireEvent("afterselectionchange", this);
+            },
+            scope: this
+        });
+
+         
+        view.on("refresh", this.onRefresh, this);
+        view.on("rowupdated", this.onRowUpdated, this);
+        view.on("rowremoved", this.onRemove, this);
     },
-    
-    /**
-     * Returns the index for a specified column dataIndex.
-     * @param {String} dataIndex The column dataIndex
-     * @return {Number} the index, or -1 if not found
-     */
-    
-    findColumnIndex : function(dataIndex){
-        for(var i = 0, len = this.config.length; i < len; i++){
-            if(this.config[i].dataIndex == dataIndex){
-                return i;
+
+    // private
+    onRefresh : function(){
+        var ds = this.grid.ds, i, v = this.grid.view;
+        var s = this.selections;
+        s.each(function(r){
+            if((i = ds.indexOfId(r.id)) != -1){
+                v.onRowSelect(i);
+                s.add(ds.getAt(i)); // updating the selection relate data
+            }else{
+                s.remove(r);
             }
-        }
-        return -1;
-    },
-    
-    
-    moveColumn : function(oldIndex, newIndex){
-        var c = this.config[oldIndex];
-        this.config.splice(oldIndex, 1);
-        this.config.splice(newIndex, 0, c);
-        this.dataMap = null;
-        this.fireEvent("columnmoved", this, oldIndex, newIndex);
+        });
     },
 
-    isLocked : function(colIndex){
-        return this.config[colIndex].locked === true;
+    // private
+    onRemove : function(v, index, r){
+        this.selections.remove(r);
     },
 
-    setLocked : function(colIndex, value, suppressEvent){
-        if(this.isLocked(colIndex) == value){
-            return;
-        }
-        this.config[colIndex].locked = value;
-        if(!suppressEvent){
-            this.fireEvent("columnlockchange", this, colIndex, value);
+    // private
+    onRowUpdated : function(v, index, r){
+        if(this.isSelected(r)){
+            v.onRowSelect(index);
         }
     },
 
-    getTotalLockedWidth : function(){
-        var totalWidth = 0;
-        for(var i = 0; i < this.config.length; i++){
-            if(this.isLocked(i) && !this.isHidden(i)){
-                this.totalWidth += this.getColumnWidth(i);
-            }
+    /**
+     * Select records.
+     * @param {Array} records The records to select
+     * @param {Boolean} keepExisting (optional) True to keep existing selections
+     */
+    selectRecords : function(records, keepExisting){
+        if(!keepExisting){
+            this.clearSelections();
         }
-        return totalWidth;
-    },
-
-    getLockedCount : function(){
-        for(var i = 0, len = this.config.length; i < len; i++){
-            if(!this.isLocked(i)){
-                return i;
-            }
+        var ds = this.grid.ds;
+        for(var i = 0, len = records.length; i < len; i++){
+            this.selectRow(ds.indexOf(records[i]), true);
         }
-        
-        return this.config.length;
     },
 
     /**
-     * Returns the number of columns.
+     * Gets the number of selected rows.
      * @return {Number}
      */
-    getColumnCount : function(visibleOnly){
-        if(visibleOnly === true){
-            var c = 0;
-            for(var i = 0, len = this.config.length; i < len; i++){
-                if(!this.isHidden(i)){
-                    c++;
-                }
-            }
-            return c;
-        }
-        return this.config.length;
+    getCount : function(){
+        return this.selections.length;
     },
 
     /**
-     * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
-     * @param {Function} fn
-     * @param {Object} scope (optional)
-     * @return {Array} result
+     * Selects the first row in the grid.
      */
-    getColumnsBy : function(fn, scope){
-        var r = [];
-        for(var i = 0, len = this.config.length; i < len; i++){
-            var c = this.config[i];
-            if(fn.call(scope||this, c, i) === true){
-                r[r.length] = c;
-            }
-        }
-        return r;
+    selectFirstRow : function(){
+        this.selectRow(0);
     },
 
     /**
-     * Returns true if the specified column is sortable.
-     * @param {Number} col The column index
-     * @return {Boolean}
+     * Select the last row.
+     * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
-    isSortable : function(col){
-        if(typeof this.config[col].sortable == "undefined"){
-            return this.defaultSortable;
-        }
-        return this.config[col].sortable;
+    selectLastRow : function(keepExisting){
+        this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
     },
 
     /**
-     * Returns the rendering (formatting) function defined for the column.
-     * @param {Number} col The column index.
-     * @return {Function} The function used to render the cell. See {@link #setRenderer}.
+     * Selects the row immediately following the last selected row.
+     * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
-    getRenderer : function(col){
-        if(!this.config[col].renderer){
-            return Roo.grid.ColumnModel.defaultRenderer;
+    selectNext : function(keepExisting){
+        if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
+            this.selectRow(this.last+1, keepExisting);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.focusRow(this.last);
         }
-        return this.config[col].renderer;
     },
 
     /**
-     * Sets the rendering (formatting) function for a column.
-     * @param {Number} col The column index
-     * @param {Function} fn The function to use to process the cell's raw data
-     * to return HTML markup for the grid view. The render function is called with
-     * the following parameters:<ul>
-     * <li>Data value.</li>
-     * <li>Cell metadata. An object in which you may set the following attributes:<ul>
-     * <li>css A CSS style string to apply to the table cell.</li>
-     * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
-     * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
-     * <li>Row index</li>
-     * <li>Column index</li>
-     * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
+     * Selects the row that precedes the last selected row.
+     * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
-    setRenderer : function(col, fn){
-        this.config[col].renderer = fn;
+    selectPrevious : function(keepExisting){
+        if(this.last){
+            this.selectRow(this.last-1, keepExisting);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.focusRow(this.last);
+        }
     },
 
     /**
-     * Returns the width for the specified column.
-     * @param {Number} col The column index
-     * @return {Number}
+     * Returns the selected records
+     * @return {Array} Array of selected records
      */
-    getColumnWidth : function(col){
-        return this.config[col].width * 1 || this.defaultWidth;
+    getSelections : function(){
+        return [].concat(this.selections.items);
     },
 
     /**
-     * Sets the width for a column.
-     * @param {Number} col The column index
-     * @param {Number} width The new width
+     * Returns the first selected record.
+     * @return {Record}
      */
-    setColumnWidth : function(col, width, suppressEvent){
-        this.config[col].width = width;
-        this.totalWidth = null;
-        if(!suppressEvent){
-             this.fireEvent("widthchange", this, col, width);
-        }
+    getSelected : function(){
+        return this.selections.itemAt(0);
     },
 
+
     /**
-     * Returns the total width of all columns.
-     * @param {Boolean} includeHidden True to include hidden column widths
-     * @return {Number}
+     * Clears all selections.
      */
-    getTotalWidth : function(includeHidden){
-        if(!this.totalWidth){
-            this.totalWidth = 0;
-            for(var i = 0, len = this.config.length; i < len; i++){
-                if(includeHidden || !this.isHidden(i)){
-                    this.totalWidth += this.getColumnWidth(i);
-                }
-            }
+    clearSelections : function(fast){
+        if(this.locked) {
+            return;
         }
-        return this.totalWidth;
+        if(fast !== true){
+            var ds = this.grid.ds;
+            var s = this.selections;
+            s.each(function(r){
+                this.deselectRow(ds.indexOfId(r.id));
+            }, this);
+            s.clear();
+        }else{
+            this.selections.clear();
+        }
+        this.last = false;
     },
 
+
     /**
-     * Returns the header for the specified column.
-     * @param {Number} col The column index
-     * @return {String}
+     * Selects all rows.
      */
-    getColumnHeader : function(col){
-        return this.config[col].header;
-    },
-
-    /**
-     * Sets the header for a column.
-     * @param {Number} col The column index
-     * @param {String} header The new header
-     */
-    setColumnHeader : function(col, header){
-        this.config[col].header = header;
-        this.fireEvent("headerchange", this, col, header);
-    },
-
-    /**
-     * Returns the tooltip for the specified column.
-     * @param {Number} col The column index
-     * @return {String}
-     */
-    getColumnTooltip : function(col){
-            return this.config[col].tooltip;
-    },
-    /**
-     * Sets the tooltip for a column.
-     * @param {Number} col The column index
-     * @param {String} tooltip The new tooltip
-     */
-    setColumnTooltip : function(col, tooltip){
-            this.config[col].tooltip = tooltip;
+    selectAll : function(){
+        if(this.locked) {
+            return;
+        }
+        this.selections.clear();
+        for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
+            this.selectRow(i, true);
+        }
     },
 
     /**
-     * Returns the dataIndex for the specified column.
-     * @param {Number} col The column index
-     * @return {Number}
+     * Returns True if there is a selection.
+     * @return {Boolean}
      */
-    getDataIndex : function(col){
-        return this.config[col].dataIndex;
+    hasSelection : function(){
+        return this.selections.length > 0;
     },
 
     /**
-     * Sets the dataIndex for a column.
-     * @param {Number} col The column index
-     * @param {Number} dataIndex The new dataIndex
+     * Returns True if the specified row is selected.
+     * @param {Number/Record} record The record or index of the record to check
+     * @return {Boolean}
      */
-    setDataIndex : function(col, dataIndex){
-        this.config[col].dataIndex = dataIndex;
+    isSelected : function(index){
+        var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
+        return (r && this.selections.key(r.id) ? true : false);
     },
 
-    
-    
     /**
-     * Returns true if the cell is editable.
-     * @param {Number} colIndex The column index
-     * @param {Number} rowIndex The row index - this is nto actually used..?
+     * Returns True if the specified record id is selected.
+     * @param {String} id The id of record to check
      * @return {Boolean}
      */
-    isCellEditable : function(colIndex, rowIndex){
-        return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
+    isIdSelected : function(id){
+        return (this.selections.key(id) ? true : false);
     },
 
-    /**
-     * Returns the editor defined for the cell/column.
-     * return false or null to disable editing.
-     * @param {Number} colIndex The column index
-     * @param {Number} rowIndex The row index
-     * @return {Object}
-     */
-    getCellEditor : function(colIndex, rowIndex){
-        return this.config[colIndex].editor;
+    // private
+    handleMouseDown : function(e, t)
+    {
+        var view = this.grid.view ? this.grid.view : this.grid;
+        var rowIndex;
+        if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
+            return;
+        };
+        if(e.shiftKey && this.last !== false){
+            var last = this.last;
+            this.selectRange(last, rowIndex, e.ctrlKey);
+            this.last = last; // reset the last
+            view.focusRow(rowIndex);
+        }else{
+            var isSelected = this.isSelected(rowIndex);
+            if(e.button !== 0 && isSelected){
+                view.focusRow(rowIndex);
+            }else if(e.ctrlKey && isSelected){
+                this.deselectRow(rowIndex);
+            }else if(!isSelected){
+                this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
+                view.focusRow(rowIndex);
+            }
+        }
+        this.fireEvent("afterselectionchange", this);
     },
-
-    /**
-     * Sets if a column is editable.
-     * @param {Number} col The column index
-     * @param {Boolean} editable True if the column is editable
-     */
-    setEditable : function(col, editable){
-        this.config[col].editable = editable;
+    // private
+    handleDragableRowClick :  function(grid, rowIndex, e) 
+    {
+        if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
+            this.selectRow(rowIndex, false);
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.focusRow(rowIndex);
+             this.fireEvent("afterselectionchange", this);
+        }
     },
-
-
+    
     /**
-     * Returns true if the column is hidden.
-     * @param {Number} colIndex The column index
-     * @return {Boolean}
+     * Selects multiple rows.
+     * @param {Array} rows Array of the indexes of the row to select
+     * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
-    isHidden : function(colIndex){
-        return this.config[colIndex].hidden;
+    selectRows : function(rows, keepExisting){
+        if(!keepExisting){
+            this.clearSelections();
+        }
+        for(var i = 0, len = rows.length; i < len; i++){
+            this.selectRow(rows[i], true);
+        }
     },
 
-
     /**
-     * Returns true if the column width cannot be changed
+     * Selects a range of rows. All rows in between startRow and endRow are also selected.
+     * @param {Number} startRow The index of the first row in the range
+     * @param {Number} endRow The index of the last row in the range
+     * @param {Boolean} keepExisting (optional) True to retain existing selections
      */
-    isFixed : function(colIndex){
-        return this.config[colIndex].fixed;
+    selectRange : function(startRow, endRow, keepExisting){
+        if(this.locked) {
+            return;
+        }
+        if(!keepExisting){
+            this.clearSelections();
+        }
+        if(startRow <= endRow){
+            for(var i = startRow; i <= endRow; i++){
+                this.selectRow(i, true);
+            }
+        }else{
+            for(var i = startRow; i >= endRow; i--){
+                this.selectRow(i, true);
+            }
+        }
     },
 
     /**
-     * Returns true if the column can be resized
-     * @return {Boolean}
+     * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
+     * @param {Number} startRow The index of the first row in the range
+     * @param {Number} endRow The index of the last row in the range
      */
-    isResizable : function(colIndex){
-        return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
+    deselectRange : function(startRow, endRow, preventViewNotify){
+        if(this.locked) {
+            return;
+        }
+        for(var i = startRow; i <= endRow; i++){
+            this.deselectRow(i, preventViewNotify);
+        }
     },
+
     /**
-     * Sets if a column is hidden.
-     * @param {Number} colIndex The column index
-     * @param {Boolean} hidden True if the column is hidden
+     * Selects a row.
+     * @param {Number} row The index of the row to select
+     * @param {Boolean} keepExisting (optional) True to keep existing selections
      */
-    setHidden : function(colIndex, hidden){
-        this.config[colIndex].hidden = hidden;
-        this.totalWidth = null;
-        this.fireEvent("hiddenchange", this, colIndex, hidden);
+    selectRow : function(index, keepExisting, preventViewNotify){
+        if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
+            return;
+        }
+        if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
+            if(!keepExisting || this.singleSelect){
+                this.clearSelections();
+            }
+            var r = this.grid.ds.getAt(index);
+            this.selections.add(r);
+            this.last = this.lastActive = index;
+            if(!preventViewNotify){
+                var view = this.grid.view ? this.grid.view : this.grid;
+                view.onRowSelect(index);
+            }
+            this.fireEvent("rowselect", this, index, r);
+            this.fireEvent("selectionchange", this);
+        }
     },
 
     /**
-     * Sets the editor for a column.
-     * @param {Number} col The column index
-     * @param {Object} editor The editor object
+     * Deselects a row.
+     * @param {Number} row The index of the row to deselect
      */
-    setEditor : function(col, editor){
-        this.config[col].editor = editor;
-    },
-    /**
-     * Add a column (experimental...) - defaults to adding to the end..
-     * @param {Object} config 
-    */
-    addColumn : function(c)
-    {
-    
-       var i = this.config.length;
-       this.config[i] = c;
-       
-       if(typeof c.dataIndex == "undefined"){
-            c.dataIndex = i;
+    deselectRow : function(index, preventViewNotify){
+        if(this.locked) {
+            return;
         }
-        if(typeof c.renderer == "string"){
-            c.renderer = Roo.util.Format[c.renderer];
+        if(this.last == index){
+            this.last = false;
         }
-        if(typeof c.id == "undefined"){
-            c.id = Roo.id();
+        if(this.lastActive == index){
+            this.lastActive = false;
         }
-        if(c.editor && c.editor.xtype){
-            c.editor  = Roo.factory(c.editor, Roo.grid);
+        var r = this.grid.ds.getAt(index);
+        this.selections.remove(r);
+        if(!preventViewNotify){
+            var view = this.grid.view ? this.grid.view : this.grid;
+            view.onRowDeselect(index);
         }
-        if(c.editor && c.editor.isFormField){
-            c.editor = new Roo.grid.GridEditor(c.editor);
+        this.fireEvent("rowdeselect", this, index);
+        this.fireEvent("selectionchange", this);
+    },
+
+    // private
+    restoreLast : function(){
+        if(this._last){
+            this.last = this._last;
         }
-        this.lookup[c.id] = c;
-    }
-    
-});
+    },
 
-Roo.grid.ColumnModel.defaultRenderer = function(value)
-{
-    if(typeof value == "object") {
-        return value;
-    }
-       if(typeof value == "string" && value.length < 1){
-           return "&#160;";
-       }
-    
-       return String.format("{0}", value);
-};
+    // private
+    acceptsNav : function(row, col, cm){
+        return !cm.isHidden(col) && cm.isCellEditable(col, row);
+    },
 
-// Alias for backwards compatibility
-Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
-/*
- * 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.
+    // private
+    onEditorKey : function(field, e){
+        var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
+        if(k == e.TAB){
+            e.stopEvent();
+            ed.completeEdit();
+            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);
+            }
+        }else if(k == e.ENTER && !e.ctrlKey){
+            e.stopEvent();
+            ed.completeEdit();
+            if(e.shiftKey){
+                newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
+            }else{
+                newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
+            }
+        }else if(k == e.ESC){
+            ed.cancelEdit();
+        }
+        if(newCell){
+            g.startEditing(newCell[0], newCell[1]);
+        }
+    }
+});/*
+ * 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.
+ * @class Roo.grid.ColumnModel
+ * @extends Roo.util.Observable
+ * This is the default implementation of a ColumnModel used by the Grid. It defines
+ * the columns in the grid.
+ * <br>Usage:<br>
+ <pre><code>
+ var colModel = new Roo.grid.ColumnModel([
+       {header: "Ticker", width: 60, sortable: true, locked: true},
+       {header: "Company Name", width: 150, sortable: true},
+       {header: "Market Cap.", width: 100, sortable: true},
+       {header: "$ Sales", width: 100, sortable: true, renderer: money},
+       {header: "Employees", width: 100, sortable: true, resizable: false}
+ ]);
+ </code></pre>
+ * <p>
+ * The config options listed for this class are options which may appear in each
+ * individual column definition.
+ * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
  * @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;
+ * @param {Object} config An Array of column config objects. See this class's
+ * config objects for details.
+*/
+Roo.grid.ColumnModel = function(config){
+       /**
+     * The config passed into the constructor
+     */
+    this.config = []; //config;
+    this.lookup = {};
+
+    // if no id, create one
+    // if the column does not have a dataIndex mapping,
+    // map it to the order it is in the config
+    for(var i = 0, len = config.length; i < len; i++){
+       this.addColumn(config[i]);
+       
     }
-};
 
-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.
+     * The width of columns which have no width specified (defaults to 100)
+     * @type Number
      */
+    this.defaultWidth = 100;
+
     /**
-     * @cfg {String} msg
-     * The text to display in a centered loading message box (defaults to 'Loading...')
+     * Default sortable of columns which have no sortable specified (defaults to false)
+     * @type Boolean
      */
-    msg : 'Loading...',
+    this.defaultSortable = false;
+
+    this.addEvents({
+        /**
+            * @event widthchange
+            * Fires when the width of a column changes.
+            * @param {ColumnModel} this
+            * @param {Number} columnIndex The column index
+            * @param {Number} newWidth The new width
+            */
+           "widthchange": true,
+        /**
+            * @event headerchange
+            * Fires when the text of a header changes.
+            * @param {ColumnModel} this
+            * @param {Number} columnIndex The column index
+            * @param {Number} newText The new header text
+            */
+           "headerchange": true,
+        /**
+            * @event hiddenchange
+            * Fires when a column is hidden or "unhidden".
+            * @param {ColumnModel} this
+            * @param {Number} columnIndex The column index
+            * @param {Boolean} hidden true if hidden, false otherwise
+            */
+           "hiddenchange": true,
+           /**
+         * @event columnmoved
+         * Fires when a column is moved.
+         * @param {ColumnModel} this
+         * @param {Number} oldIndex
+         * @param {Number} newIndex
+         */
+        "columnmoved" : true,
+        /**
+         * @event columlockchange
+         * Fires when a column's locked state is changed
+         * @param {ColumnModel} this
+         * @param {Number} colIndex
+         * @param {Boolean} locked true if locked
+         */
+        "columnlockchange" : true
+    });
+    Roo.grid.ColumnModel.superclass.constructor.call(this);
+};
+Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
     /**
-     * @cfg {String} msgCls
-     * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
+     * @cfg {String} header The header text to display in the Grid view.
+     */
+       /**
+     * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
+     */
+       /**
+     * @cfg {String} smHeader Header at Bootsrap Small width
+     */
+       /**
+     * @cfg {String} mdHeader Header at Bootsrap Medium width
+     */
+       /**
+     * @cfg {String} lgHeader Header at Bootsrap Large width
+     */
+       /**
+     * @cfg {String} xlHeader Header at Bootsrap extra Large width
      */
-    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
+     * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
+     * {@link Roo.data.Record} definition from which to draw the column's value. If not
+     * specified, the column's index is used as an index into the Record's data Array.
      */
-    disabled: false,
+    /**
+     * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
+     * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
+     */
+    /**
+     * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
+     * Defaults to the value of the {@link #defaultSortable} property.
+     * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
+     */
+    /**
+     * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
+     */
+    /**
+     * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
+     */
+    /**
+     * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
+     */
+    /**
+     * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
+     */
+    /**
+     * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
+     * given the cell's data value. See {@link #setRenderer}. If not specified, the
+     * default renderer returns the escaped data value. If an object is returned (bootstrap only)
+     * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
+     */
+       /**
+     * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
+     */
+    /**
+     * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
+     */
+    /**
+     * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
+     */
+    /**
+     * @cfg {String} cursor (Optional)
+     */
+    /**
+     * @cfg {String} tooltip (Optional)
+     */
+    /**
+     * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
+     */
+    /**
+     * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
+     */
+    /**
+     * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
+     */
+    /**
+     * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
+     */
+       /**
+     * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
+     */
+    /**
+     * Returns the id of the column at the specified index.
+     * @param {Number} index The column index
+     * @return {String} the id
+     */
+    getColumnId : function(index){
+        return this.config[index].id;
+    },
 
     /**
-     * Disables the mask to prevent it from being displayed
+     * Returns the column for a specified id.
+     * @param {String} id The column id
+     * @return {Object} the column
      */
-    disable : function(){
-       this.disabled = true;
+    getColumnById : function(id){
+        return this.lookup[id];
     },
 
+    
     /**
-     * Enables the mask so that it can be displayed
+     * Returns the column Object for a specified dataIndex.
+     * @param {String} dataIndex The column dataIndex
+     * @return {Object|Boolean} the column or false if not found
      */
-    enable : function(){
-        this.disabled = false;
+    getColumnByDataIndex: function(dataIndex){
+        var index = this.findColumnIndex(dataIndex);
+        return index > -1 ? this.config[index] : false;
     },
     
-    onLoadException : function()
-    {
-        Roo.log(arguments);
-        
-        if (typeof(arguments[3]) != 'undefined') {
-            Roo.MessageBox.alert("Error loading",arguments[3]);
-        } 
-        /*
-        try {
-            if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
-                Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
-            }   
-        } catch(e) {
-            
+    /**
+     * Returns the index for a specified column id.
+     * @param {String} id The column id
+     * @return {Number} the index, or -1 if not found
+     */
+    getIndexById : function(id){
+        for(var i = 0, len = this.config.length; i < len; i++){
+            if(this.config[i].id == id){
+                return i;
+            }
         }
-        */
+        return -1;
+    },
     
-        (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+    /**
+     * Returns the index for a specified column dataIndex.
+     * @param {String} dataIndex The column dataIndex
+     * @return {Number} the index, or -1 if not found
+     */
+    
+    findColumnIndex : function(dataIndex){
+        for(var i = 0, len = this.config.length; i < len; i++){
+            if(this.config[i].dataIndex == dataIndex){
+                return i;
+            }
+        }
+        return -1;
     },
-    // private
-    onLoad : function()
-    {
-        (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+    
+    
+    moveColumn : function(oldIndex, newIndex){
+        var c = this.config[oldIndex];
+        this.config.splice(oldIndex, 1);
+        this.config.splice(newIndex, 0, c);
+        this.dataMap = null;
+        this.fireEvent("columnmoved", this, oldIndex, newIndex);
     },
 
-    // private
-    onBeforeLoad : function(){
-        if(!this.disabled){
-            (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
+    isLocked : function(colIndex){
+        return this.config[colIndex].locked === true;
+    },
+
+    setLocked : function(colIndex, value, suppressEvent){
+        if(this.isLocked(colIndex) == value){
+            return;
+        }
+        this.config[colIndex].locked = value;
+        if(!suppressEvent){
+            this.fireEvent("columnlockchange", this, colIndex, value);
         }
     },
 
-    // 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);
+    getTotalLockedWidth : function(){
+        var totalWidth = 0;
+        for(var i = 0; i < this.config.length; i++){
+            if(this.isLocked(i) && !this.isHidden(i)){
+                this.totalWidth += this.getColumnWidth(i);
+            }
         }
-    }
-};/*
- * - LGPL
- *
- * table
- * 
- */
+        return totalWidth;
+    },
 
-/**
- * @class Roo.bootstrap.Table
- * @extends Roo.bootstrap.Component
- * Bootstrap Table class
- * @cfg {String} cls table class
- * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
- * @cfg {String} bgcolor Specifies the background color for a table
- * @cfg {Number} border Specifies whether the table cells should have borders or not
- * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
- * @cfg {Number} cellspacing Specifies the space between cells
- * @cfg {String} frame Specifies which parts of the outside borders that should be visible
- * @cfg {String} rules Specifies which parts of the inside borders that should be visible
- * @cfg {String} sortable Specifies that the table should be sortable
- * @cfg {String} summary Specifies a summary of the content of a table
- * @cfg {Number} width Specifies the width of a table
- * @cfg {String} layout table layout (auto | fixed | initial | inherit)
- * 
- * @cfg {boolean} striped Should the rows be alternative striped
- * @cfg {boolean} bordered Add borders to the table
- * @cfg {boolean} hover Add hover highlighting
- * @cfg {boolean} condensed Format condensed
- * @cfg {boolean} responsive Format condensed
- * @cfg {Boolean} loadMask (true|false) default false
- * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
- * @cfg {Boolean} headerShow (true|false) generate thead, default true
- * @cfg {Boolean} rowSelection (true|false) default false
- * @cfg {Boolean} cellSelection (true|false) default false
- * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
- * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
- * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
- * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
- * 
- * @constructor
- * Create a new Table
- * @param {Object} config The config object
- */
+    getLockedCount : function(){
+        for(var i = 0, len = this.config.length; i < len; i++){
+            if(!this.isLocked(i)){
+                return i;
+            }
+        }
+        
+        return this.config.length;
+    },
 
-Roo.bootstrap.Table = function(config){
-    Roo.bootstrap.Table.superclass.constructor.call(this, config);
-    
-  
-    
-    // BC...
-    this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
-    this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
-    this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
-    this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
-    
-    this.sm = this.sm || {xtype: 'RowSelectionModel'};
-    if (this.sm) {
-        this.sm.grid = this;
-        this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
-        this.sm = this.selModel;
-        this.sm.xmodule = this.xmodule || false;
-    }
-    
-    if (this.cm && typeof(this.cm.config) == 'undefined') {
-        this.colModel = new Roo.grid.ColumnModel(this.cm);
-        this.cm = this.colModel;
-        this.cm.xmodule = this.xmodule || false;
-    }
-    if (this.store) {
-        this.store= Roo.factory(this.store, Roo.data);
-        this.ds = this.store;
-        this.ds.xmodule = this.xmodule || false;
-         
-    }
-    if (this.footer && this.store) {
-        this.footer.dataSource = this.ds;
-        this.footer = Roo.factory(this.footer);
-    }
-    
-    /** @private */
-    this.addEvents({
-        /**
-         * @event cellclick
-         * Fires when a cell is clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Roo.Element} el
-         * @param {Number} rowIndex
-         * @param {Number} columnIndex
-         * @param {Roo.EventObject} e
-         */
-        "cellclick" : true,
-        /**
-         * @event celldblclick
-         * Fires when a cell is double clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Roo.Element} el
-         * @param {Number} rowIndex
-         * @param {Number} columnIndex
-         * @param {Roo.EventObject} e
-         */
-        "celldblclick" : true,
-        /**
-         * @event rowclick
-         * Fires when a row is clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Roo.Element} el
-         * @param {Number} rowIndex
-         * @param {Roo.EventObject} e
-         */
-        "rowclick" : true,
-        /**
-         * @event rowdblclick
-         * Fires when a row is double clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Roo.Element} el
-         * @param {Number} rowIndex
-         * @param {Roo.EventObject} e
-         */
-        "rowdblclick" : true,
-        /**
-         * @event mouseover
-         * Fires when a mouseover occur
-         * @param {Roo.bootstrap.Table} this
-         * @param {Roo.Element} el
-         * @param {Number} rowIndex
-         * @param {Number} columnIndex
-         * @param {Roo.EventObject} e
-         */
-        "mouseover" : true,
-        /**
-         * @event mouseout
-         * Fires when a mouseout occur
-         * @param {Roo.bootstrap.Table} this
-         * @param {Roo.Element} el
-         * @param {Number} rowIndex
-         * @param {Number} columnIndex
-         * @param {Roo.EventObject} e
-         */
-        "mouseout" : true,
-        /**
-         * @event rowclass
-         * Fires when a row is rendered, so you can change add a style to it.
-         * @param {Roo.bootstrap.Table} this
-         * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
-         */
-        'rowclass' : true,
-          /**
-         * @event rowsrendered
-         * Fires when all the  rows have been rendered
-         * @param {Roo.bootstrap.Table} this
-         */
-        'rowsrendered' : true,
-        /**
-         * @event contextmenu
-         * The raw contextmenu event for the entire grid.
-         * @param {Roo.EventObject} e
-         */
-        "contextmenu" : true,
-        /**
-         * @event rowcontextmenu
-         * Fires when a row is right clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Number} rowIndex
-         * @param {Roo.EventObject} e
-         */
-        "rowcontextmenu" : true,
-        /**
-         * @event cellcontextmenu
-         * Fires when a cell is right clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Number} rowIndex
-         * @param {Number} cellIndex
-         * @param {Roo.EventObject} e
-         */
-         "cellcontextmenu" : true,
-         /**
-         * @event headercontextmenu
-         * Fires when a header is right clicked
-         * @param {Roo.bootstrap.Table} this
-         * @param {Number} columnIndex
-         * @param {Roo.EventObject} e
-         */
-        "headercontextmenu" : true
-    });
-};
-
-Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
-    
-    cls: false,
-    align: false,
-    bgcolor: false,
-    border: false,
-    cellpadding: false,
-    cellspacing: false,
-    frame: false,
-    rules: false,
-    sortable: false,
-    summary: false,
-    width: false,
-    striped : false,
-    scrollBody : false,
-    bordered: false,
-    hover:  false,
-    condensed : false,
-    responsive : false,
-    sm : false,
-    cm : false,
-    store : false,
-    loadMask : false,
-    footerShow : true,
-    headerShow : true,
-  
-    rowSelection : false,
-    cellSelection : false,
-    layout : false,
-    
-    // Roo.Element - the tbody
-    mainBody: false,
-    // Roo.Element - thead element
-    mainHead: false,
-    
-    container: false, // used by gridpanel...
-    
-    lazyLoad : false,
-    
-    CSS : Roo.util.CSS,
-    
-    auto_hide_footer : false,
-    
-    getAutoCreate : function()
-    {
-        var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
-        
-        cfg = {
-            tag: 'table',
-            cls : 'table',
-            cn : []
-        };
-        if (this.scrollBody) {
-            cfg.cls += ' table-body-fixed';
-        }    
-        if (this.striped) {
-            cfg.cls += ' table-striped';
-        }
-        
-        if (this.hover) {
-            cfg.cls += ' table-hover';
-        }
-        if (this.bordered) {
-            cfg.cls += ' table-bordered';
-        }
-        if (this.condensed) {
-            cfg.cls += ' table-condensed';
-        }
-        if (this.responsive) {
-            cfg.cls += ' table-responsive';
-        }
-        
-        if (this.cls) {
-            cfg.cls+=  ' ' +this.cls;
-        }
-        
-        // this lot should be simplifed...
-        var _t = this;
-        var cp = [
-            'align',
-            'bgcolor',
-            'border',
-            'cellpadding',
-            'cellspacing',
-            'frame',
-            'rules',
-            'sortable',
-            'summary',
-            'width'
-        ].forEach(function(k) {
-            if (_t[k]) {
-                cfg[k] = _t[k];
+    /**
+     * Returns the number of columns.
+     * @return {Number}
+     */
+    getColumnCount : function(visibleOnly){
+        if(visibleOnly === true){
+            var c = 0;
+            for(var i = 0, len = this.config.length; i < len; i++){
+                if(!this.isHidden(i)){
+                    c++;
+                }
             }
-        });
-        
-        
-        if (this.layout) {
-            cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
+            return c;
         }
-        
-        if(this.store || this.cm){
-            if(this.headerShow){
-                cfg.cn.push(this.renderHeader());
-            }
-            
-            cfg.cn.push(this.renderBody());
-            
-            if(this.footerShow){
-                cfg.cn.push(this.renderFooter());
+        return this.config.length;
+    },
+
+    /**
+     * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
+     * @param {Function} fn
+     * @param {Object} scope (optional)
+     * @return {Array} result
+     */
+    getColumnsBy : function(fn, scope){
+        var r = [];
+        for(var i = 0, len = this.config.length; i < len; i++){
+            var c = this.config[i];
+            if(fn.call(scope||this, c, i) === true){
+                r[r.length] = c;
             }
-            // where does this come from?
-            //cfg.cls+=  ' TableGrid';
         }
-        
-        return { cn : [ cfg ] };
+        return r;
     },
-    
-    initEvents : function()
-    {   
-        if(!this.store || !this.cm){
-            return;
-        }
-        if (this.selModel) {
-            this.selModel.initEvents();
+
+    /**
+     * Returns true if the specified column is sortable.
+     * @param {Number} col The column index
+     * @return {Boolean}
+     */
+    isSortable : function(col){
+        if(typeof this.config[col].sortable == "undefined"){
+            return this.defaultSortable;
         }
-        
-        
-        //Roo.log('initEvents with ds!!!!');
-        
-        this.mainBody = this.el.select('tbody', true).first();
-        this.mainHead = this.el.select('thead', true).first();
-        this.mainFoot = this.el.select('tfoot', true).first();
-        
-        
-        
-        
-        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
-            e.on('click', this.sort, this);
-        }, this);
-        
-        this.mainBody.on("click", this.onClick, this);
-        this.mainBody.on("dblclick", this.onDblClick, this);
-        
-        // why is this done????? = it breaks dialogs??
-        //this.parent().el.setStyle('position', 'relative');
-        
-        
-        if (this.footer) {
-            this.footer.parentId = this.id;
-            this.footer.onRender(this.el.select('tfoot tr td').first(), null);
-            
-            if(this.lazyLoad){
-                this.el.select('tfoot tr td').first().addClass('hide');
-            }
-        } 
-        
-        if(this.loadMask) {
-            this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
+        return this.config[col].sortable;
+    },
+
+    /**
+     * Returns the rendering (formatting) function defined for the column.
+     * @param {Number} col The column index.
+     * @return {Function} The function used to render the cell. See {@link #setRenderer}.
+     */
+    getRenderer : function(col){
+        if(!this.config[col].renderer){
+            return Roo.grid.ColumnModel.defaultRenderer;
         }
-        
-        this.store.on('load', this.onLoad, this);
-        this.store.on('beforeload', this.onBeforeLoad, this);
-        this.store.on('update', this.onUpdate, this);
-        this.store.on('add', this.onAdd, this);
-        this.store.on("clear", this.clear, this);
-        
-        this.el.on("contextmenu", this.onContextMenu, this);
-        
-        this.mainBody.on('scroll', this.onBodyScroll, this);
-        
-        this.cm.on("headerchange", this.onHeaderChange, this);
-        
-        this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
-        
+        return this.config[col].renderer;
     },
-    
-    onContextMenu : function(e, t)
-    {
-        this.processEvent("contextmenu", e);
+
+    /**
+     * Sets the rendering (formatting) function for a column.
+     * @param {Number} col The column index
+     * @param {Function} fn The function to use to process the cell's raw data
+     * to return HTML markup for the grid view. The render function is called with
+     * the following parameters:<ul>
+     * <li>Data value.</li>
+     * <li>Cell metadata. An object in which you may set the following attributes:<ul>
+     * <li>css A CSS style string to apply to the table cell.</li>
+     * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
+     * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
+     * <li>Row index</li>
+     * <li>Column index</li>
+     * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
+     */
+    setRenderer : function(col, fn){
+        this.config[col].renderer = fn;
     },
-    
-    processEvent : function(name, e)
-    {
-        if (name != 'touchstart' ) {
-            this.fireEvent(name, e);    
-        }
-        
-        var t = e.getTarget();
-        
-        var cell = Roo.get(t);
-        
-        if(!cell){
-            return;
-        }
-        
-        if(cell.findParent('tfoot', false, true)){
-            return;
+
+    /**
+     * Returns the width for the specified column.
+     * @param {Number} col The column index
+     * @param (optional) {String} gridSize bootstrap width size.
+     * @return {Number}
+     */
+    getColumnWidth : function(col, gridSize)
+       {
+               var cfg = this.config[col];
+               
+               if (typeof(gridSize) == 'undefined') {
+                       return cfg.width * 1 || this.defaultWidth;
+               }
+               if (gridSize === false) { // if we set it..
+                       return cfg.width || false;
+               }
+               var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
+               
+               for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
+                       if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
+                               continue;
+                       }
+                       return cfg[ sizes[i] ];
+               }
+               return 1;
+               
+    },
+
+    /**
+     * Sets the width for a column.
+     * @param {Number} col The column index
+     * @param {Number} width The new width
+     */
+    setColumnWidth : function(col, width, suppressEvent){
+        this.config[col].width = width;
+        this.totalWidth = null;
+        if(!suppressEvent){
+             this.fireEvent("widthchange", this, col, width);
         }
-        
-        if(cell.findParent('thead', false, true)){
-            
-            if(e.getTarget().nodeName.toLowerCase() != 'th'){
-                cell = Roo.get(t).findParent('th', false, true);
-                if (!cell) {
-                    Roo.log("failed to find th in thead?");
-                    Roo.log(e.getTarget());
-                    return;
+    },
+
+    /**
+     * Returns the total width of all columns.
+     * @param {Boolean} includeHidden True to include hidden column widths
+     * @return {Number}
+     */
+    getTotalWidth : function(includeHidden){
+        if(!this.totalWidth){
+            this.totalWidth = 0;
+            for(var i = 0, len = this.config.length; i < len; i++){
+                if(includeHidden || !this.isHidden(i)){
+                    this.totalWidth += this.getColumnWidth(i);
                 }
             }
-            
-            var cellIndex = cell.dom.cellIndex;
-            
-            var ename = name == 'touchstart' ? 'click' : name;
-            this.fireEvent("header" + ename, this, cellIndex, e);
-            
-            return;
-        }
-        
-        if(e.getTarget().nodeName.toLowerCase() != 'td'){
-            cell = Roo.get(t).findParent('td', false, true);
-            if (!cell) {
-                Roo.log("failed to find th in tbody?");
-                Roo.log(e.getTarget());
-                return;
-            }
         }
-        
-        var row = cell.findParent('tr', false, true);
-        var cellIndex = cell.dom.cellIndex;
-        var rowIndex = row.dom.rowIndex - 1;
-        
-        if(row !== false){
-            
-            this.fireEvent("row" + name, this, rowIndex, e);
-            
-            if(cell !== false){
-            
-                this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
-            }
-        }
-        
+        return this.totalWidth;
     },
-    
-    onMouseover : function(e, el)
-    {
-        var cell = Roo.get(el);
-        
-        if(!cell){
-            return;
-        }
-        
-        if(e.getTarget().nodeName.toLowerCase() != 'td'){
-            cell = cell.findParent('td', false, true);
-        }
-        
-        var row = cell.findParent('tr', false, true);
-        var cellIndex = cell.dom.cellIndex;
-        var rowIndex = row.dom.rowIndex - 1; // start from 0
-        
-        this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
-        
+
+    /**
+     * Returns the header for the specified column.
+     * @param {Number} col The column index
+     * @return {String}
+     */
+    getColumnHeader : function(col){
+        return this.config[col].header;
     },
-    
-    onMouseout : function(e, el)
-    {
-        var cell = Roo.get(el);
-        
-        if(!cell){
-            return;
-        }
-        
-        if(e.getTarget().nodeName.toLowerCase() != 'td'){
-            cell = cell.findParent('td', false, true);
-        }
-        
-        var row = cell.findParent('tr', false, true);
-        var cellIndex = cell.dom.cellIndex;
-        var rowIndex = row.dom.rowIndex - 1; // start from 0
-        
-        this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
-        
+
+    /**
+     * Sets the header for a column.
+     * @param {Number} col The column index
+     * @param {String} header The new header
+     */
+    setColumnHeader : function(col, header){
+        this.config[col].header = header;
+        this.fireEvent("headerchange", this, col, header);
+    },
+
+    /**
+     * Returns the tooltip for the specified column.
+     * @param {Number} col The column index
+     * @return {String}
+     */
+    getColumnTooltip : function(col){
+            return this.config[col].tooltip;
+    },
+    /**
+     * Sets the tooltip for a column.
+     * @param {Number} col The column index
+     * @param {String} tooltip The new tooltip
+     */
+    setColumnTooltip : function(col, tooltip){
+            this.config[col].tooltip = tooltip;
+    },
+
+    /**
+     * Returns the dataIndex for the specified column.
+     * @param {Number} col The column index
+     * @return {Number}
+     */
+    getDataIndex : function(col){
+        return this.config[col].dataIndex;
+    },
+
+    /**
+     * Sets the dataIndex for a column.
+     * @param {Number} col The column index
+     * @param {Number} dataIndex The new dataIndex
+     */
+    setDataIndex : function(col, dataIndex){
+        this.config[col].dataIndex = dataIndex;
     },
+
     
-    onClick : function(e, el)
+    
+    /**
+     * Returns true if the cell is editable.
+     * @param {Number} colIndex The column index
+     * @param {Number} rowIndex The row index - this is nto actually used..?
+     * @return {Boolean}
+     */
+    isCellEditable : function(colIndex, rowIndex){
+        return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
+    },
+
+    /**
+     * Returns the editor defined for the cell/column.
+     * return false or null to disable editing.
+     * @param {Number} colIndex The column index
+     * @param {Number} rowIndex The row index
+     * @return {Object}
+     */
+    getCellEditor : function(colIndex, rowIndex){
+        return this.config[colIndex].editor;
+    },
+
+    /**
+     * Sets if a column is editable.
+     * @param {Number} col The column index
+     * @param {Boolean} editable True if the column is editable
+     */
+    setEditable : function(col, editable){
+        this.config[col].editable = editable;
+    },
+
+
+    /**
+     * Returns true if the column is hidden.
+     * @param {Number} colIndex The column index
+     * @return {Boolean}
+     */
+    isHidden : function(colIndex){
+        return this.config[colIndex].hidden;
+    },
+
+
+    /**
+     * Returns true if the column width cannot be changed
+     */
+    isFixed : function(colIndex){
+        return this.config[colIndex].fixed;
+    },
+
+    /**
+     * Returns true if the column can be resized
+     * @return {Boolean}
+     */
+    isResizable : function(colIndex){
+        return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
+    },
+    /**
+     * Sets if a column is hidden.
+     * @param {Number} colIndex The column index
+     * @param {Boolean} hidden True if the column is hidden
+     */
+    setHidden : function(colIndex, hidden){
+        this.config[colIndex].hidden = hidden;
+        this.totalWidth = null;
+        this.fireEvent("hiddenchange", this, colIndex, hidden);
+    },
+
+    /**
+     * Sets the editor for a column.
+     * @param {Number} col The column index
+     * @param {Object} editor The editor object
+     */
+    setEditor : function(col, editor){
+        this.config[col].editor = editor;
+    },
+    /**
+     * Add a column (experimental...) - defaults to adding to the end..
+     * @param {Object} config 
+    */
+    addColumn : function(c)
     {
-        var cell = Roo.get(el);
-        
-        if(!cell || (!this.cellSelection && !this.rowSelection)){
-            return;
+    
+       var i = this.config.length;
+       this.config[i] = c;
+       
+       if(typeof c.dataIndex == "undefined"){
+            c.dataIndex = i;
         }
-        
-        if(e.getTarget().nodeName.toLowerCase() != 'td'){
-            cell = cell.findParent('td', false, true);
+        if(typeof c.renderer == "string"){
+            c.renderer = Roo.util.Format[c.renderer];
         }
-        
-        if(!cell || typeof(cell) == 'undefined'){
-            return;
+        if(typeof c.id == "undefined"){
+            c.id = Roo.id();
         }
-        
-        var row = cell.findParent('tr', false, true);
-        
-        if(!row || typeof(row) == 'undefined'){
-            return;
+        if(c.editor && c.editor.xtype){
+            c.editor  = Roo.factory(c.editor, Roo.grid);
         }
-        
-        var cellIndex = cell.dom.cellIndex;
-        var rowIndex = this.getRowIndex(row);
-        
-        // why??? - should these not be based on SelectionModel?
-        //if(this.cellSelection){
-            this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
-        //}
-        
-        //if(this.rowSelection){
-            this.fireEvent('rowclick', this, row, rowIndex, e);
-        //}
-         
+        if(c.editor && c.editor.isFormField){
+            c.editor = new Roo.grid.GridEditor(c.editor);
+        }
+        this.lookup[c.id] = c;
+    }
+    
+});
+
+Roo.grid.ColumnModel.defaultRenderer = function(value)
+{
+    if(typeof value == "object") {
+        return value;
+    }
+       if(typeof value == "string" && value.length < 1){
+           return "&#160;";
+       }
+    
+       return String.format("{0}", value);
+};
+
+// Alias for backwards compatibility
+Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
+/*
+ * 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.
+     */
+    removeMask : 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;
     },
-        
-    onDblClick : function(e,el)
+
+    /**
+     * Enables the mask so that it can be displayed
+     */
+    enable : function(){
+        this.disabled = false;
+    },
+    
+    onLoadException : function()
     {
-        var cell = Roo.get(el);
-        
-        if(!cell || (!this.cellSelection && !this.rowSelection)){
-            return;
-        }
-        
-        if(e.getTarget().nodeName.toLowerCase() != 'td'){
-            cell = cell.findParent('td', false, true);
-        }
-        
-        if(!cell || typeof(cell) == 'undefined'){
-            return;
-        }
-        
-        var row = cell.findParent('tr', false, true);
-        
-        if(!row || typeof(row) == 'undefined'){
-            return;
-        }
-        
-        var cellIndex = cell.dom.cellIndex;
-        var rowIndex = this.getRowIndex(row);
-        
-        if(this.cellSelection){
-            this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
-        }
+        Roo.log(arguments);
         
-        if(this.rowSelection){
-            this.fireEvent('rowdblclick', this, row, rowIndex, e);
+        if (typeof(arguments[3]) != 'undefined') {
+            Roo.MessageBox.alert("Error loading",arguments[3]);
+        } 
+        /*
+        try {
+            if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+                Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+            }   
+        } catch(e) {
+            
         }
-    },
+        */
     
-    sort : function(e,el)
+        (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+    },
+    // private
+    onLoad : function()
     {
-        var col = Roo.get(el);
-        
-        if(!col.hasClass('sortable')){
-            return;
+        (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+    },
+
+    // private
+    onBeforeLoad : function(){
+        if(!this.disabled){
+            (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
         }
-        
-        var sort = col.attr('sort');
-        var dir = 'ASC';
-        
-        if(col.select('i', true).first().hasClass('fa-arrow-up')){
-            dir = 'DESC';
+    },
+
+    // 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);
         }
-        
-        this.store.sortInfo = {field : sort, direction : dir};
-        
-        if (this.footer) {
-            Roo.log("calling footer first");
-            this.footer.onClick('first');
-        } else {
-        
-            this.store.load({ params : { start : 0 } });
+    }
+};/**
+ * @class Roo.bootstrap.Table
+ * @licence LGBL
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
+ * Similar to Roo.grid.Grid
+ * <pre><code>
+ var table = Roo.factory({
+    xtype : 'Table',
+    xns : Roo.bootstrap,
+    autoSizeColumns: true,
+    
+    
+    store : {
+        xtype : 'Store',
+        xns : Roo.data,
+        remoteSort : true,
+        sortInfo : { direction : 'ASC', field: 'name' },
+        proxy : {
+           xtype : 'HttpProxy',
+           xns : Roo.data,
+           method : 'GET',
+           url : 'https://example.com/some.data.url.json'
+        },
+        reader : {
+           xtype : 'JsonReader',
+           xns : Roo.data,
+           fields : [ 'id', 'name', whatever' ],
+           id : 'id',
+           root : 'data'
         }
     },
+    cm : [
+        {
+            xtype : 'ColumnModel',
+            xns : Roo.grid,
+            align : 'center',
+            cursor : 'pointer',
+            dataIndex : 'is_in_group',
+            header : "Name",
+            sortable : true,
+            renderer : function(v, x , r) {  
+            
+                return String.format("{0}", v)
+            }
+            width : 3
+        } // more columns..
+    ],
+    selModel : {
+        xtype : 'RowSelectionModel',
+        xns : Roo.bootstrap.Table
+        // you can add listeners to catch selection change here....
+    }
+     
+
+ });
+ // set any options
+ grid.render(Roo.get("some-div"));
+</code></pre>
+
+Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
+
+
+
+ *
+ * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
+ * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
+ * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
+ * 
+ * @cfg {String} cls table class
+ *
+ * 
+ * @cfg {boolean} striped Should the rows be alternative striped
+ * @cfg {boolean} bordered Add borders to the table
+ * @cfg {boolean} hover Add hover highlighting
+ * @cfg {boolean} condensed Format condensed
+ * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
+ *                also adds table-responsive (see bootstrap docs for details)
+ * @cfg {Boolean} loadMask (true|false) default false
+ * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
+ * @cfg {Boolean} headerShow (true|false) generate thead, default true
+ * @cfg {Boolean} rowSelection (true|false) default false
+ * @cfg {Boolean} cellSelection (true|false) default false
+ * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
+ * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
+ * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
+ * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
+ * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
+ * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
+ * 
+ * @constructor
+ * Create a new Table
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Table = function(config)
+{
+    Roo.bootstrap.Table.superclass.constructor.call(this, config);
+     
+    // BC...
+    this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
+    this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
+    this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
+    this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
     
-    renderHeader : function()
-    {
-        var header = {
-            tag: 'thead',
-            cn : []
-        };
-        
-        var cm = this.cm;
-        this.totalWidth = 0;
-        
-        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
-            
-            var config = cm.config[i];
-            
-            var c = {
-                tag: 'th',
-                cls : 'x-hcol-' + i,
-                style : '',
-                
-                html: cm.getColumnHeader(i)
-            };
-            
-            var tooltip = cm.getColumnTooltip(i);
-            if (tooltip) {
-                c.tooltip = tooltip;
-            }
-            
-            
-            var hh = '';
-            
-            if(typeof(config.sortable) != 'undefined' && config.sortable){
-                c.cls = 'sortable';
-                c.html = '<i class="fa"></i>' + c.html;
-            }
-            
-            // could use BS4 hidden-..-down 
-            
-            if(typeof(config.lgHeader) != 'undefined'){
-                hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
-            }
-            
-            if(typeof(config.mdHeader) != 'undefined'){
-                hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
-            }
-            
-            if(typeof(config.smHeader) != 'undefined'){
-                hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
-            }
-            
-            if(typeof(config.xsHeader) != 'undefined'){
-                hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
-            }
-            
-            if(hh.length){
-                c.html = hh;
-            }
-            
-            if(typeof(config.tooltip) != 'undefined'){
-                c.tooltip = config.tooltip;
-            }
-            
-            if(typeof(config.colspan) != 'undefined'){
-                c.colspan = config.colspan;
-            }
-            
-            if(typeof(config.hidden) != 'undefined' && config.hidden){
-                c.style += ' display:none;';
-            }
-            
-            if(typeof(config.dataIndex) != 'undefined'){
-                c.sort = config.dataIndex;
-            }
-            
-           
-            
-            if(typeof(config.align) != 'undefined' && config.align.length){
-                c.style += ' text-align:' + config.align + ';';
-            }
-            
-            if(typeof(config.width) != 'undefined'){
-                c.style += ' width:' + config.width + 'px;';
-                this.totalWidth += config.width;
-            } else {
-                this.totalWidth += 100; // assume minimum of 100 per column?
-            }
-            
-            if(typeof(config.cls) != 'undefined'){
-                c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
-            }
-            
-            ['xs','sm','md','lg'].map(function(size){
-                
-                if(typeof(config[size]) == 'undefined'){
-                    return;
-                }
-                 
-                if (!config[size]) { // 0 = hidden
-                    // BS 4 '0' is treated as hide that column and below.
-                    c.cls += ' hidden-' + size + ' hidden' + size + '-down';
-                    return;
-                }
-                
-                c.cls += ' col-' + size + '-' + config[size] + (
-                    size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
-                );
-                
-                
-            });
-            
-            header.cn.push(c)
-        }
-        
-        return header;
-    },
+    this.view = this; // compat with grid.
     
-    renderBody : function()
-    {
-        var body = {
-            tag: 'tbody',
-            cn : [
-                {
-                    tag: 'tr',
-                    cn : [
-                        {
-                            tag : 'td',
-                            colspan :  this.cm.getColumnCount()
-                        }
-                    ]
-                }
-            ]
-        };
-        
-        return body;
-    },
+    this.sm = this.sm || {xtype: 'RowSelectionModel'};
+    if (this.sm) {
+        this.sm.grid = this;
+        this.selModel = Roo.factory(this.sm, Roo.grid);
+        this.sm = this.selModel;
+        this.sm.xmodule = this.xmodule || false;
+    }
     
-    renderFooter : function()
-    {
-        var footer = {
-            tag: 'tfoot',
-            cn : [
-                {
-                    tag: 'tr',
-                    cn : [
-                        {
-                            tag : 'td',
-                            colspan :  this.cm.getColumnCount()
-                        }
-                    ]
-                }
-            ]
-        };
+    if (this.cm && typeof(this.cm.config) == 'undefined') {
+        this.colModel = new Roo.grid.ColumnModel(this.cm);
+        this.cm = this.colModel;
+        this.cm.xmodule = this.xmodule || false;
+    }
+    if (this.store) {
+        this.store= Roo.factory(this.store, Roo.data);
+        this.ds = this.store;
+        this.ds.xmodule = this.xmodule || false;
+         
+    }
+    if (this.footer && this.store) {
+        this.footer.dataSource = this.ds;
+        this.footer = Roo.factory(this.footer);
+    }
+    
+    /** @private */
+    this.addEvents({
+        /**
+         * @event cellclick
+         * Fires when a cell is clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Roo.Element} el
+         * @param {Number} rowIndex
+         * @param {Number} columnIndex
+         * @param {Roo.EventObject} e
+         */
+        "cellclick" : true,
+        /**
+         * @event celldblclick
+         * Fires when a cell is double clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Roo.Element} el
+         * @param {Number} rowIndex
+         * @param {Number} columnIndex
+         * @param {Roo.EventObject} e
+         */
+        "celldblclick" : true,
+        /**
+         * @event rowclick
+         * Fires when a row is clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Roo.Element} el
+         * @param {Number} rowIndex
+         * @param {Roo.EventObject} e
+         */
+        "rowclick" : true,
+        /**
+         * @event rowdblclick
+         * Fires when a row is double clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Roo.Element} el
+         * @param {Number} rowIndex
+         * @param {Roo.EventObject} e
+         */
+        "rowdblclick" : true,
+        /**
+         * @event mouseover
+         * Fires when a mouseover occur
+         * @param {Roo.bootstrap.Table} this
+         * @param {Roo.Element} el
+         * @param {Number} rowIndex
+         * @param {Number} columnIndex
+         * @param {Roo.EventObject} e
+         */
+        "mouseover" : true,
+        /**
+         * @event mouseout
+         * Fires when a mouseout occur
+         * @param {Roo.bootstrap.Table} this
+         * @param {Roo.Element} el
+         * @param {Number} rowIndex
+         * @param {Number} columnIndex
+         * @param {Roo.EventObject} e
+         */
+        "mouseout" : true,
+        /**
+         * @event rowclass
+         * Fires when a row is rendered, so you can change add a style to it.
+         * @param {Roo.bootstrap.Table} this
+         * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
+         */
+        'rowclass' : true,
+          /**
+         * @event rowsrendered
+         * Fires when all the  rows have been rendered
+         * @param {Roo.bootstrap.Table} this
+         */
+        'rowsrendered' : true,
+        /**
+         * @event contextmenu
+         * The raw contextmenu event for the entire grid.
+         * @param {Roo.EventObject} e
+         */
+        "contextmenu" : true,
+        /**
+         * @event rowcontextmenu
+         * Fires when a row is right clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Number} rowIndex
+         * @param {Roo.EventObject} e
+         */
+        "rowcontextmenu" : true,
+        /**
+         * @event cellcontextmenu
+         * Fires when a cell is right clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Number} rowIndex
+         * @param {Number} cellIndex
+         * @param {Roo.EventObject} e
+         */
+         "cellcontextmenu" : true,
+         /**
+         * @event headercontextmenu
+         * Fires when a header is right clicked
+         * @param {Roo.bootstrap.Table} this
+         * @param {Number} columnIndex
+         * @param {Roo.EventObject} e
+         */
+        "headercontextmenu" : true,
+        /**
+         * @event mousedown
+         * The raw mousedown event for the entire grid.
+         * @param {Roo.EventObject} e
+         */
+        "mousedown" : true
         
-        return footer;
-    },
+    });
+};
+
+Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
     
+    cls: false,
+    
+    striped : false,
+    scrollBody : false,
+    bordered: false,
+    hover:  false,
+    condensed : false,
+    responsive : false,
+    sm : false,
+    cm : false,
+    store : false,
+    loadMask : false,
+    footerShow : true,
+    headerShow : true,
+    enableColumnResize: true,
+  
+    rowSelection : false,
+    cellSelection : false,
+    layout : false,
+
+    minColumnWidth : 50,
     
+    // Roo.Element - the tbody
+    bodyEl: false,  // <tbody> Roo.Element - thead element    
+    headEl: false,  // <thead> Roo.Element - thead element
+    resizeProxy : false, // proxy element for dragging?
+
+
     
-    onLoad : function()
+    container: false, // used by gridpanel...
+    
+    lazyLoad : false,
+    
+    CSS : Roo.util.CSS,
+    
+    auto_hide_footer : false,
+    
+    view: false, // actually points to this..
+    
+    getAutoCreate : function()
     {
-//        Roo.log('ds onload');
-        this.clear();
+        var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
         
-        var _this = this;
-        var cm = this.cm;
-        var ds = this.store;
+        cfg = {
+            tag: 'table',
+            cls : 'table', 
+            cn : []
+        };
+        // this get's auto added by panel.Grid
+        if (this.scrollBody) {
+            cfg.cls += ' table-body-fixed';
+        }    
+        if (this.striped) {
+            cfg.cls += ' table-striped';
+        }
         
-        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
-            e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
-            if (_this.store.sortInfo) {
-                    
-                if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
-                    e.select('i', true).addClass(['fa-arrow-up']);
-                }
-                
-                if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
-                    e.select('i', true).addClass(['fa-arrow-down']);
-                }
-            }
-        });
+        if (this.hover) {
+            cfg.cls += ' table-hover';
+        }
+        if (this.bordered) {
+            cfg.cls += ' table-bordered';
+        }
+        if (this.condensed) {
+            cfg.cls += ' table-condensed';
+        }
         
-        var tbody =  this.mainBody;
-              
-        if(ds.getCount() > 0){
-            ds.data.each(function(d,rowIndex){
-                var row =  this.renderRow(cm, ds, rowIndex);
-                
-                tbody.createChild(row);
-                
-                var _this = this;
-                
-                if(row.cellObjects.length){
-                    Roo.each(row.cellObjects, function(r){
-                        _this.renderCellObject(r);
-                    })
-                }
-                
-            }, this);
+        if (this.responsive) {
+            cfg.cls += ' table-responsive';
         }
         
-        var tfoot = this.el.select('tfoot', true).first();
+        if (this.cls) {
+            cfg.cls+=  ' ' +this.cls;
+        }
         
-        if(this.footerShow && this.auto_hide_footer && this.mainFoot){
-            
-            this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
+        
+        
+        if (this.layout) {
+            cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
+        }
+        
+        if(this.store || this.cm){
+            if(this.headerShow){
+                cfg.cn.push(this.renderHeader());
+            }
             
-            var total = this.ds.getTotalCount();
+            cfg.cn.push(this.renderBody());
             
-            if(this.footer.pageSize < total){
-                this.mainFoot.show();
+            if(this.footerShow){
+                cfg.cn.push(this.renderFooter());
             }
+            // where does this come from?
+            //cfg.cls+=  ' TableGrid';
         }
         
-        Roo.each(this.el.select('tbody td', true).elements, function(e){
-            e.on('mouseover', _this.onMouseover, _this);
-        });
-        
-        Roo.each(this.el.select('tbody td', true).elements, function(e){
-            e.on('mouseout', _this.onMouseout, _this);
-        });
-        this.fireEvent('rowsrendered', this);
-        
-        this.autoSize();
-    },
-    
-    
-    onUpdate : function(ds,record)
-    {
-        this.refreshRow(record);
-        this.autoSize();
+        return { cn : [ cfg ] };
     },
     
-    onRemove : function(ds, record, index, isUpdate){
-        if(isUpdate !== true){
-            this.fireEvent("beforerowremoved", this, index, record);
+    initEvents : function()
+    {   
+        if(!this.store || !this.cm){
+            return;
+        }
+        if (this.selModel) {
+            this.selModel.initEvents();
         }
-        var bt = this.mainBody.dom;
         
-        var rows = this.el.select('tbody > tr', true).elements;
         
-        if(typeof(rows[index]) != 'undefined'){
-            bt.removeChild(rows[index].dom);
-        }
+        //Roo.log('initEvents with ds!!!!');
         
-//        if(bt.rows[index]){
-//            bt.removeChild(bt.rows[index]);
-//        }
+        this.bodyEl = this.el.select('tbody', true).first();
+        this.headEl = this.el.select('thead', true).first();
+        this.mainFoot = this.el.select('tfoot', true).first();
         
-        if(isUpdate !== true){
-            //this.stripeRows(index);
-            //this.syncRowHeights(index, index);
-            //this.layout();
-            this.fireEvent("rowremoved", this, index, record);
-        }
-    },
-    
-    onAdd : function(ds, records, rowIndex)
-    {
-        //Roo.log('on Add called');
-        // - note this does not handle multiple adding very well..
-        var bt = this.mainBody.dom;
-        for (var i =0 ; i < records.length;i++) {
-            //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
-            //Roo.log(records[i]);
-            //Roo.log(this.store.getAt(rowIndex+i));
-            this.insertRow(this.store, rowIndex + i, false);
-            return;
-        }
         
-    },
-    
-    
-    refreshRow : function(record){
-        var ds = this.store, index;
-        if(typeof record == 'number'){
-            index = record;
-            record = ds.getAt(index);
-        }else{
-            index = ds.indexOf(record);
-            if (index < 0) {
-                return; // should not happen - but seems to 
+        
+        
+        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+            e.on('click', this.sort, this);
+        }, this);
+        
+        
+        // why is this done????? = it breaks dialogs??
+        //this.parent().el.setStyle('position', 'relative');
+        
+        
+        if (this.footer) {
+            this.footer.parentId = this.id;
+            this.footer.onRender(this.el.select('tfoot tr td').first(), null);
+            
+            if(this.lazyLoad){
+                this.el.select('tfoot tr td').first().addClass('hide');
             }
-        }
-        this.insertRow(ds, index, true);
-        this.autoSize();
-        this.onRemove(ds, record, index+1, true);
-        this.autoSize();
-        //this.syncRowHeights(index, index);
-        //this.layout();
-        this.fireEvent("rowupdated", this, index, record);
-    },
-    
-    insertRow : function(dm, rowIndex, isUpdate){
+        } 
         
-        if(!isUpdate){
-            this.fireEvent("beforerowsinserted", this, rowIndex);
+        if(this.loadMask) {
+            this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
         }
-            //var s = this.getScrollState();
-        var row = this.renderRow(this.cm, this.store, rowIndex);
-        // insert before rowIndex..
-        var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
         
-        var _this = this;
-                
-        if(row.cellObjects.length){
-            Roo.each(row.cellObjects, function(r){
-                _this.renderCellObject(r);
-            })
-        }
-            
-        if(!isUpdate){
-            this.fireEvent("rowsinserted", this, rowIndex);
-            //this.syncRowHeights(firstRow, lastRow);
-            //this.stripeRows(firstRow);
-            //this.layout();
+        this.store.on('load', this.onLoad, this);
+        this.store.on('beforeload', this.onBeforeLoad, this);
+        this.store.on('update', this.onUpdate, this);
+        this.store.on('add', this.onAdd, this);
+        this.store.on("clear", this.clear, this);
+        
+        this.el.on("contextmenu", this.onContextMenu, this);
+        
+        
+        this.cm.on("headerchange", this.onHeaderChange, this);
+        this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
+
+ //?? does bodyEl get replaced on render?
+        this.bodyEl.on("click", this.onClick, this);
+        this.bodyEl.on("dblclick", this.onDblClick, this);        
+        this.bodyEl.on('scroll', this.onBodyScroll, this);
+
+        // guessing mainbody will work - this relays usually caught by selmodel at present.
+        this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
+  
+  
+        this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
+        
+  
+        if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+            new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
         }
         
+        this.initCSS();
+    },
+    // Compatibility with grid - we implement all the view features at present.
+    getView : function()
+    {
+        return this;
     },
     
-    
-    getRowDom : function(rowIndex)
+    initCSS : function()
     {
-        var rows = this.el.select('tbody > tr', true).elements;
         
-        return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
         
-    },
-    // returns the object tree for a tr..
-  
-    
-    renderRow : function(cm, ds, rowIndex) 
-    {
-        var d = ds.getAt(rowIndex);
-        
-        var row = {
-            tag : 'tr',
-            cls : 'x-row-' + rowIndex,
-            cn : []
-        };
-            
-        var cellObjects = [];
+        var cm = this.cm, styles = [];
+        this.CSS.removeStyleSheet(this.id + '-cssrules');
+        var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
+        // we can honour xs/sm/md/xl  as widths...
+        // we first have to decide what widht we are currently at...
+        var sz = Roo.getGridSize();
         
-        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
-            var config = cm.config[i];
-            
-            var renderer = cm.getRenderer(i);
-            var value = '';
-            var id = false;
-            
-            if(typeof(renderer) !== 'undefined'){
-                value = renderer(d.data[cm.getDataIndex(i)], false, d);
-            }
-            // if object are returned, then they are expected to be Roo.bootstrap.Component instances
-            // and are rendered into the cells after the row is rendered - using the id for the element.
-            
-            if(typeof(value) === 'object'){
-                id = Roo.id();
-                cellObjects.push({
-                    container : id,
-                    cfg : value 
-                })
-            }
-            
-            var rowcfg = {
-                record: d,
-                rowIndex : rowIndex,
-                colIndex : i,
-                rowClass : ''
-            };
-
-            this.fireEvent('rowclass', this, rowcfg);
-            
-            var td = {
-                tag: 'td',
-                // this might end up displaying HTML?
-                // this is too messy... - better to only do it on columsn you know are going to be too long
-                //tooltip : (typeof(value) === 'object') ? '' : value,
-                cls : rowcfg.rowClass + ' x-col-' + i,
-                style: '',
-                html: (typeof(value) === 'object') ? '' : value
-            };
-            
-            if (id) {
-                td.id = id;
-            }
-            
-            if(typeof(config.colspan) != 'undefined'){
-                td.colspan = config.colspan;
-            }
-            
-            if(typeof(config.hidden) != 'undefined' && config.hidden){
-                td.style += ' display:none;';
-            }
-            
-            if(typeof(config.align) != 'undefined' && config.align.length){
-                td.style += ' text-align:' + config.align + ';';
-            }
-            if(typeof(config.valign) != 'undefined' && config.valign.length){
-                td.style += ' vertical-align:' + config.valign + ';';
-            }
-            
-            if(typeof(config.width) != 'undefined'){
-                td.style += ' width:' +  config.width + 'px;';
+        var total = 0;
+        var last = -1;
+        var cols = []; // visable cols.
+        var total_abs = 0;
+        for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
+            var w = cm.getColumnWidth(i, false);
+            if(cm.isHidden(i)){
+                cols.push( { rel : false, abs : 0 });
+                continue;
             }
-            
-            if(typeof(config.cursor) != 'undefined'){
-                td.style += ' cursor:' +  config.cursor + ';';
+            if (w !== false) {
+                cols.push( { rel : false, abs : w });
+                total_abs += w;
+                last = i; // not really..
+                continue;
             }
-            
-            if(typeof(config.cls) != 'undefined'){
-                td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+            var w = cm.getColumnWidth(i, sz);
+            if (w > 0) {
+                last = i
             }
+            total += w;
+            cols.push( { rel : w, abs : false });
+        }
+        
+        var avail = this.bodyEl.dom.clientWidth - total_abs;
+        
+        var unitWidth = Math.floor(avail / total);
+        var rem = avail - (unitWidth * total);
+        
+        var hidden, width, pos = 0 , splithide , left;
+        for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
             
-            ['xs','sm','md','lg'].map(function(size){
+            hidden = 'display:none;';
+            left = '';
+            width  = 'width:0px;';
+            splithide = '';
+            if(!cm.isHidden(i)){
+                hidden = '';
                 
-                if(typeof(config[size]) == 'undefined'){
-                    return;
-                }
                 
+                // we can honour xs/sm/md/xl ?
+                var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
+                if (w===0) {
+                    hidden = 'display:none;';
+                }
+                // width should return a small number...
+                if (i == last) {
+                    w+=rem; // add the remaining with..
+                }
+                pos += w;
+                left = "left:" + (pos -4) + "px;";
+                width = "width:" + w+ "px;";
                 
-                  
-                if (!config[size]) { // 0 = hidden
-                    // BS 4 '0' is treated as hide that column and below.
-                    td.cls += ' hidden-' + size + ' hidden' + size + '-down';
-                    return;
+            }
+            if (this.responsive) {
+                width = '';
+                left = '';
+                hidden = cm.isHidden(i) ? 'display:none' : '';
+                splithide = 'display: none';
+            }
+            
+            styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
+            if (this.headEl) {
+                if (i == last) {
+                    splithide = 'display:none;';
                 }
                 
-                td.cls += ' col-' + size + '-' + config[size] + (
-                    size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
+                styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
+                            '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
                 );
-                 
-
-            });
+            }
             
-            row.cn.push(td);
-           
         }
+        //Roo.log(styles.join(''));
+        this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
         
-        row.cellObjects = cellObjects;
-        
-        return row;
-          
     },
     
     
     
-    onBeforeLoad : function()
-    {
-        
-    },
-     /**
-     * Remove all rows
-     */
-    clear : function()
+    onContextMenu : function(e, t)
     {
-        this.el.select('tbody', true).first().dom.innerHTML = '';
+        this.processEvent("contextmenu", e);
     },
-    /**
-     * Show or hide a row.
-     * @param {Number} rowIndex to show or hide
-     * @param {Boolean} state hide
-     */
-    setRowVisibility : function(rowIndex, state)
+    
+    processEvent : function(name, e)
     {
-        var bt = this.mainBody.dom;
+        if (name != 'touchstart' ) {
+            this.fireEvent(name, e);    
+        }
         
-        var rows = this.el.select('tbody > tr', true).elements;
+        var t = e.getTarget();
         
-        if(typeof(rows[rowIndex]) == 'undefined'){
+        var cell = Roo.get(t);
+        
+        if(!cell){
             return;
         }
-        rows[rowIndex].dom.style.display = state ? '' : 'none';
-    },
-    
-    
-    getSelectionModel : function(){
-        if(!this.selModel){
-            this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
-        }
-        return this.selModel;
-    },
-    /*
-     * Render the Roo.bootstrap object from renderder
-     */
-    renderCellObject : function(r)
-    {
-        var _this = this;
         
-        r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
-        
-        var t = r.cfg.render(r.container);
+        if(cell.findParent('tfoot', false, true)){
+            return;
+        }
         
-        if(r.cfg.cn){
-            Roo.each(r.cfg.cn, function(c){
-                var child = {
-                    container: t.getChildContainer(),
-                    cfg: c
-                };
-                _this.renderCellObject(child);
-            })
+        if(cell.findParent('thead', false, true)){
+            
+            if(e.getTarget().nodeName.toLowerCase() != 'th'){
+                cell = Roo.get(t).findParent('th', false, true);
+                if (!cell) {
+                    Roo.log("failed to find th in thead?");
+                    Roo.log(e.getTarget());
+                    return;
+                }
+            }
+            
+            var cellIndex = cell.dom.cellIndex;
+            
+            var ename = name == 'touchstart' ? 'click' : name;
+            this.fireEvent("header" + ename, this, cellIndex, e);
+            
+            return;
         }
-    },
-    
-    getRowIndex : function(row)
-    {
-        var rowIndex = -1;
         
-        Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
-            if(el != row){
+        if(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = Roo.get(t).findParent('td', false, true);
+            if (!cell) {
+                Roo.log("failed to find th in tbody?");
+                Roo.log(e.getTarget());
                 return;
             }
+        }
+        
+        var row = cell.findParent('tr', false, true);
+        var cellIndex = cell.dom.cellIndex;
+        var rowIndex = row.dom.rowIndex - 1;
+        
+        if(row !== false){
             
-            rowIndex = index;
-        });
+            this.fireEvent("row" + name, this, rowIndex, e);
+            
+            if(cell !== false){
+            
+                this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
+            }
+        }
         
-        return rowIndex;
     },
-     /**
-     * Returns the grid's underlying element = used by panel.Grid
-     * @return {Element} The element
-     */
-    getGridEl : function(){
-        return this.el;
-    },
-     /**
-     * Forces a resize - used by panel.Grid
-     * @return {Element} The element
-     */
-    autoSize : function()
+    
+    onMouseover : function(e, el)
     {
-        //var ctr = Roo.get(this.container.dom.parentElement);
-        var ctr = Roo.get(this.el.dom);
-        
-        var thd = this.getGridEl().select('thead',true).first();
-        var tbd = this.getGridEl().select('tbody', true).first();
-        var tfd = this.getGridEl().select('tfoot', true).first();
+        var cell = Roo.get(el);
         
-        var cw = ctr.getWidth();
-        this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
+        if(!cell){
+            return;
+        }
         
-        if (tbd) {
-            
-            tbd.setWidth(ctr.getWidth());
-            // if the body has a max height - and then scrolls - we should perhaps set up the height here
-            // this needs fixing for various usage - currently only hydra job advers I think..
-            //tdb.setHeight(
-            //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
-            //); 
-            var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
-            cw -= barsize;
+        if(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = cell.findParent('td', false, true);
         }
-        cw = Math.max(cw, this.totalWidth);
-        this.getGridEl().select('tbody tr',true).setWidth(cw);
         
-        // resize 'expandable coloumn?
+        var row = cell.findParent('tr', false, true);
+        var cellIndex = cell.dom.cellIndex;
+        var rowIndex = row.dom.rowIndex - 1; // start from 0
         
-        return; // we doe not have a view in this design..
+        this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
         
     },
-    onBodyScroll: function()
-    {
-        //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
-        if(this.mainHead){
-            this.mainHead.setStyle({
-                'position' : 'relative',
-                'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
-            });
+    
+    onMouseout : function(e, el)
+    {
+        var cell = Roo.get(el);
+        
+        if(!cell){
+            return;
         }
         
-        if(this.lazyLoad){
-            
-            var scrollHeight = this.mainBody.dom.scrollHeight;
-            
-            var scrollTop = Math.ceil(this.mainBody.getScroll().top);
-            
-            var height = this.mainBody.getHeight();
-            
-            if(scrollHeight - height == scrollTop) {
-                
-                var total = this.ds.getTotalCount();
-                
-                if(this.footer.cursor + this.footer.pageSize < total){
-                    
-                    this.footer.ds.load({
-                        params : {
-                            start : this.footer.cursor + this.footer.pageSize,
-                            limit : this.footer.pageSize
-                        },
-                        add : true
-                    });
-                }
-            }
-            
+        if(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = cell.findParent('td', false, true);
         }
+        
+        var row = cell.findParent('tr', false, true);
+        var cellIndex = cell.dom.cellIndex;
+        var rowIndex = row.dom.rowIndex - 1; // start from 0
+        
+        this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
+        
     },
     
-    onHeaderChange : function()
+    onClick : function(e, el)
     {
-        var header = this.renderHeader();
-        var table = this.el.select('table', true).first();
+        var cell = Roo.get(el);
         
-        this.mainHead.remove();
-        this.mainHead = table.createChild(header, this.mainBody, false);
+        if(!cell || (!this.cellSelection && !this.rowSelection)){
+            return;
+        }
         
-        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
-            e.on('click', this.sort, this);
-        }, this);
+        if(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = cell.findParent('td', false, true);
+        }
+        
+        if(!cell || typeof(cell) == 'undefined'){
+            return;
+        }
+        
+        var row = cell.findParent('tr', false, true);
+        
+        if(!row || typeof(row) == 'undefined'){
+            return;
+        }
+        
+        var cellIndex = cell.dom.cellIndex;
+        var rowIndex = this.getRowIndex(row);
         
+        // why??? - should these not be based on SelectionModel?
+        //if(this.cellSelection){
+            this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
+        //}
         
+        //if(this.rowSelection){
+            this.fireEvent('rowclick', this, row, rowIndex, e);
+        //}
+         
     },
-    
-    onHiddenChange : function(colModel, colIndex, hidden)
+        
+    onDblClick : function(e,el)
     {
-        var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
-        var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
+        var cell = Roo.get(el);
         
-        this.CSS.updateRule(thSelector, "display", "");
-        this.CSS.updateRule(tdSelector, "display", "");
+        if(!cell || (!this.cellSelection && !this.rowSelection)){
+            return;
+        }
         
-        if(hidden){
-            this.CSS.updateRule(thSelector, "display", "none");
-            this.CSS.updateRule(tdSelector, "display", "none");
+        if(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = cell.findParent('td', false, true);
         }
         
-        this.onHeaderChange();
-        this.onLoad();
+        if(!cell || typeof(cell) == 'undefined'){
+            return;
+        }
+        
+        var row = cell.findParent('tr', false, true);
+        
+        if(!row || typeof(row) == 'undefined'){
+            return;
+        }
+        
+        var cellIndex = cell.dom.cellIndex;
+        var rowIndex = this.getRowIndex(row);
+        
+        if(this.cellSelection){
+            this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
+        }
+        
+        if(this.rowSelection){
+            this.fireEvent('rowdblclick', this, row, rowIndex, e);
+        }
     },
-    
-    setColumnWidth: function(col_index, width)
+    findRowIndex : function(el)
     {
-        // width = "md-2 xs-2..."
-        if(!this.colModel.config[col_index]) {
+        var cell = Roo.get(el);
+        if(!cell) {
+            return false;
+        }
+        var row = cell.findParent('tr', false, true);
+        
+        if(!row || typeof(row) == 'undefined'){
+            return false;
+        }
+        return this.getRowIndex(row);
+    },
+    sort : function(e,el)
+    {
+        var col = Roo.get(el);
+        
+        if(!col.hasClass('sortable')){
             return;
         }
         
-        var w = width.split(" ");
+        var sort = col.attr('sort');
+        var dir = 'ASC';
         
-        var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
+        if(col.select('i', true).first().hasClass('fa-arrow-up')){
+            dir = 'DESC';
+        }
         
-        var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
+        this.store.sortInfo = {field : sort, direction : dir};
+        
+        if (this.footer) {
+            Roo.log("calling footer first");
+            this.footer.onClick('first');
+        } else {
+        
+            this.store.load({ params : { start : 0 } });
+        }
+    },
+    
+    renderHeader : function()
+    {
+        var header = {
+            tag: 'thead',
+            cn : []
+        };
         
+        var cm = this.cm;
+        this.totalWidth = 0;
         
-        for(var j = 0; j < w.length; j++) {
+        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
             
-            if(!w[j]) {
-                continue;
+            var config = cm.config[i];
+            
+            var c = {
+                tag: 'th',
+                cls : 'x-hcol-' + i,
+                style : '',
+                
+                html: cm.getColumnHeader(i)
+            };
+            
+            var tooltip = cm.getColumnTooltip(i);
+            if (tooltip) {
+                c.tooltip = tooltip;
             }
             
-            var size_cls = w[j].split("-");
             
-            if(!Number.isInteger(size_cls[1] * 1)) {
-                continue;
+            var hh = '';
+            
+            if(typeof(config.sortable) != 'undefined' && config.sortable){
+                c.cls += ' sortable';
+                c.html = '<i class="fa"></i>' + c.html;
             }
             
-            if(!this.colModel.config[col_index][size_cls[0]]) {
-                continue;
+            // could use BS4 hidden-..-down 
+            
+            if(typeof(config.lgHeader) != 'undefined'){
+                hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
             }
             
-            if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
-                continue;
+            if(typeof(config.mdHeader) != 'undefined'){
+                hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
             }
             
-            h_row[0].classList.replace(
-                "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
-                "col-"+size_cls[0]+"-"+size_cls[1]
-            );
+            if(typeof(config.smHeader) != 'undefined'){
+                hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
+            }
             
-            for(var i = 0; i < rows.length; i++) {
-                
-                var size_cls = w[j].split("-");
-                
-                if(!Number.isInteger(size_cls[1] * 1)) {
-                    continue;
-                }
-                
-                if(!this.colModel.config[col_index][size_cls[0]]) {
-                    continue;
-                }
-                
-                if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
-                    continue;
-                }
-                
-                rows[i].classList.replace(
-                    "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
-                    "col-"+size_cls[0]+"-"+size_cls[1]
-                );
+            if(typeof(config.xsHeader) != 'undefined'){
+                hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
             }
             
-            this.colModel.config[col_index][size_cls[0]] = size_cls[1];
-        }
-    }
-});
-
-
- /*
- * - LGPL
- *
- * table cell
- * 
- */
-
-/**
- * @class Roo.bootstrap.TableCell
- * @extends Roo.bootstrap.Component
- * Bootstrap TableCell class
- * @cfg {String} html cell contain text
- * @cfg {String} cls cell class
- * @cfg {String} tag cell tag (td|th) default td
- * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
- * @cfg {String} align Aligns the content in a cell
- * @cfg {String} axis Categorizes cells
- * @cfg {String} bgcolor Specifies the background color of a cell
- * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
- * @cfg {Number} colspan Specifies the number of columns a cell should span
- * @cfg {String} headers Specifies one or more header cells a cell is related to
- * @cfg {Number} height Sets the height of a cell
- * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
- * @cfg {Number} rowspan Sets the number of rows a cell should span
- * @cfg {String} scope Defines a way to associate header cells and data cells in a table
- * @cfg {String} valign Vertical aligns the content in a cell
- * @cfg {Number} width Specifies the width of a cell
- * 
- * @constructor
- * Create a new TableCell
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TableCell = function(config){
-    Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
-    
-    html: false,
-    cls: false,
-    tag: false,
-    abbr: false,
-    align: false,
-    axis: false,
-    bgcolor: false,
-    charoff: false,
-    colspan: false,
-    headers: false,
-    height: false,
-    nowrap: false,
-    rowspan: false,
-    scope: false,
-    valign: false,
-    width: false,
+            if(hh.length){
+                c.html = hh;
+            }
+            
+            if(typeof(config.tooltip) != 'undefined'){
+                c.tooltip = config.tooltip;
+            }
+            
+            if(typeof(config.colspan) != 'undefined'){
+                c.colspan = config.colspan;
+            }
+            
+            // hidden is handled by CSS now
+            
+            if(typeof(config.dataIndex) != 'undefined'){
+                c.sort = config.dataIndex;
+            }
+            
+           
+            
+            if(typeof(config.align) != 'undefined' && config.align.length){
+                c.style += ' text-align:' + config.align + ';';
+            }
+            
+            /* width is done in CSS
+             *if(typeof(config.width) != 'undefined'){
+                c.style += ' width:' + config.width + 'px;';
+                this.totalWidth += config.width;
+            } else {
+                this.totalWidth += 100; // assume minimum of 100 per column?
+            }
+            */
+            
+            if(typeof(config.cls) != 'undefined'){
+                c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
+            }
+            // this is the bit that doesnt reall work at all...
+            
+            if (this.responsive) {
+                 
+            
+                ['xs','sm','md','lg'].map(function(size){
+                    
+                    if(typeof(config[size]) == 'undefined'){
+                        return;
+                    }
+                     
+                    if (!config[size]) { // 0 = hidden
+                        // BS 4 '0' is treated as hide that column and below.
+                        c.cls += ' hidden-' + size + ' hidden' + size + '-down';
+                        return;
+                    }
+                    
+                    c.cls += ' col-' + size + '-' + config[size] + (
+                        size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
+                    );
+                    
+                    
+                });
+            }
+            // at the end?
+            
+            c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
+            
+            
+            
+            
+            header.cn.push(c)
+        }
+        
+        return header;
+    },
     
+    renderBody : function()
+    {
+        var body = {
+            tag: 'tbody',
+            cn : [
+                {
+                    tag: 'tr',
+                    cn : [
+                        {
+                            tag : 'td',
+                            colspan :  this.cm.getColumnCount()
+                        }
+                    ]
+                }
+            ]
+        };
+        
+        return body;
+    },
     
-    getAutoCreate : function(){
-        var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
-       
-        cfg = {
-            tag: 'td'
+    renderFooter : function()
+    {
+        var footer = {
+            tag: 'tfoot',
+            cn : [
+                {
+                    tag: 'tr',
+                    cn : [
+                        {
+                            tag : 'td',
+                            colspan :  this.cm.getColumnCount()
+                        }
+                    ]
+                }
+            ]
         };
         
-        if(this.tag){
-            cfg.tag = this.tag;
-        }
+        return footer;
+    },
+    
+    
+    
+    onLoad : function()
+    {
+//        Roo.log('ds onload');
+        this.clear();
         
-        if (this.html) {
-            cfg.html=this.html
-        }
-        if (this.cls) {
-            cfg.cls=this.cls
-        }
-        if (this.abbr) {
-            cfg.abbr=this.abbr
-        }
-        if (this.align) {
-            cfg.align=this.align
+        var _this = this;
+        var cm = this.cm;
+        var ds = this.store;
+        
+        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+            e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
+            if (_this.store.sortInfo) {
+                    
+                if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
+                    e.select('i', true).addClass(['fa-arrow-up']);
+                }
+                
+                if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
+                    e.select('i', true).addClass(['fa-arrow-down']);
+                }
+            }
+        });
+        
+        var tbody =  this.bodyEl;
+              
+        if(ds.getCount() > 0){
+            ds.data.each(function(d,rowIndex){
+                var row =  this.renderRow(cm, ds, rowIndex);
+                
+                tbody.createChild(row);
+                
+                var _this = this;
+                
+                if(row.cellObjects.length){
+                    Roo.each(row.cellObjects, function(r){
+                        _this.renderCellObject(r);
+                    })
+                }
+                
+            }, this);
         }
-        if (this.axis) {
-            cfg.axis=this.axis
+        
+        var tfoot = this.el.select('tfoot', true).first();
+        
+        if(this.footerShow && this.auto_hide_footer && this.mainFoot){
+            
+            this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
+            
+            var total = this.ds.getTotalCount();
+            
+            if(this.footer.pageSize < total){
+                this.mainFoot.show();
+            }
         }
-        if (this.bgcolor) {
-            cfg.bgcolor=this.bgcolor
+        
+        Roo.each(this.el.select('tbody td', true).elements, function(e){
+            e.on('mouseover', _this.onMouseover, _this);
+        });
+        
+        Roo.each(this.el.select('tbody td', true).elements, function(e){
+            e.on('mouseout', _this.onMouseout, _this);
+        });
+        this.fireEvent('rowsrendered', this);
+        
+        this.autoSize();
+        
+        this.initCSS(); /// resize cols
+
+        
+    },
+    
+    
+    onUpdate : function(ds,record)
+    {
+        this.refreshRow(record);
+        this.autoSize();
+    },
+    
+    onRemove : function(ds, record, index, isUpdate){
+        if(isUpdate !== true){
+            this.fireEvent("beforerowremoved", this, index, record);
         }
-        if (this.charoff) {
-            cfg.charoff=this.charoff
+        var bt = this.bodyEl.dom;
+        
+        var rows = this.el.select('tbody > tr', true).elements;
+        
+        if(typeof(rows[index]) != 'undefined'){
+            bt.removeChild(rows[index].dom);
         }
-        if (this.colspan) {
-            cfg.colspan=this.colspan
+        
+//        if(bt.rows[index]){
+//            bt.removeChild(bt.rows[index]);
+//        }
+        
+        if(isUpdate !== true){
+            //this.stripeRows(index);
+            //this.syncRowHeights(index, index);
+            //this.layout();
+            this.fireEvent("rowremoved", this, index, record);
         }
-        if (this.headers) {
-            cfg.headers=this.headers
-        }
-        if (this.height) {
-            cfg.height=this.height
-        }
-        if (this.nowrap) {
-            cfg.nowrap=this.nowrap
-        }
-        if (this.rowspan) {
-            cfg.rowspan=this.rowspan
-        }
-        if (this.scope) {
-            cfg.scope=this.scope
-        }
-        if (this.valign) {
-            cfg.valign=this.valign
-        }
-        if (this.width) {
-            cfg.width=this.width
+    },
+    
+    onAdd : function(ds, records, rowIndex)
+    {
+        //Roo.log('on Add called');
+        // - note this does not handle multiple adding very well..
+        var bt = this.bodyEl.dom;
+        for (var i =0 ; i < records.length;i++) {
+            //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
+            //Roo.log(records[i]);
+            //Roo.log(this.store.getAt(rowIndex+i));
+            this.insertRow(this.store, rowIndex + i, false);
+            return;
         }
         
-       
-        return cfg;
-    }
-   
-});
-
-
- /*
- * - LGPL
- *
- * table row
- * 
- */
-
-/**
- * @class Roo.bootstrap.TableRow
- * @extends Roo.bootstrap.Component
- * Bootstrap TableRow class
- * @cfg {String} cls row class
- * @cfg {String} align Aligns the content in a table row
- * @cfg {String} bgcolor Specifies a background color for a table row
- * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
- * @cfg {String} valign Vertical aligns the content in a table row
- * 
- * @constructor
- * Create a new TableRow
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TableRow = function(config){
-    Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
+    },
     
-    cls: false,
-    align: false,
-    bgcolor: false,
-    charoff: false,
-    valign: false,
     
-    getAutoCreate : function(){
-        var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
-       
-        cfg = {
-            tag: 'tr'
-        };
-            
-        if(this.cls){
-            cfg.cls = this.cls;
+    refreshRow : function(record){
+        var ds = this.store, index;
+        if(typeof record == 'number'){
+            index = record;
+            record = ds.getAt(index);
+        }else{
+            index = ds.indexOf(record);
+            if (index < 0) {
+                return; // should not happen - but seems to 
+            }
         }
-        if(this.align){
-            cfg.align = this.align;
+        this.insertRow(ds, index, true);
+        this.autoSize();
+        this.onRemove(ds, record, index+1, true);
+        this.autoSize();
+        //this.syncRowHeights(index, index);
+        //this.layout();
+        this.fireEvent("rowupdated", this, index, record);
+    },
+    // private - called by RowSelection
+    onRowSelect : function(rowIndex){
+        var row = this.getRowDom(rowIndex);
+        row.addClass(['bg-info','info']);
+    },
+    // private - called by RowSelection
+    onRowDeselect : function(rowIndex)
+    {
+        if (rowIndex < 0) {
+            return;
         }
-        if(this.bgcolor){
-            cfg.bgcolor = this.bgcolor;
+        var row = this.getRowDom(rowIndex);
+        row.removeClass(['bg-info','info']);
+    },
+      /**
+     * Focuses the specified row.
+     * @param {Number} row The row index
+     */
+    focusRow : function(row)
+    {
+        //Roo.log('GridView.focusRow');
+        var x = this.bodyEl.dom.scrollLeft;
+        this.focusCell(row, 0, false);
+        this.bodyEl.dom.scrollLeft = x;
+
+    },
+     /**
+     * Focuses the specified cell.
+     * @param {Number} row The row index
+     * @param {Number} col The column index
+     * @param {Boolean} hscroll false to disable horizontal scrolling
+     */
+    focusCell : function(row, col, hscroll)
+    {
+        //Roo.log('GridView.focusCell');
+        var el = this.ensureVisible(row, col, hscroll);
+        // not sure what focusEL achives = it's a <a> pos relative 
+        //this.focusEl.alignTo(el, "tl-tl");
+        //if(Roo.isGecko){
+        //    this.focusEl.focus();
+        //}else{
+        //    this.focusEl.focus.defer(1, this.focusEl);
+        //}
+    },
+    
+     /**
+     * Scrolls the specified cell into view
+     * @param {Number} row The row index
+     * @param {Number} col The column index
+     * @param {Boolean} hscroll false to disable horizontal scrolling
+     */
+    ensureVisible : function(row, col, hscroll)
+    {
+        //Roo.log('GridView.ensureVisible,' + row + ',' + col);
+        //return null; //disable for testing.
+        if(typeof row != "number"){
+            row = row.rowIndex;
         }
-        if(this.charoff){
-            cfg.charoff = this.charoff;
+        if(row < 0 && row >= this.ds.getCount()){
+            return  null;
         }
-        if(this.valign){
-            cfg.valign = this.valign;
+        col = (col !== undefined ? col : 0);
+        var cm = this.cm;
+        while(cm.isHidden(col)){
+            col++;
         }
-       
-        return cfg;
-    }
-   
-});
 
+        var el = this.getCellDom(row, col);
+        if(!el){
+            return null;
+        }
+        var c = this.bodyEl.dom;
 
- /*
- * - LGPL
- *
- * table body
- * 
- */
+        var ctop = parseInt(el.offsetTop, 10);
+        var cleft = parseInt(el.offsetLeft, 10);
+        var cbot = ctop + el.offsetHeight;
+        var cright = cleft + el.offsetWidth;
 
-/**
- * @class Roo.bootstrap.TableBody
- * @extends Roo.bootstrap.Component
- * Bootstrap TableBody class
- * @cfg {String} cls element class
- * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
- * @cfg {String} align Aligns the content inside the element
- * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
- * @cfg {String} valign Vertical aligns the content inside the <tbody> element
- * 
- * @constructor
- * Create a new TableBody
- * @param {Object} config The config object
- */
+        //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
+        var ch = 0; //?? header is not withing the area?
+        var stop = parseInt(c.scrollTop, 10);
+        var sleft = parseInt(c.scrollLeft, 10);
+        var sbot = stop + ch;
+        var sright = sleft + c.clientWidth;
+        /*
+        Roo.log('GridView.ensureVisible:' +
+                ' ctop:' + ctop +
+                ' c.clientHeight:' + c.clientHeight +
+                ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
+                ' stop:' + stop +
+                ' cbot:' + cbot +
+                ' sbot:' + sbot +
+                ' ch:' + ch  
+                );
+        */
+        if(ctop < stop){
+            c.scrollTop = ctop;
+            //Roo.log("set scrolltop to ctop DISABLE?");
+        }else if(cbot > sbot){
+            //Roo.log("set scrolltop to cbot-ch");
+            c.scrollTop = cbot-ch;
+        }
 
-Roo.bootstrap.TableBody = function(config){
-    Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
-};
+        if(hscroll !== false){
+            if(cleft < sleft){
+                c.scrollLeft = cleft;
+            }else if(cright > sright){
+                c.scrollLeft = cright-c.clientWidth;
+            }
+        }
 
-Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
+        return el;
+    },
     
-    cls: false,
-    tag: false,
-    align: false,
-    charoff: false,
-    valign: false,
     
-    getAutoCreate : function(){
-        var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
-       
-        cfg = {
-            tag: 'tbody'
-        };
-            
-        if (this.cls) {
-            cfg.cls=this.cls
-        }
-        if(this.tag){
-            cfg.tag = this.tag;
+    insertRow : function(dm, rowIndex, isUpdate){
+        
+        if(!isUpdate){
+            this.fireEvent("beforerowsinserted", this, rowIndex);
         }
-       
-        if(this.align){
-            cfg.align = this.align;
+            //var s = this.getScrollState();
+        var row = this.renderRow(this.cm, this.store, rowIndex);
+        // insert before rowIndex..
+        var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
+        
+        var _this = this;
+                
+        if(row.cellObjects.length){
+            Roo.each(row.cellObjects, function(r){
+                _this.renderCellObject(r);
+            })
         }
-        if(this.charoff){
-            cfg.charoff = this.charoff;
+            
+        if(!isUpdate){
+            this.fireEvent("rowsinserted", this, rowIndex);
+            //this.syncRowHeights(firstRow, lastRow);
+            //this.stripeRows(firstRow);
+            //this.layout();
         }
-        if(this.valign){
-            cfg.valign = this.valign;
+        
+    },
+    
+    
+    getRowDom : function(rowIndex)
+    {
+        var rows = this.el.select('tbody > tr', true).elements;
+        
+        return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
+        
+    },
+    getCellDom : function(rowIndex, colIndex)
+    {
+        var row = this.getRowDom(rowIndex);
+        if (row === false) {
+            return false;
         }
+        var cols = row.select('td', true).elements;
+        return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
         
-        return cfg;
-    }
+    },
     
+    // returns the object tree for a tr..
+  
     
-//    initEvents : function()
-//    {
-//        
-//        if(!this.store){
-//            return;
-//        }
-//        
-//        this.store = Roo.factory(this.store, Roo.data);
-//        this.store.on('load', this.onLoad, this);
-//        
-//        this.store.load();
-//        
-//    },
-//    
-//    onLoad: function () 
-//    {   
-//        this.fireEvent('load', 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">
- */
-
-// as we use this in bootstrap.
-Roo.namespace('Roo.form');
- /**
- * @class Roo.form.Action
- * Internal Class used to handle form actions
- * @constructor
- * @param {Roo.form.BasicForm} el The form element or its id
- * @param {Object} config Configuration options
- */
-
-// define the action interface
-Roo.form.Action = function(form, options){
-    this.form = form;
-    this.options = options || {};
-};
-/**
- * Client Validation Failed
- * @const 
- */
-Roo.form.Action.CLIENT_INVALID = 'client';
-/**
- * Server Validation Failed
- * @const 
- */
-Roo.form.Action.SERVER_INVALID = 'server';
- /**
- * Connect to Server Failed
- * @const 
- */
-Roo.form.Action.CONNECT_FAILURE = 'connect';
-/**
- * Reading Data from Server Failed
- * @const 
- */
-Roo.form.Action.LOAD_FAILURE = 'load';
-
-Roo.form.Action.prototype = {
-    type : 'default',
-    failureType : undefined,
-    response : undefined,
-    result : undefined,
-
-    // interface method
-    run : function(options){
-
-    },
-
-    // interface method
-    success : function(response){
-
-    },
-
-    // interface method
-    handleResponse : function(response){
-
-    },
-
-    // default connection failure
-    failure : function(response){
+    renderRow : function(cm, ds, rowIndex) 
+    {
+        var d = ds.getAt(rowIndex);
         
-        this.response = response;
-        this.failureType = Roo.form.Action.CONNECT_FAILURE;
-        this.form.afterAction(this, false);
-    },
-
-    processResponse : function(response){
-        this.response = response;
-        if(!response.responseText){
-            return true;
-        }
-        this.result = this.handleResponse(response);
-        return this.result;
-    },
-
-    // utility functions used internally
-    getUrl : function(appendParams){
-        var url = this.options.url || this.form.url || this.form.el.dom.action;
-        if(appendParams){
-            var p = this.getParams();
-            if(p){
-                url += (url.indexOf('?') != -1 ? '&' : '?') + p;
+        var row = {
+            tag : 'tr',
+            cls : 'x-row-' + rowIndex,
+            cn : []
+        };
+            
+        var cellObjects = [];
+        
+        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+            var config = cm.config[i];
+            
+            var renderer = cm.getRenderer(i);
+            var value = '';
+            var id = false;
+            
+            if(typeof(renderer) !== 'undefined'){
+                value = renderer(d.data[cm.getDataIndex(i)], false, d);
             }
-        }
-        return url;
-    },
-
-    getMethod : function(){
-        return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
-    },
+            // if object are returned, then they are expected to be Roo.bootstrap.Component instances
+            // and are rendered into the cells after the row is rendered - using the id for the element.
+            
+            if(typeof(value) === 'object'){
+                id = Roo.id();
+                cellObjects.push({
+                    container : id,
+                    cfg : value 
+                })
+            }
+            
+            var rowcfg = {
+                record: d,
+                rowIndex : rowIndex,
+                colIndex : i,
+                rowClass : ''
+            };
 
-    getParams : function(){
-        var bp = this.form.baseParams;
-        var p = this.options.params;
-        if(p){
-            if(typeof p == "object"){
-                p = Roo.urlEncode(Roo.applyIf(p, bp));
-            }else if(typeof p == 'string' && bp){
-                p += '&' + Roo.urlEncode(bp);
+            this.fireEvent('rowclass', this, rowcfg);
+            
+            var td = {
+                tag: 'td',
+                // this might end up displaying HTML?
+                // this is too messy... - better to only do it on columsn you know are going to be too long
+                //tooltip : (typeof(value) === 'object') ? '' : value,
+                cls : rowcfg.rowClass + ' x-col-' + i,
+                style: '',
+                html: (typeof(value) === 'object') ? '' : value
+            };
+            
+            if (id) {
+                td.id = id;
             }
-        }else if(bp){
-            p = Roo.urlEncode(bp);
+            
+            if(typeof(config.colspan) != 'undefined'){
+                td.colspan = config.colspan;
+            }
+            
+            
+            
+            if(typeof(config.align) != 'undefined' && config.align.length){
+                td.style += ' text-align:' + config.align + ';';
+            }
+            if(typeof(config.valign) != 'undefined' && config.valign.length){
+                td.style += ' vertical-align:' + config.valign + ';';
+            }
+            /*
+            if(typeof(config.width) != 'undefined'){
+                td.style += ' width:' +  config.width + 'px;';
+            }
+            */
+            
+            if(typeof(config.cursor) != 'undefined'){
+                td.style += ' cursor:' +  config.cursor + ';';
+            }
+            
+            if(typeof(config.cls) != 'undefined'){
+                td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+            }
+            if (this.responsive) {
+                ['xs','sm','md','lg'].map(function(size){
+                    
+                    if(typeof(config[size]) == 'undefined'){
+                        return;
+                    }
+                    
+                    
+                      
+                    if (!config[size]) { // 0 = hidden
+                        // BS 4 '0' is treated as hide that column and below.
+                        td.cls += ' hidden-' + size + ' hidden' + size + '-down';
+                        return;
+                    }
+                    
+                    td.cls += ' col-' + size + '-' + config[size] + (
+                        size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
+                    );
+                     
+    
+                });
+            }
+            row.cn.push(td);
+           
         }
-        return p;
+        
+        row.cellObjects = cellObjects;
+        
+        return row;
+          
     },
-
-    createCallback : function(){
-        return {
-            success: this.success,
-            failure: this.failure,
-            scope: this,
-            timeout: (this.form.timeout*1000),
-            upload: this.form.fileUpload ? this.success : undefined
-        };
-    }
-};
-
-Roo.form.Action.Submit = function(form, options){
-    Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
-};
-
-Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
-    type : 'submit',
-
-    haveProgress : false,
-    uploadComplete : false,
     
-    // uploadProgress indicator.
-    uploadProgress : function()
+    
+    
+    onBeforeLoad : function()
     {
-        if (!this.form.progressUrl) {
+        
+    },
+     /**
+     * Remove all rows
+     */
+    clear : function()
+    {
+        this.el.select('tbody', true).first().dom.innerHTML = '';
+    },
+    /**
+     * Show or hide a row.
+     * @param {Number} rowIndex to show or hide
+     * @param {Boolean} state hide
+     */
+    setRowVisibility : function(rowIndex, state)
+    {
+        var bt = this.bodyEl.dom;
+        
+        var rows = this.el.select('tbody > tr', true).elements;
+        
+        if(typeof(rows[rowIndex]) == 'undefined'){
             return;
         }
+        rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
         
-        if (!this.haveProgress) {
-            Roo.MessageBox.progress("Uploading", "Uploading");
-        }
-        if (this.uploadComplete) {
-           Roo.MessageBox.hide();
-           return;
+    },
+    
+    
+    getSelectionModel : function(){
+        if(!this.selModel){
+            this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
         }
+        return this.selModel;
+    },
+    /*
+     * Render the Roo.bootstrap object from renderder
+     */
+    renderCellObject : function(r)
+    {
+        var _this = this;
         
-        this.haveProgress = true;
-   
-        var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
-        
-        var c = new Roo.data.Connection();
-        c.request({
-            url : this.form.progressUrl,
-            params: {
-                id : uid
-            },
-            method: 'GET',
-            success : function(req){
-               //console.log(data);
-                var rdata = false;
-                var edata;
-                try  {
-                   rdata = Roo.decode(req.responseText)
-                } catch (e) {
-                    Roo.log("Invalid data from server..");
-                    Roo.log(edata);
-                    return;
-                }
-                if (!rdata || !rdata.success) {
-                    Roo.log(rdata);
-                    Roo.MessageBox.alert(Roo.encode(rdata));
-                    return;
-                }
-                var data = rdata.data;
-                
-                if (this.uploadComplete) {
-                   Roo.MessageBox.hide();
-                   return;
-                }
-                   
-                if (data){
-                    Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
-                       Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
-                    );
-                }
-                this.uploadProgress.defer(2000,this);
-            },
-       
-            failure: function(data) {
-                Roo.log('progress url failed ');
-                Roo.log(data);
-            },
-            scope : this
-        });
-           
+        r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
+        
+        var t = r.cfg.render(r.container);
+        
+        if(r.cfg.cn){
+            Roo.each(r.cfg.cn, function(c){
+                var child = {
+                    container: t.getChildContainer(),
+                    cfg: c
+                };
+                _this.renderCellObject(child);
+            })
+        }
     },
-    
-    
-    run : function()
+    /**
+     * get the Row Index from a dom element.
+     * @param {Roo.Element} row The row to look for
+     * @returns {Number} the row
+     */
+    getRowIndex : function(row)
     {
-        // run get Values on the form, so it syncs any secondary forms.
-        this.form.getValues();
+        var rowIndex = -1;
         
-        var o = this.options;
-        var method = this.getMethod();
-        var isPost = method == 'POST';
-        if(o.clientValidation === false || this.form.isValid()){
-            
-            if (this.form.progressUrl) {
-                this.form.findField('UPLOAD_IDENTIFIER').setValue(
-                    (new Date() * 1) + '' + Math.random());
-                    
-            } 
-            
-            
-            Roo.Ajax.request(Roo.apply(this.createCallback(), {
-                form:this.form.el.dom,
-                url:this.getUrl(!isPost),
-                method: method,
-                params:isPost ? this.getParams() : null,
-                isUpload: this.form.fileUpload,
-                formData : this.form.formData
-            }));
+        Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
+            if(el != row){
+                return;
+            }
             
-            this.uploadProgress();
-
-        }else if (o.clientValidation !== false){ // client validation failed
-            this.failureType = Roo.form.Action.CLIENT_INVALID;
-            this.form.afterAction(this, false);
-        }
+            rowIndex = index;
+        });
+        
+        return rowIndex;
     },
-
-    success : function(response)
+    /**
+     * get the header TH element for columnIndex
+     * @param {Number} columnIndex
+     * @returns {Roo.Element}
+     */
+    getHeaderIndex: function(colIndex)
     {
-        this.uploadComplete= true;
-        if (this.haveProgress) {
-            Roo.MessageBox.hide();
+        var cols = this.headEl.select('th', true).elements;
+        return cols[colIndex]; 
+    },
+    /**
+     * get the Column Index from a dom element. (using regex on x-hcol-{colid})
+     * @param {domElement} cell to look for
+     * @returns {Number} the column
+     */
+    getCellIndex : function(cell)
+    {
+        var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
+        if(id){
+            return parseInt(id[1], 10);
         }
+        return 0;
+    },
+     /**
+     * Returns the grid's underlying element = used by panel.Grid
+     * @return {Element} The element
+     */
+    getGridEl : function(){
+        return this.el;
+    },
+     /**
+     * Forces a resize - used by panel.Grid
+     * @return {Element} The element
+     */
+    autoSize : function()
+    {
+        //var ctr = Roo.get(this.container.dom.parentElement);
+        var ctr = Roo.get(this.el.dom);
         
+        var thd = this.getGridEl().select('thead',true).first();
+        var tbd = this.getGridEl().select('tbody', true).first();
+        var tfd = this.getGridEl().select('tfoot', true).first();
         
-        var result = this.processResponse(response);
-        if(result === true || result.success){
-            this.form.afterAction(this, true);
-            return;
-        }
-        if(result.errors){
-            this.form.markInvalid(result.errors);
-            this.failureType = Roo.form.Action.SERVER_INVALID;
+        var cw = ctr.getWidth();
+        this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
+        
+        if (tbd) {
+            
+            tbd.setWidth(ctr.getWidth());
+            // if the body has a max height - and then scrolls - we should perhaps set up the height here
+            // this needs fixing for various usage - currently only hydra job advers I think..
+            //tdb.setHeight(
+            //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
+            //); 
+            var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
+            cw -= barsize;
         }
-        this.form.afterAction(this, false);
+        cw = Math.max(cw, this.totalWidth);
+        this.getGridEl().select('tbody tr',true).setWidth(cw);
+        this.initCSS();
+        
+        // resize 'expandable coloumn?
+        
+        return; // we doe not have a view in this design..
+        
     },
-    failure : function(response)
+    onBodyScroll: function()
     {
-        this.uploadComplete= true;
-        if (this.haveProgress) {
-            Roo.MessageBox.hide();
+        //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
+        if(this.headEl){
+            this.headEl.setStyle({
+                'position' : 'relative',
+                'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
+            });
         }
         
-        this.response = response;
-        this.failureType = Roo.form.Action.CONNECT_FAILURE;
-        this.form.afterAction(this, false);
-    },
-    
-    handleResponse : function(response){
-        if(this.form.errorReader){
-            var rs = this.form.errorReader.read(response);
-            var errors = [];
-            if(rs.records){
-                for(var i = 0, len = rs.records.length; i < len; i++) {
-                    var r = rs.records[i];
-                    errors[i] = r.data;
+        if(this.lazyLoad){
+            
+            var scrollHeight = this.bodyEl.dom.scrollHeight;
+            
+            var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
+            
+            var height = this.bodyEl.getHeight();
+            
+            if(scrollHeight - height == scrollTop) {
+                
+                var total = this.ds.getTotalCount();
+                
+                if(this.footer.cursor + this.footer.pageSize < total){
+                    
+                    this.footer.ds.load({
+                        params : {
+                            start : this.footer.cursor + this.footer.pageSize,
+                            limit : this.footer.pageSize
+                        },
+                        add : true
+                    });
                 }
             }
-            if(errors.length < 1){
-                errors = null;
-            }
-            return {
-                success : rs.success,
-                errors : errors
-            };
-        }
-        var ret = false;
-        try {
-            ret = Roo.decode(response.responseText);
-        } catch (e) {
-            ret = {
-                success: false,
-                errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
-                errors : []
-            };
+            
         }
-        return ret;
+    },
+    onColumnSplitterMoved : function(i, diff)
+    {
+        this.userResized = true;
         
-    }
-});
-
-
-Roo.form.Action.Load = function(form, options){
-    Roo.form.Action.Load.superclass.constructor.call(this, form, options);
-    this.reader = this.form.reader;
-};
-
-Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
-    type : 'load',
-
-    run : function(){
+        var cm = this.colModel;
         
-        Roo.Ajax.request(Roo.apply(
-                this.createCallback(), {
-                    method:this.getMethod(),
-                    url:this.getUrl(false),
-                    params:this.getParams()
-        }));
-    },
-
-    success : function(response){
+        var w = this.getHeaderIndex(i).getWidth() + diff;
         
-        var result = this.processResponse(response);
-        if(result === true || !result.success || !result.data){
-            this.failureType = Roo.form.Action.LOAD_FAILURE;
-            this.form.afterAction(this, false);
-            return;
-        }
-        this.form.clearInvalid();
-        this.form.setValues(result.data);
-        this.form.afterAction(this, true);
+        
+        cm.setColumnWidth(i, w, true);
+        this.initCSS();
+        //var cid = cm.getColumnId(i); << not used in this version?
+       /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
+        
+        this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
+        this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
+        this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
+*/
+        //this.updateSplitters();
+        //this.layout(); << ??
+        this.fireEvent("columnresize", i, w);
     },
-
-    handleResponse : function(response){
-        if(this.form.reader){
-            var rs = this.form.reader.read(response);
-            var data = rs.records && rs.records[0] ? rs.records[0].data : null;
-            return {
-                success : rs.success,
-                data : data
-            };
+    onHeaderChange : function()
+    {
+        var header = this.renderHeader();
+        var table = this.el.select('table', true).first();
+        
+        this.headEl.remove();
+        this.headEl = table.createChild(header, this.bodyEl, false);
+        
+        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+            e.on('click', this.sort, this);
+        }, this);
+        
+        if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+            new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+        }
+        
+    },
+    
+    onHiddenChange : function(colModel, colIndex, hidden)
+    {
+        /*
+        this.cm.setHidden()
+        var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
+        var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
+        
+        this.CSS.updateRule(thSelector, "display", "");
+        this.CSS.updateRule(tdSelector, "display", "");
+        
+        if(hidden){
+            this.CSS.updateRule(thSelector, "display", "none");
+            this.CSS.updateRule(tdSelector, "display", "none");
+        }
+        */
+        // onload calls initCSS()
+        this.onHeaderChange();
+        this.onLoad();
+    },
+    
+    setColumnWidth: function(col_index, width)
+    {
+        // width = "md-2 xs-2..."
+        if(!this.colModel.config[col_index]) {
+            return;
+        }
+        
+        var w = width.split(" ");
+        
+        var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
+        
+        var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
+        
+        
+        for(var j = 0; j < w.length; j++) {
+            
+            if(!w[j]) {
+                continue;
+            }
+            
+            var size_cls = w[j].split("-");
+            
+            if(!Number.isInteger(size_cls[1] * 1)) {
+                continue;
+            }
+            
+            if(!this.colModel.config[col_index][size_cls[0]]) {
+                continue;
+            }
+            
+            if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
+                continue;
+            }
+            
+            h_row[0].classList.replace(
+                "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
+                "col-"+size_cls[0]+"-"+size_cls[1]
+            );
+            
+            for(var i = 0; i < rows.length; i++) {
+                
+                var size_cls = w[j].split("-");
+                
+                if(!Number.isInteger(size_cls[1] * 1)) {
+                    continue;
+                }
+                
+                if(!this.colModel.config[col_index][size_cls[0]]) {
+                    continue;
+                }
+                
+                if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
+                    continue;
+                }
+                
+                rows[i].classList.replace(
+                    "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
+                    "col-"+size_cls[0]+"-"+size_cls[1]
+                );
+            }
+            
+            this.colModel.config[col_index][size_cls[0]] = size_cls[1];
         }
-        return Roo.decode(response.responseText);
     }
 });
 
-Roo.form.Action.ACTION_TYPES = {
-    'load' : Roo.form.Action.Load,
-    'submit' : Roo.form.Action.Submit
-};/*
+// currently only used to find the split on drag.. 
+Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
+
+/**
+ * @depricated
+*/
+Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
+Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
+/*
  * - LGPL
  *
- * form
- *
+ * table cell
+ * 
  */
 
 /**
- * @class Roo.bootstrap.Form
+ * @class Roo.bootstrap.TableCell
  * @extends Roo.bootstrap.Component
- * Bootstrap Form class
- * @cfg {String} method  GET | POST (default POST)
- * @cfg {String} labelAlign top | left (default top)
- * @cfg {String} align left  | right - for navbars
- * @cfg {Boolean} loadMask load mask when submit (default true)
-
- *
+ * Bootstrap TableCell class
+ * @cfg {String} html cell contain text
+ * @cfg {String} cls cell class
+ * @cfg {String} tag cell tag (td|th) default td
+ * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
+ * @cfg {String} align Aligns the content in a cell
+ * @cfg {String} axis Categorizes cells
+ * @cfg {String} bgcolor Specifies the background color of a cell
+ * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
+ * @cfg {Number} colspan Specifies the number of columns a cell should span
+ * @cfg {String} headers Specifies one or more header cells a cell is related to
+ * @cfg {Number} height Sets the height of a cell
+ * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
+ * @cfg {Number} rowspan Sets the number of rows a cell should span
+ * @cfg {String} scope Defines a way to associate header cells and data cells in a table
+ * @cfg {String} valign Vertical aligns the content in a cell
+ * @cfg {Number} width Specifies the width of a cell
+ * 
  * @constructor
- * Create a new Form
+ * Create a new TableCell
  * @param {Object} config The config object
  */
 
-
-Roo.bootstrap.Form = function(config){
-    
-    Roo.bootstrap.Form.superclass.constructor.call(this, config);
-    
-    Roo.bootstrap.Form.popover.apply();
-    
-    this.addEvents({
-        /**
-         * @event clientvalidation
-         * If the monitorValid config option is true, this event fires repetitively to notify of valid state
-         * @param {Form} this
-         * @param {Boolean} valid true if the form has passed client-side validation
-         */
-        clientvalidation: true,
-        /**
-         * @event beforeaction
-         * Fires before any action is performed. Return false to cancel the action.
-         * @param {Form} this
-         * @param {Action} action The action to be performed
-         */
-        beforeaction: true,
-        /**
-         * @event actionfailed
-         * Fires when an action fails.
-         * @param {Form} this
-         * @param {Action} action The action that failed
-         */
-        actionfailed : true,
-        /**
-         * @event actioncomplete
-         * Fires when an action is completed.
-         * @param {Form} this
-         * @param {Action} action The action that completed
-         */
-        actioncomplete : true
-    });
+Roo.bootstrap.TableCell = function(config){
+    Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
 };
 
-Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
-
-     /**
-     * @cfg {String} method
-     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
-     */
-    method : 'POST',
-    /**
-     * @cfg {String} url
-     * The URL to use for form actions if one isn't supplied in the action options.
-     */
-    /**
-     * @cfg {Boolean} fileUpload
-     * Set to true if this form is a file upload.
-     */
-
-    /**
-     * @cfg {Object} baseParams
-     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
-     */
-
-    /**
-     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
-     */
-    timeout: 30,
-    /**
-     * @cfg {Sting} align (left|right) for navbar forms
-     */
-    align : 'left',
-
-    // private
-    activeAction : null,
-
-    /**
-     * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
-     * element by passing it or its id or mask the form itself by passing in true.
-     * @type Mixed
-     */
-    waitMsgTarget : false,
-
-    loadMask : true,
+Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
     
-    /**
-     * @cfg {Boolean} errorMask (true|false) default false
-     */
-    errorMask : false,
+    html: false,
+    cls: false,
+    tag: false,
+    abbr: false,
+    align: false,
+    axis: false,
+    bgcolor: false,
+    charoff: false,
+    colspan: false,
+    headers: false,
+    height: false,
+    nowrap: false,
+    rowspan: false,
+    scope: false,
+    valign: false,
+    width: false,
     
-    /**
-     * @cfg {Number} maskOffset Default 100
-     */
-    maskOffset : 100,
     
-    /**
-     * @cfg {Boolean} maskBody
-     */
-    maskBody : false,
-
     getAutoCreate : function(){
-
-        var cfg = {
-            tag: 'form',
-            method : this.method || 'POST',
-            id : this.id || Roo.id(),
-            cls : ''
+        var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
+       
+        cfg = {
+            tag: 'td'
         };
-        if (this.parent().xtype.match(/^Nav/)) {
-            cfg.cls = 'navbar-form form-inline navbar-' + this.align;
-
+        
+        if(this.tag){
+            cfg.tag = this.tag;
         }
-
-        if (this.labelAlign == 'left' ) {
-            cfg.cls += ' form-horizontal';
+        
+        if (this.html) {
+            cfg.html=this.html
         }
-
-
+        if (this.cls) {
+            cfg.cls=this.cls
+        }
+        if (this.abbr) {
+            cfg.abbr=this.abbr
+        }
+        if (this.align) {
+            cfg.align=this.align
+        }
+        if (this.axis) {
+            cfg.axis=this.axis
+        }
+        if (this.bgcolor) {
+            cfg.bgcolor=this.bgcolor
+        }
+        if (this.charoff) {
+            cfg.charoff=this.charoff
+        }
+        if (this.colspan) {
+            cfg.colspan=this.colspan
+        }
+        if (this.headers) {
+            cfg.headers=this.headers
+        }
+        if (this.height) {
+            cfg.height=this.height
+        }
+        if (this.nowrap) {
+            cfg.nowrap=this.nowrap
+        }
+        if (this.rowspan) {
+            cfg.rowspan=this.rowspan
+        }
+        if (this.scope) {
+            cfg.scope=this.scope
+        }
+        if (this.valign) {
+            cfg.valign=this.valign
+        }
+        if (this.width) {
+            cfg.width=this.width
+        }
+        
+       
         return cfg;
-    },
-    initEvents : function()
-    {
-        this.el.on('submit', this.onSubmit, this);
-        // this was added as random key presses on the form where triggering form submit.
-        this.el.on('keypress', function(e) {
-            if (e.getCharCode() != 13) {
-                return true;
-            }
-            // we might need to allow it for textareas.. and some other items.
-            // check e.getTarget().
+    }
+   
+});
 
-            if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
-                return true;
-            }
 
-            Roo.log("keypress blocked");
+ /*
+ * - LGPL
+ *
+ * table row
+ * 
+ */
 
-            e.preventDefault();
-            return false;
-        });
-        
-    },
-    // private
-    onSubmit : function(e){
-        e.stopEvent();
-    },
+/**
+ * @class Roo.bootstrap.TableRow
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TableRow class
+ * @cfg {String} cls row class
+ * @cfg {String} align Aligns the content in a table row
+ * @cfg {String} bgcolor Specifies a background color for a table row
+ * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
+ * @cfg {String} valign Vertical aligns the content in a table row
+ * 
+ * @constructor
+ * Create a new TableRow
+ * @param {Object} config The config object
+ */
 
-     /**
-     * Returns true if client-side validation on the form is successful.
-     * @return Boolean
-     */
-    isValid : function(){
-        var items = this.getItems();
-        var valid = true;
-        var target = false;
-        
-        items.each(function(f){
-            
-            if(f.validate()){
-                return;
-            }
-            
-            Roo.log('invalid field: ' + f.name);
-            
-            valid = false;
+Roo.bootstrap.TableRow = function(config){
+    Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
+};
 
-            if(!target && f.el.isVisible(true)){
-                target = f;
-            }
-           
-        });
-        
-        if(this.errorMask && !valid){
-            Roo.bootstrap.Form.popover.mask(this, target);
-        }
-        
-        return valid;
-    },
+Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
     
-    /**
-     * Returns true if any fields in this form have changed since their original load.
-     * @return Boolean
-     */
-    isDirty : function(){
-        var dirty = false;
-        var items = this.getItems();
-        items.each(function(f){
-           if(f.isDirty()){
-               dirty = true;
-               return false;
-           }
-           return true;
-        });
-        return dirty;
-    },
-     /**
-     * Performs a predefined action (submit or load) or custom actions you define on this form.
-     * @param {String} actionName The name of the action type
-     * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
-     * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
-     * accept other config options):
-     * <pre>
-Property          Type             Description
-----------------  ---------------  ----------------------------------------------------------------------------------
-url               String           The url for the action (defaults to the form's url)
-method            String           The form method to use (defaults to the form's method, or POST if not defined)
-params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
-clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
-                                   validate the form on the client (defaults to false)
-     * </pre>
-     * @return {BasicForm} this
-     */
-    doAction : function(action, options){
-        if(typeof action == 'string'){
-            action = new Roo.form.Action.ACTION_TYPES[action](this, options);
+    cls: false,
+    align: false,
+    bgcolor: false,
+    charoff: false,
+    valign: false,
+    
+    getAutoCreate : function(){
+        var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
+       
+        cfg = {
+            tag: 'tr'
+        };
+            
+        if(this.cls){
+            cfg.cls = this.cls;
         }
-        if(this.fireEvent('beforeaction', this, action) !== false){
-            this.beforeAction(action);
-            action.run.defer(100, action);
+        if(this.align){
+            cfg.align = this.align;
         }
-        return this;
-    },
-
-    // private
-    beforeAction : function(action){
-        var o = action.options;
-        
-        if(this.loadMask){
-            
-            if(this.maskBody){
-                Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
-            } else {
-                this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
-            }
+        if(this.bgcolor){
+            cfg.bgcolor = this.bgcolor;
         }
-        // not really supported yet.. ??
+        if(this.charoff){
+            cfg.charoff = this.charoff;
+        }
+        if(this.valign){
+            cfg.valign = this.valign;
+        }
+       
+        return cfg;
+    }
+   
+});
 
-        //if(this.waitMsgTarget === true){
-        //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
-        //}else if(this.waitMsgTarget){
-        //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
-        //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
-        //}else {
-        //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
-       // }
 
-    },
+ /*
+ * - LGPL
+ *
+ * table body
+ * 
+ */
 
-    // private
-    afterAction : function(action, success){
-        this.activeAction = null;
-        var o = action.options;
+/**
+ * @class Roo.bootstrap.TableBody
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TableBody class
+ * @cfg {String} cls element class
+ * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
+ * @cfg {String} align Aligns the content inside the element
+ * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
+ * @cfg {String} valign Vertical aligns the content inside the <tbody> element
+ * 
+ * @constructor
+ * Create a new TableBody
+ * @param {Object} config The config object
+ */
 
-        if(this.loadMask){
+Roo.bootstrap.TableBody = function(config){
+    Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
+    
+    cls: false,
+    tag: false,
+    align: false,
+    charoff: false,
+    valign: false,
+    
+    getAutoCreate : function(){
+        var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
+       
+        cfg = {
+            tag: 'tbody'
+        };
             
-            if(this.maskBody){
-                Roo.get(document.body).unmask();
-            } else {
-                this.el.unmask();
-            }
+        if (this.cls) {
+            cfg.cls=this.cls
+        }
+        if(this.tag){
+            cfg.tag = this.tag;
+        }
+       
+        if(this.align){
+            cfg.align = this.align;
+        }
+        if(this.charoff){
+            cfg.charoff = this.charoff;
+        }
+        if(this.valign){
+            cfg.valign = this.valign;
         }
         
-        //if(this.waitMsgTarget === true){
-//            this.el.unmask();
-        //}else if(this.waitMsgTarget){
-        //    this.waitMsgTarget.unmask();
-        //}else{
-        //    Roo.MessageBox.updateProgress(1);
-        //    Roo.MessageBox.hide();
-       // }
-        //
-        if(success){
-            if(o.reset){
-                this.reset();
-            }
-            Roo.callback(o.success, o.scope, [this, action]);
-            this.fireEvent('actioncomplete', this, action);
+        return cfg;
+    }
+    
+    
+//    initEvents : function()
+//    {
+//        
+//        if(!this.store){
+//            return;
+//        }
+//        
+//        this.store = Roo.factory(this.store, Roo.data);
+//        this.store.on('load', this.onLoad, this);
+//        
+//        this.store.load();
+//        
+//    },
+//    
+//    onLoad: function () 
+//    {   
+//        this.fireEvent('load', this);
+//    }
+//    
+//   
+});
 
-        }else{
 
-            // failure condition..
-            // 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) != 'undefined')  &&
-                (typeof(action.result.errors) != 'undefined')  &&
-                (typeof(action.result.errors.needs_confirm) != 'undefined')
-           ){
-                var _t = this;
-                Roo.log("not supported yet");
-                 /*
+ /*
+ * 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.MessageBox.confirm(
-                    "Change requires confirmation",
-                    action.result.errorMsg,
-                    function(r) {
-                        if (r != 'yes') {
-                            return;
-                        }
-                        _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
-                    }
+// as we use this in bootstrap.
+Roo.namespace('Roo.form');
+ /**
+ * @class Roo.form.Action
+ * Internal Class used to handle form actions
+ * @constructor
+ * @param {Roo.form.BasicForm} el The form element or its id
+ * @param {Object} config Configuration options
+ */
 
-                );
-                */
+// define the action interface
+Roo.form.Action = function(form, options){
+    this.form = form;
+    this.options = options || {};
+};
+/**
+ * Client Validation Failed
+ * @const 
+ */
+Roo.form.Action.CLIENT_INVALID = 'client';
+/**
+ * Server Validation Failed
+ * @const 
+ */
+Roo.form.Action.SERVER_INVALID = 'server';
+ /**
+ * Connect to Server Failed
+ * @const 
+ */
+Roo.form.Action.CONNECT_FAILURE = 'connect';
+/**
+ * Reading Data from Server Failed
+ * @const 
+ */
+Roo.form.Action.LOAD_FAILURE = 'load';
 
+Roo.form.Action.prototype = {
+    type : 'default',
+    failureType : undefined,
+    response : undefined,
+    result : undefined,
 
-                return;
-            }
+    // interface method
+    run : function(options){
 
-            Roo.callback(o.failure, o.scope, [this, action]);
-            // show an error message if no failed handler is set..
-            if (!this.hasListener('actionfailed')) {
-                Roo.log("need to add dialog support");
-                /*
-                Roo.MessageBox.alert("Error",
-                    (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
-                        action.result.errorMsg :
-                        "Saving Failed, please check your entries or try again"
-                );
-                */
-            }
+    },
 
-            this.fireEvent('actionfailed', this, action);
-        }
+    // interface method
+    success : function(response){
 
     },
-    /**
-     * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
-     * @param {String} id The value to search for
-     * @return Field
-     */
-    findField : function(id){
-        var items = this.getItems();
-        var field = items.get(id);
-        if(!field){
-             items.each(function(f){
-                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
-                    field = f;
-                    return false;
-                }
-                return true;
-            });
-        }
-        return field || null;
+
+    // interface method
+    handleResponse : function(response){
+
     },
-     /**
-     * Mark fields in this form invalid in bulk.
-     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
-     * @return {BasicForm} this
-     */
-    markInvalid : function(errors){
-        if(errors instanceof Array){
-            for(var i = 0, len = errors.length; i < len; i++){
-                var fieldError = errors[i];
-                var f = this.findField(fieldError.id);
-                if(f){
-                    f.markInvalid(fieldError.msg);
-                }
-            }
-        }else{
-            var field, id;
-            for(id in errors){
-                if(typeof errors[id] != 'function' && (field = this.findField(id))){
-                    field.markInvalid(errors[id]);
-                }
-            }
-        }
-        //Roo.each(this.childForms || [], function (f) {
-        //    f.markInvalid(errors);
-        //});
 
-        return this;
+    // default connection failure
+    failure : function(response){
+        
+        this.response = response;
+        this.failureType = Roo.form.Action.CONNECT_FAILURE;
+        this.form.afterAction(this, false);
     },
 
-    /**
-     * Set values for fields in this form in bulk.
-     * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
-     * @return {BasicForm} this
-     */
-    setValues : function(values){
-        if(values instanceof Array){ // array of objects
-            for(var i = 0, len = values.length; i < len; i++){
-                var v = values[i];
-                var f = this.findField(v.id);
-                if(f){
-                    f.setValue(v.value);
-                    if(this.trackResetOnLoad){
-                        f.originalValue = f.getValue();
-                    }
-                }
-            }
-        }else{ // object hash
-            var field, id;
-            for(id in values){
-                if(typeof values[id] != 'function' && (field = this.findField(id))){
-
-                    if (field.setFromData &&
-                        field.valueField &&
-                        field.displayField &&
-                        // combos' with local stores can
-                        // be queried via setValue()
-                        // to set their value..
-                        (field.store && !field.store.isLocal)
-                        ) {
-                        // it's a combo
-                        var sd = { };
-                        sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
-                        sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
-                        field.setFromData(sd);
-
-                    } else if(field.setFromData && (field.store && !field.store.isLocal)) {
-                        
-                        field.setFromData(values);
-                        
-                    } else {
-                        field.setValue(values[id]);
-                    }
-
+    processResponse : function(response){
+        this.response = response;
+        if(!response.responseText){
+            return true;
+        }
+        this.result = this.handleResponse(response);
+        return this.result;
+    },
 
-                    if(this.trackResetOnLoad){
-                        field.originalValue = field.getValue();
-                    }
-                }
+    // utility functions used internally
+    getUrl : function(appendParams){
+        var url = this.options.url || this.form.url || this.form.el.dom.action;
+        if(appendParams){
+            var p = this.getParams();
+            if(p){
+                url += (url.indexOf('?') != -1 ? '&' : '?') + p;
             }
         }
+        return url;
+    },
 
-        //Roo.each(this.childForms || [], function (f) {
-        //    f.setValues(values);
-        //});
+    getMethod : function(){
+        return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
+    },
 
-        return this;
+    getParams : function(){
+        var bp = this.form.baseParams;
+        var p = this.options.params;
+        if(p){
+            if(typeof p == "object"){
+                p = Roo.urlEncode(Roo.applyIf(p, bp));
+            }else if(typeof p == 'string' && bp){
+                p += '&' + Roo.urlEncode(bp);
+            }
+        }else if(bp){
+            p = Roo.urlEncode(bp);
+        }
+        return p;
     },
 
-    /**
-     * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
-     * they are returned as an array.
-     * @param {Boolean} asString
-     * @return {Object}
-     */
-    getValues : function(asString){
-        //if (this.childForms) {
-            // copy values from the child forms
-        //    Roo.each(this.childForms, function (f) {
-        //        this.setValues(f.getValues());
-        //    }, this);
-        //}
+    createCallback : function(){
+        return {
+            success: this.success,
+            failure: this.failure,
+            scope: this,
+            timeout: (this.form.timeout*1000),
+            upload: this.form.fileUpload ? this.success : undefined
+        };
+    }
+};
 
+Roo.form.Action.Submit = function(form, options){
+    Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
+};
 
+Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
+    type : 'submit',
 
-        var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
-        if(asString === true){
-            return fs;
+    haveProgress : false,
+    uploadComplete : false,
+    
+    // uploadProgress indicator.
+    uploadProgress : function()
+    {
+        if (!this.form.progressUrl) {
+            return;
         }
-        return Roo.urlDecode(fs);
+        
+        if (!this.haveProgress) {
+            Roo.MessageBox.progress("Uploading", "Uploading");
+        }
+        if (this.uploadComplete) {
+           Roo.MessageBox.hide();
+           return;
+        }
+        
+        this.haveProgress = true;
+   
+        var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
+        
+        var c = new Roo.data.Connection();
+        c.request({
+            url : this.form.progressUrl,
+            params: {
+                id : uid
+            },
+            method: 'GET',
+            success : function(req){
+               //console.log(data);
+                var rdata = false;
+                var edata;
+                try  {
+                   rdata = Roo.decode(req.responseText)
+                } catch (e) {
+                    Roo.log("Invalid data from server..");
+                    Roo.log(edata);
+                    return;
+                }
+                if (!rdata || !rdata.success) {
+                    Roo.log(rdata);
+                    Roo.MessageBox.alert(Roo.encode(rdata));
+                    return;
+                }
+                var data = rdata.data;
+                
+                if (this.uploadComplete) {
+                   Roo.MessageBox.hide();
+                   return;
+                }
+                   
+                if (data){
+                    Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
+                       Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
+                    );
+                }
+                this.uploadProgress.defer(2000,this);
+            },
+       
+            failure: function(data) {
+                Roo.log('progress url failed ');
+                Roo.log(data);
+            },
+            scope : this
+        });
+           
     },
-
-    /**
-     * Returns the fields in this form as an object with key/value pairs.
-     * This differs from getValues as it calls getValue on each child item, rather than using dom data.
-     * @return {Object}
-     */
-    getFieldValues : function(with_hidden)
+    
+    
+    run : function()
     {
-        var items = this.getItems();
-        var ret = {};
-        items.each(function(f){
+        // run get Values on the form, so it syncs any secondary forms.
+        this.form.getValues();
+        
+        var o = this.options;
+        var method = this.getMethod();
+        var isPost = method == 'POST';
+        if(o.clientValidation === false || this.form.isValid()){
             
-            if (!f.getName()) {
-                return;
-            }
+            if (this.form.progressUrl) {
+                this.form.findField('UPLOAD_IDENTIFIER').setValue(
+                    (new Date() * 1) + '' + Math.random());
+                    
+            } 
             
-            var v = f.getValue();
             
-            if (f.inputType =='radio') {
-                if (typeof(ret[f.getName()]) == 'undefined') {
-                    ret[f.getName()] = ''; // empty..
-                }
-
-                if (!f.el.dom.checked) {
-                    return;
-
-                }
-                v = f.el.dom.value;
-
-            }
+            Roo.Ajax.request(Roo.apply(this.createCallback(), {
+                form:this.form.el.dom,
+                url:this.getUrl(!isPost),
+                method: method,
+                params:isPost ? this.getParams() : null,
+                isUpload: this.form.fileUpload,
+                formData : this.form.formData
+            }));
             
-            if(f.xtype == 'MoneyField'){
-                ret[f.currencyName] = f.getCurrency();
-            }
-
-            // not sure if this supported any more..
-            if ((typeof(v) == 'object') && f.getRawValue) {
-                v = f.getRawValue() ; // dates..
-            }
-            // combo boxes where name != hiddenName...
-            if (f.name !== false && f.name != '' && f.name != f.getName()) {
-                ret[f.name] = f.getRawValue();
-            }
-            ret[f.getName()] = v;
-        });
+            this.uploadProgress();
 
-        return ret;
+        }else if (o.clientValidation !== false){ // client validation failed
+            this.failureType = Roo.form.Action.CLIENT_INVALID;
+            this.form.afterAction(this, false);
+        }
     },
 
-    /**
-     * Clears all invalid messages in this form.
-     * @return {BasicForm} this
-     */
-    clearInvalid : function(){
-        var items = this.getItems();
-
-        items.each(function(f){
-           f.clearInvalid();
-        });
-
-        return this;
-    },
-
-    /**
-     * Resets this form.
-     * @return {BasicForm} this
-     */
-    reset : function(){
-        var items = this.getItems();
-        items.each(function(f){
-            f.reset();
-        });
-
-        Roo.each(this.childForms || [], function (f) {
-            f.reset();
-        });
-
-
-        return this;
-    },
-    
-    getItems : function()
+    success : function(response)
     {
-        var r=new Roo.util.MixedCollection(false, function(o){
-            return o.id || (o.id = Roo.id());
-        });
-        var iter = function(el) {
-            if (el.inputEl) {
-                r.add(el);
-            }
-            if (!el.items) {
-                return;
-            }
-            Roo.each(el.items,function(e) {
-                iter(e);
-            });
-        };
-
-        iter(this);
-        return r;
+        this.uploadComplete= true;
+        if (this.haveProgress) {
+            Roo.MessageBox.hide();
+        }
+        
+        
+        var result = this.processResponse(response);
+        if(result === true || result.success){
+            this.form.afterAction(this, true);
+            return;
+        }
+        if(result.errors){
+            this.form.markInvalid(result.errors);
+            this.failureType = Roo.form.Action.SERVER_INVALID;
+        }
+        this.form.afterAction(this, false);
     },
-    
-    hideFields : function(items)
+    failure : function(response)
     {
-        Roo.each(items, function(i){
-            
-            var f = this.findField(i);
-            
-            if(!f){
-                return;
-            }
-            
-            f.hide();
-            
-        }, this);
+        this.uploadComplete= true;
+        if (this.haveProgress) {
+            Roo.MessageBox.hide();
+        }
+        
+        this.response = response;
+        this.failureType = Roo.form.Action.CONNECT_FAILURE;
+        this.form.afterAction(this, false);
     },
     
-    showFields : function(items)
-    {
-        Roo.each(items, function(i){
-            
-            var f = this.findField(i);
-            
-            if(!f){
-                return;
+    handleResponse : function(response){
+        if(this.form.errorReader){
+            var rs = this.form.errorReader.read(response);
+            var errors = [];
+            if(rs.records){
+                for(var i = 0, len = rs.records.length; i < len; i++) {
+                    var r = rs.records[i];
+                    errors[i] = r.data;
+                }
             }
-            
-            f.show();
-            
-        }, this);
-    }
-
-});
-
-Roo.apply(Roo.bootstrap.Form, {
-    
-    popover : {
-        
-        padding : 5,
-        
-        isApplied : false,
-        
-        isMasked : false,
-        
-        form : false,
-        
-        target : false,
-        
-        toolTip : false,
-        
-        intervalID : false,
-        
-        maskEl : false,
-        
-        apply : function()
-        {
-            if(this.isApplied){
-                return;
+            if(errors.length < 1){
+                errors = null;
             }
-            
-            this.maskEl = {
-                top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
-                left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
-                bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
-                right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
+            return {
+                success : rs.success,
+                errors : errors
             };
-            
-            this.maskEl.top.enableDisplayMode("block");
-            this.maskEl.left.enableDisplayMode("block");
-            this.maskEl.bottom.enableDisplayMode("block");
-            this.maskEl.right.enableDisplayMode("block");
-            
-            this.toolTip = new Roo.bootstrap.Tooltip({
-                cls : 'roo-form-error-popover',
-                alignment : {
-                    'left' : ['r-l', [-2,0], 'right'],
-                    'right' : ['l-r', [2,0], 'left'],
-                    'bottom' : ['tl-bl', [0,2], 'top'],
-                    'top' : [ 'bl-tl', [0,-2], 'bottom']
-                }
-            });
-            
-            this.toolTip.render(Roo.get(document.body));
-
-            this.toolTip.el.enableDisplayMode("block");
-            
-            Roo.get(document.body).on('click', function(){
-                this.unmask();
-            }, this);
-            
-            Roo.get(document.body).on('touchstart', function(){
-                this.unmask();
-            }, this);
-            
-            this.isApplied = true
-        },
+        }
+        var ret = false;
+        try {
+            ret = Roo.decode(response.responseText);
+        } catch (e) {
+            ret = {
+                success: false,
+                errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
+                errors : []
+            };
+        }
+        return ret;
         
-        mask : function(form, target)
-        {
-            this.form = form;
-            
-            this.target = target;
-            
-            if(!this.form.errorMask || !target.el){
-                return;
-            }
-            
-            var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
-            
-            Roo.log(scrollable);
-            
-            var ot = this.target.el.calcOffsetsTo(scrollable);
-            
-            var scrollTo = ot[1] - this.form.maskOffset;
-            
-            scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
-            
-            scrollable.scrollTo('top', scrollTo);
-            
-            var box = this.target.el.getBox();
-            Roo.log(box);
-            var zIndex = Roo.bootstrap.Modal.zIndex++;
-
-            
-            this.maskEl.top.setStyle('position', 'absolute');
-            this.maskEl.top.setStyle('z-index', zIndex);
-            this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
-            this.maskEl.top.setLeft(0);
-            this.maskEl.top.setTop(0);
-            this.maskEl.top.show();
-            
-            this.maskEl.left.setStyle('position', 'absolute');
-            this.maskEl.left.setStyle('z-index', zIndex);
-            this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
-            this.maskEl.left.setLeft(0);
-            this.maskEl.left.setTop(box.y - this.padding);
-            this.maskEl.left.show();
-
-            this.maskEl.bottom.setStyle('position', 'absolute');
-            this.maskEl.bottom.setStyle('z-index', zIndex);
-            this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
-            this.maskEl.bottom.setLeft(0);
-            this.maskEl.bottom.setTop(box.bottom + this.padding);
-            this.maskEl.bottom.show();
+    }
+});
 
-            this.maskEl.right.setStyle('position', 'absolute');
-            this.maskEl.right.setStyle('z-index', zIndex);
-            this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
-            this.maskEl.right.setLeft(box.right + this.padding);
-            this.maskEl.right.setTop(box.y - this.padding);
-            this.maskEl.right.show();
 
-            this.toolTip.bindEl = this.target.el;
+Roo.form.Action.Load = function(form, options){
+    Roo.form.Action.Load.superclass.constructor.call(this, form, options);
+    this.reader = this.form.reader;
+};
 
-            this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
+    type : 'load',
 
-            var tip = this.target.blankText;
+    run : function(){
+        
+        Roo.Ajax.request(Roo.apply(
+                this.createCallback(), {
+                    method:this.getMethod(),
+                    url:this.getUrl(false),
+                    params:this.getParams()
+        }));
+    },
 
-            if(this.target.getValue() !== '' ) {
-                
-                if (this.target.invalidText.length) {
-                    tip = this.target.invalidText;
-                } else if (this.target.regexText.length){
-                    tip = this.target.regexText;
-                }
-            }
-
-            this.toolTip.show(tip);
-
-            this.intervalID = window.setInterval(function() {
-                Roo.bootstrap.Form.popover.unmask();
-            }, 10000);
-
-            window.onwheel = function(){ return false;};
-            
-            (function(){ this.isMasked = true; }).defer(500, this);
-            
-        },
+    success : function(response){
         
-        unmask : function()
-        {
-            if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
-                return;
-            }
-            
-            this.maskEl.top.setStyle('position', 'absolute');
-            this.maskEl.top.setSize(0, 0).setXY([0, 0]);
-            this.maskEl.top.hide();
-
-            this.maskEl.left.setStyle('position', 'absolute');
-            this.maskEl.left.setSize(0, 0).setXY([0, 0]);
-            this.maskEl.left.hide();
-
-            this.maskEl.bottom.setStyle('position', 'absolute');
-            this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
-            this.maskEl.bottom.hide();
+        var result = this.processResponse(response);
+        if(result === true || !result.success || !result.data){
+            this.failureType = Roo.form.Action.LOAD_FAILURE;
+            this.form.afterAction(this, false);
+            return;
+        }
+        this.form.clearInvalid();
+        this.form.setValues(result.data);
+        this.form.afterAction(this, true);
+    },
 
-            this.maskEl.right.setStyle('position', 'absolute');
-            this.maskEl.right.setSize(0, 0).setXY([0, 0]);
-            this.maskEl.right.hide();
-            
-            this.toolTip.hide();
-            
-            this.toolTip.el.hide();
-            
-            window.onwheel = function(){ return true;};
-            
-            if(this.intervalID){
-                window.clearInterval(this.intervalID);
-                this.intervalID = false;
-            }
-            
-            this.isMasked = false;
-            
+    handleResponse : function(response){
+        if(this.form.reader){
+            var rs = this.form.reader.read(response);
+            var data = rs.records && rs.records[0] ? rs.records[0].data : null;
+            return {
+                success : rs.success,
+                data : data
+            };
         }
-        
+        return Roo.decode(response.responseText);
     }
-    
 });
 
-/*
- * 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.VTypes
- * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
- * @singleton
- */
-Roo.form.VTypes = function(){
-    // closure these in so they are only created once.
-    var alpha = /^[a-zA-Z_]+$/;
-    var alphanum = /^[a-zA-Z0-9_]+$/;
-    var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
-    var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
-
-    // All these messages and functions are configurable
-    return {
-        /**
-         * The function used to validate email addresses
-         * @param {String} value The email address
-         */
-        'email' : function(v){
-            return email.test(v);
-        },
-        /**
-         * The error text to display when the email validation function returns false
-         * @type String
-         */
-        'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
-        /**
-         * The keystroke filter mask to be applied on email input
-         * @type RegExp
-         */
-        'emailMask' : /[a-z0-9_\.\-@]/i,
-
-        /**
-         * The function used to validate URLs
-         * @param {String} value The URL
-         */
-        'url' : function(v){
-            return url.test(v);
-        },
-        /**
-         * The error text to display when the url validation function returns false
-         * @type String
-         */
-        'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
-        
-        /**
-         * The function used to validate alpha values
-         * @param {String} value The value
-         */
-        'alpha' : function(v){
-            return alpha.test(v);
-        },
-        /**
-         * The error text to display when the alpha validation function returns false
-         * @type String
-         */
-        'alphaText' : 'This field should only contain letters and _',
-        /**
-         * The keystroke filter mask to be applied on alpha input
-         * @type RegExp
-         */
-        'alphaMask' : /[a-z_]/i,
-
-        /**
-         * The function used to validate alphanumeric values
-         * @param {String} value The value
-         */
-        'alphanum' : function(v){
-            return alphanum.test(v);
-        },
-        /**
-         * The error text to display when the alphanumeric validation function returns false
-         * @type String
-         */
-        'alphanumText' : 'This field should only contain letters, numbers and _',
-        /**
-         * The keystroke filter mask to be applied on alphanumeric input
-         * @type RegExp
-         */
-        'alphanumMask' : /[a-z0-9_]/i
-    };
-}();/*
+Roo.form.Action.ACTION_TYPES = {
+    'load' : Roo.form.Action.Load,
+    'submit' : Roo.form.Action.Submit
+};/*
  * - LGPL
  *
- * Input
- * 
+ * form
+ *
  */
 
 /**
- * @class Roo.bootstrap.Input
+ * @class Roo.bootstrap.Form
  * @extends Roo.bootstrap.Component
- * Bootstrap Input class
- * @cfg {Boolean} disabled is it disabled
- * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
- * @cfg {String} name name of the input
- * @cfg {string} fieldLabel - the label associated
- * @cfg {string} placeholder - placeholder to put in text.
- * @cfg {string}  before - input group add on before
- * @cfg {string} after - input group add on after
- * @cfg {string} size - (lg|sm) or leave empty..
- * @cfg {Number} xs colspan out of 12 for mobile-sized screens
- * @cfg {Number} sm colspan out of 12 for tablet-sized screens
- * @cfg {Number} md colspan out of 12 for computer-sized screens
- * @cfg {Number} lg colspan out of 12 for large computer-sized screens
- * @cfg {string} value default value of the input
- * @cfg {Number} labelWidth set the width of label 
- * @cfg {Number} labellg set the width of label (1-12)
- * @cfg {Number} labelmd set the width of label (1-12)
- * @cfg {Number} labelsm set the width of label (1-12)
- * @cfg {Number} labelxs set the width of label (1-12)
- * @cfg {String} labelAlign (top|left)
- * @cfg {Boolean} readOnly Specifies that the field should be read-only
- * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
- * @cfg {String} indicatorpos (left|right) default left
- * @cfg {String} capture (user|camera) use for file input only. (default empty)
- * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
- * @cfg {Boolean} preventMark Do not show tick or cross if error/success
+ * Bootstrap Form class
+ * @cfg {String} method  GET | POST (default POST)
+ * @cfg {String} labelAlign top | left (default top)
+ * @cfg {String} align left  | right - for navbars
+ * @cfg {Boolean} loadMask load mask when submit (default true)
 
- * @cfg {String} align (left|center|right) Default left
- * @cfg {Boolean} forceFeedback (true|false) Default false
- * 
+ *
  * @constructor
- * Create a new Input
+ * Create a new Form
  * @param {Object} config The config object
  */
 
-Roo.bootstrap.Input = function(config){
+
+Roo.bootstrap.Form = function(config){
     
-    Roo.bootstrap.Input.superclass.constructor.call(this, config);
+    Roo.bootstrap.Form.superclass.constructor.call(this, config);
+    
+    Roo.bootstrap.Form.popover.apply();
     
     this.addEvents({
         /**
-         * @event focus
-         * Fires when this field receives input focus.
-         * @param {Roo.form.Field} this
+         * @event clientvalidation
+         * If the monitorValid config option is true, this event fires repetitively to notify of valid state
+         * @param {Form} this
+         * @param {Boolean} valid true if the form has passed client-side validation
          */
-        focus : true,
+        clientvalidation: true,
         /**
-         * @event blur
-         * Fires when this field loses input focus.
-         * @param {Roo.form.Field} this
+         * @event beforeaction
+         * Fires before any action is performed. Return false to cancel the action.
+         * @param {Form} this
+         * @param {Action} action The action to be performed
          */
-        blur : true,
+        beforeaction: true,
         /**
-         * @event specialkey
-         * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
-         * {@link Roo.EventObject#getKey} to determine which key was pressed.
-         * @param {Roo.form.Field} this
-         * @param {Roo.EventObject} e The event object
+         * @event actionfailed
+         * Fires when an action fails.
+         * @param {Form} this
+         * @param {Action} action The action that failed
          */
-        specialkey : true,
+        actionfailed : true,
         /**
-         * @event change
-         * Fires just before the field blurs if the field value has changed.
-         * @param {Roo.form.Field} this
-         * @param {Mixed} newValue The new value
-         * @param {Mixed} oldValue The original value
+         * @event actioncomplete
+         * Fires when an action is completed.
+         * @param {Form} this
+         * @param {Action} action The action that completed
          */
-        change : true,
-        /**
-         * @event invalid
-         * Fires after the field has been marked as invalid.
-         * @param {Roo.form.Field} this
-         * @param {String} msg The validation message
-         */
-        invalid : true,
-        /**
-         * @event valid
-         * Fires after the field has been validated with no errors.
-         * @param {Roo.form.Field} this
-         */
-        valid : true,
-         /**
-         * @event keyup
-         * Fires after the key up
-         * @param {Roo.form.Field} this
-         * @param {Roo.EventObject}  e The event Object
-         */
-        keyup : true,
-        /**
-         * @event paste
-         * Fires after the user pastes into input
-         * @param {Roo.form.Field} this
-         * @param {Roo.EventObject}  e The event Object
-         */
-        paste : true
+        actioncomplete : true
     });
 };
 
-Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
-     /**
-     * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
-      automatic validation (defaults to "keyup").
-     */
-    validationEvent : "keyup",
-     /**
-     * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
-     */
-    validateOnBlur : true,
-    /**
-     * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
-     */
-    validationDelay : 250,
+Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
+
      /**
-     * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
-     */
-    focusClass : "x-form-focus",  // not needed???
-    
-       
-    /**
-     * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
+     * @cfg {String} method
+     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
      */
-    invalidClass : "has-warning",
-    
+    method : 'POST',
     /**
-     * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
+     * @cfg {String} url
+     * The URL to use for form actions if one isn't supplied in the action options.
      */
-    validClass : "has-success",
-    
     /**
-     * @cfg {Boolean} hasFeedback (true|false) default true
+     * @cfg {Boolean} fileUpload
+     * Set to true if this form is a file upload.
      */
-    hasFeedback : true,
-    
+
     /**
-     * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
+     * @cfg {Object} baseParams
+     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
      */
-    invalidFeedbackClass : "glyphicon-warning-sign",
-    
+
     /**
-     * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
+     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
      */
-    validFeedbackClass : "glyphicon-ok",
-    
+    timeout: 30,
     /**
-     * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
-     */
-    selectOnFocus : false,
-    
-     /**
-     * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
-     */
-    maskRe : null,
-       /**
-     * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
-     */
-    vtype : null,
-    
-      /**
-     * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
-     */
-    disableKeyFilter : false,
-    
-       /**
-     * @cfg {Boolean} disabled True to disable the field (defaults to false).
-     */
-    disabled : false,
-     /**
-     * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
+     * @cfg {Sting} align (left|right) for navbar forms
      */
-    allowBlank : true,
+    align : 'left',
+
+    // private
+    activeAction : null,
+
     /**
-     * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
+     * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
+     * element by passing it or its id or mask the form itself by passing in true.
+     * @type Mixed
      */
-    blankText : "Please complete this mandatory field",
+    waitMsgTarget : false,
+
+    loadMask : true,
     
-     /**
-     * @cfg {Number} minLength Minimum input field length required (defaults to 0)
-     */
-    minLength : 0,
-    /**
-     * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
-     */
-    maxLength : Number.MAX_VALUE,
-    /**
-     * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
-     */
-    minLengthText : "The minimum length for this field is {0}",
     /**
-     * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
+     * @cfg {Boolean} errorMask (true|false) default false
      */
-    maxLengthText : "The maximum length for this field is {0}",
-  
+    errorMask : false,
     
     /**
-     * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
-     * If available, this function will be called only after the basic validators all return true, and will be passed the
-     * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
-     */
-    validator : null,
-    /**
-     * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
-     * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
-     * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
-     */
-    regex : null,
-    /**
-     * @cfg {String} regexText -- Depricated - use Invalid Text
+     * @cfg {Number} maskOffset Default 100
      */
-    regexText : "",
+    maskOffset : 100,
     
     /**
-     * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
+     * @cfg {Boolean} maskBody
      */
-    invalidText : "",
-    
-    
-    
-    autocomplete: false,
-    
-    
-    fieldLabel : '',
-    inputType : 'text',
-    
-    name : false,
-    placeholder: false,
-    before : false,
-    after : false,
-    size : false,
-    hasFocus : false,
-    preventMark: false,
-    isFormField : true,
-    value : '',
-    labelWidth : 2,
-    labelAlign : false,
-    readOnly : false,
-    align : false,
-    formatedValue : false,
-    forceFeedback : false,
-    
-    indicatorpos : 'left',
-    
-    labellg : 0,
-    labelmd : 0,
-    labelsm : 0,
-    labelxs : 0,
-    
-    capture : '',
-    accept : '',
-    
-    parentLabelAlign : function()
-    {
-        var parent = this;
-        while (parent.parent()) {
-            parent = parent.parent();
-            if (typeof(parent.labelAlign) !='undefined') {
-                return parent.labelAlign;
-            }
+    maskBody : false,
+
+    getAutoCreate : function(){
+
+        var cfg = {
+            tag: 'form',
+            method : this.method || 'POST',
+            id : this.id || Roo.id(),
+            cls : ''
+        };
+        if (this.parent().xtype.match(/^Nav/)) {
+            cfg.cls = 'navbar-form form-inline navbar-' + this.align;
+
         }
-        return 'left';
-        
+
+        if (this.labelAlign == 'left' ) {
+            cfg.cls += ' form-horizontal';
+        }
+
+
+        return cfg;
     },
-    
-    getAutoCreate : function()
+    initEvents : function()
     {
-        var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+        this.el.on('submit', this.onSubmit, this);
+        // this was added as random key presses on the form where triggering form submit.
+        this.el.on('keypress', function(e) {
+            if (e.getCharCode() != 13) {
+                return true;
+            }
+            // we might need to allow it for textareas.. and some other items.
+            // check e.getTarget().
+
+            if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
+                return true;
+            }
+
+            Roo.log("keypress blocked");
+
+            e.preventDefault();
+            return false;
+        });
         
-        var id = Roo.id();
+    },
+    // private
+    onSubmit : function(e){
+        e.stopEvent();
+    },
+
+     /**
+     * Returns true if client-side validation on the form is successful.
+     * @return Boolean
+     */
+    isValid : function(){
+        var items = this.getItems();
+        var valid = true;
+        var target = false;
         
-        var cfg = {};
-        
-        if(this.inputType != 'hidden'){
-            cfg.cls = 'form-group' //input-group
-        }
-        
-        var input =  {
-            tag: 'input',
-            id : id,
-            type : this.inputType,
-            value : this.value,
-            cls : 'form-control',
-            placeholder : this.placeholder || '',
-            autocomplete : this.autocomplete || 'new-password'
-        };
-        if (this.inputType == 'file') {
-            input.style = 'overflow:hidden'; // why not in CSS?
-        }
-        
-        if(this.capture.length){
-            input.capture = this.capture;
-        }
-        
-        if(this.accept.length){
-            input.accept = this.accept + "/*";
-        }
-        
-        if(this.align){
-            input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
-        }
-        
-        if(this.maxLength && this.maxLength != Number.MAX_VALUE){
-            input.maxLength = this.maxLength;
-        }
-        
-        if (this.disabled) {
-            input.disabled=true;
-        }
+        items.each(function(f){
+            
+            if(f.validate()){
+                return;
+            }
+            
+            Roo.log('invalid field: ' + f.name);
+            
+            valid = false;
+
+            if(!target && f.el.isVisible(true)){
+                target = f;
+            }
+           
+        });
         
-        if (this.readOnly) {
-            input.readonly=true;
+        if(this.errorMask && !valid){
+            Roo.bootstrap.Form.popover.mask(this, target);
         }
         
-        if (this.name) {
-            input.name = this.name;
+        return valid;
+    },
+    
+    /**
+     * Returns true if any fields in this form have changed since their original load.
+     * @return Boolean
+     */
+    isDirty : function(){
+        var dirty = false;
+        var items = this.getItems();
+        items.each(function(f){
+           if(f.isDirty()){
+               dirty = true;
+               return false;
+           }
+           return true;
+        });
+        return dirty;
+    },
+     /**
+     * Performs a predefined action (submit or load) or custom actions you define on this form.
+     * @param {String} actionName The name of the action type
+     * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
+     * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
+     * accept other config options):
+     * <pre>
+Property          Type             Description
+----------------  ---------------  ----------------------------------------------------------------------------------
+url               String           The url for the action (defaults to the form's url)
+method            String           The form method to use (defaults to the form's method, or POST if not defined)
+params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
+clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
+                                   validate the form on the client (defaults to false)
+     * </pre>
+     * @return {BasicForm} this
+     */
+    doAction : function(action, options){
+        if(typeof action == 'string'){
+            action = new Roo.form.Action.ACTION_TYPES[action](this, options);
         }
-        
-        if (this.size) {
-            input.cls += ' input-' + this.size;
+        if(this.fireEvent('beforeaction', this, action) !== false){
+            this.beforeAction(action);
+            action.run.defer(100, action);
         }
+        return this;
+    },
+
+    // private
+    beforeAction : function(action){
+        var o = action.options;
         
-        var settings=this;
-        ['xs','sm','md','lg'].map(function(size){
-            if (settings[size]) {
-                cfg.cls += ' col-' + size + '-' + settings[size];
-            }
-        });
-        
-        var inputblock = input;
-        
-        var feedback = {
-            tag: 'span',
-            cls: 'glyphicon form-control-feedback'
-        };
+        if(this.loadMask){
             
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+            if(this.maskBody){
+                Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
+            } else {
+                this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
+            }
+        }
+        // not really supported yet.. ??
+
+        //if(this.waitMsgTarget === true){
+        //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
+        //}else if(this.waitMsgTarget){
+        //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
+        //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
+        //}else {
+        //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
+       // }
+
+    },
+
+    // private
+    afterAction : function(action, success){
+        this.activeAction = null;
+        var o = action.options;
+
+        if(this.loadMask){
             
-            inputblock = {
-                cls : 'has-feedback',
-                cn :  [
-                    input,
-                    feedback
-                ] 
-            };  
+            if(this.maskBody){
+                Roo.get(document.body).unmask();
+            } else {
+                this.el.unmask();
+            }
         }
         
-        if (this.before || this.after) {
-            
-            inputblock = {
-                cls : 'input-group',
-                cn :  [] 
-            };
-            
-            if (this.before && typeof(this.before) == 'string') {
-                
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
-                    html : this.before
-                });
+        //if(this.waitMsgTarget === true){
+//            this.el.unmask();
+        //}else if(this.waitMsgTarget){
+        //    this.waitMsgTarget.unmask();
+        //}else{
+        //    Roo.MessageBox.updateProgress(1);
+        //    Roo.MessageBox.hide();
+       // }
+        //
+        if(success){
+            if(o.reset){
+                this.reset();
             }
-            if (this.before && typeof(this.before) == 'object') {
-                this.before = Roo.factory(this.before);
-                
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'roo-input-before input-group-prepend   input-group-' +
-                        (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
-                });
+            Roo.callback(o.success, o.scope, [this, action]);
+            this.fireEvent('actioncomplete', this, action);
+
+        }else{
+
+            // failure condition..
+            // 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) != 'undefined')  &&
+                (typeof(action.result.errors) != 'undefined')  &&
+                (typeof(action.result.errors.needs_confirm) != 'undefined')
+           ){
+                var _t = this;
+                Roo.log("not supported yet");
+                 /*
+
+                Roo.MessageBox.confirm(
+                    "Change requires confirmation",
+                    action.result.errorMsg,
+                    function(r) {
+                        if (r != 'yes') {
+                            return;
+                        }
+                        _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
+                    }
+
+                );
+                */
+
+
+                return;
             }
-            
-            inputblock.cn.push(input);
-            
-            if (this.after && typeof(this.after) == 'string') {
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'roo-input-after input-group-append input-group-text input-group-addon',
-                    html : this.after
-                });
+
+            Roo.callback(o.failure, o.scope, [this, action]);
+            // show an error message if no failed handler is set..
+            if (!this.hasListener('actionfailed')) {
+                Roo.log("need to add dialog support");
+                /*
+                Roo.MessageBox.alert("Error",
+                    (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
+                        action.result.errorMsg :
+                        "Saving Failed, please check your entries or try again"
+                );
+                */
             }
-            if (this.after && typeof(this.after) == 'object') {
-                this.after = Roo.factory(this.after);
-                
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'roo-input-after input-group-append  input-group-' +
-                        (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
-                });
-            }
-            
-            if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-                inputblock.cls += ' has-feedback';
-                inputblock.cn.push(feedback);
-            }
-        };
-        var indicator = {
-            tag : 'i',
-            cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
-            tooltip : 'This field is required'
-        };
-        if (this.allowBlank ) {
-            indicator.style = this.allowBlank ? ' display:none' : '';
+
+            this.fireEvent('actionfailed', this, action);
         }
-        if (align ==='left' && this.fieldLabel.length) {
-            
-            cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
-            
-            cfg.cn = [
-                indicator,
-                {
-                    tag: 'label',
-                    'for' :  id,
-                    cls : 'control-label col-form-label',
-                    html : this.fieldLabel
 
-                },
-                {
-                    cls : "", 
-                    cn: [
-                        inputblock
-                    ]
+    },
+    /**
+     * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
+     * @param {String} id The value to search for
+     * @return Field
+     */
+    findField : function(id){
+        var items = this.getItems();
+        var field = items.get(id);
+        if(!field){
+             items.each(function(f){
+                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
+                    field = f;
+                    return false;
+                }
+                return true;
+            });
+        }
+        return field || null;
+    },
+     /**
+     * Mark fields in this form invalid in bulk.
+     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
+     * @return {BasicForm} this
+     */
+    markInvalid : function(errors){
+        if(errors instanceof Array){
+            for(var i = 0, len = errors.length; i < len; i++){
+                var fieldError = errors[i];
+                var f = this.findField(fieldError.id);
+                if(f){
+                    f.markInvalid(fieldError.msg);
                 }
-            ];
-            
-            var labelCfg = cfg.cn[1];
-            var contentCfg = cfg.cn[2];
-            
-            if(this.indicatorpos == 'right'){
-                cfg.cn = [
-                    {
-                        tag: 'label',
-                        'for' :  id,
-                        cls : 'control-label col-form-label',
-                        cn : [
-                            {
-                                tag : 'span',
-                                html : this.fieldLabel
-                            },
-                            indicator
-                        ]
-                    },
-                    {
-                        cls : "",
-                        cn: [
-                            inputblock
-                        ]
-                    }
-
-                ];
-                
-                labelCfg = cfg.cn[0];
-                contentCfg = cfg.cn[1];
-            
-            }
-            
-            if(this.labelWidth > 12){
-                labelCfg.style = "width: " + this.labelWidth + 'px';
-            }
-            
-            if(this.labelWidth < 13 && this.labelmd == 0){
-                this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
-            }
-            
-            if(this.labellg > 0){
-                labelCfg.cls += ' col-lg-' + this.labellg;
-                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
-            }
-            
-            if(this.labelmd > 0){
-                labelCfg.cls += ' col-md-' + this.labelmd;
-                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
-            }
-            
-            if(this.labelsm > 0){
-                labelCfg.cls += ' col-sm-' + this.labelsm;
-                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
             }
-            
-            if(this.labelxs > 0){
-                labelCfg.cls += ' col-xs-' + this.labelxs;
-                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+        }else{
+            var field, id;
+            for(id in errors){
+                if(typeof errors[id] != 'function' && (field = this.findField(id))){
+                    field.markInvalid(errors[id]);
+                }
             }
-            
-            
-        } else if ( this.fieldLabel.length) {
-                
-            
-            
-            cfg.cn = [
-                {
-                    tag : 'i',
-                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
-                    tooltip : 'This field is required',
-                    style : this.allowBlank ? ' display:none' : '' 
-                },
-                {
-                    tag: 'label',
-                   //cls : 'input-group-addon',
-                    html : this.fieldLabel
-
-                },
+        }
+        //Roo.each(this.childForms || [], function (f) {
+        //    f.markInvalid(errors);
+        //});
 
-               inputblock
+        return this;
+    },
 
-           ];
-           
-           if(this.indicatorpos == 'right'){
-       
-                cfg.cn = [
-                    {
-                        tag: 'label',
-                       //cls : 'input-group-addon',
-                        html : this.fieldLabel
+    /**
+     * Set values for fields in this form in bulk.
+     * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
+     * @return {BasicForm} this
+     */
+    setValues : function(values){
+        if(values instanceof Array){ // array of objects
+            for(var i = 0, len = values.length; i < len; i++){
+                var v = values[i];
+                var f = this.findField(v.id);
+                if(f){
+                    f.setValue(v.value);
+                    if(this.trackResetOnLoad){
+                        f.originalValue = f.getValue();
+                    }
+                }
+            }
+        }else{ // object hash
+            var field, id;
+            for(id in values){
+                if(typeof values[id] != 'function' && (field = this.findField(id))){
 
-                    },
-                    {
-                        tag : 'i',
-                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
-                        tooltip : 'This field is required',
-                        style : this.allowBlank ? ' display:none' : '' 
-                    },
+                    if (field.setFromData &&
+                        field.valueField &&
+                        field.displayField &&
+                        // combos' with local stores can
+                        // be queried via setValue()
+                        // to set their value..
+                        (field.store && !field.store.isLocal)
+                        ) {
+                        // it's a combo
+                        var sd = { };
+                        sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
+                        sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
+                        field.setFromData(sd);
 
-                   inputblock
+                    } else if(field.setFromData && (field.store && !field.store.isLocal)) {
+                        
+                        field.setFromData(values);
+                        
+                    } else {
+                        field.setValue(values[id]);
+                    }
 
-               ];
 
+                    if(this.trackResetOnLoad){
+                        field.originalValue = field.getValue();
+                    }
+                }
             }
+        }
 
-        } else {
-            
-            cfg.cn = [
-
-                    inputblock
+        //Roo.each(this.childForms || [], function (f) {
+        //    f.setValues(values);
+        //});
 
-            ];
-                
-                
-        };
-        
-        if (this.parentType === 'Navbar' &&  this.parent().bar) {
-           cfg.cls += ' navbar-form';
-        }
-        
-        if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
-            // on BS4 we do this only if not form 
-            cfg.cls += ' navbar-form';
-            cfg.tag = 'li';
-        }
-        
-        return cfg;
-        
+        return this;
     },
+
     /**
-     * return the real input element.
+     * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
+     * they are returned as an array.
+     * @param {Boolean} asString
+     * @return {Object}
      */
-    inputEl: function ()
-    {
-        return this.el.select('input.form-control',true).first();
-    },
-    
-    tooltipEl : function()
+    getValues : function(asString){
+        //if (this.childForms) {
+            // copy values from the child forms
+        //    Roo.each(this.childForms, function (f) {
+        //        this.setValues(f.getValues());
+        //    }, this);
+        //}
+
+
+
+        var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
+        if(asString === true){
+            return fs;
+        }
+        return Roo.urlDecode(fs);
+    },
+
+    /**
+     * Returns the fields in this form as an object with key/value pairs.
+     * This differs from getValues as it calls getValue on each child item, rather than using dom data.
+     * @return {Object}
+     */
+    getFieldValues : function(with_hidden)
     {
-        return this.inputEl();
+        var items = this.getItems();
+        var ret = {};
+        items.each(function(f){
+            
+            if (!f.getName()) {
+                return;
+            }
+            
+            var v = f.getValue();
+            
+            if (f.inputType =='radio') {
+                if (typeof(ret[f.getName()]) == 'undefined') {
+                    ret[f.getName()] = ''; // empty..
+                }
+
+                if (!f.el.dom.checked) {
+                    return;
+
+                }
+                v = f.el.dom.value;
+
+            }
+            
+            if(f.xtype == 'MoneyField'){
+                ret[f.currencyName] = f.getCurrency();
+            }
+
+            // not sure if this supported any more..
+            if ((typeof(v) == 'object') && f.getRawValue) {
+                v = f.getRawValue() ; // dates..
+            }
+            // combo boxes where name != hiddenName...
+            if (f.name !== false && f.name != '' && f.name != f.getName()) {
+                ret[f.name] = f.getRawValue();
+            }
+            ret[f.getName()] = v;
+        });
+
+        return ret;
+    },
+
+    /**
+     * Clears all invalid messages in this form.
+     * @return {BasicForm} this
+     */
+    clearInvalid : function(){
+        var items = this.getItems();
+
+        items.each(function(f){
+           f.clearInvalid();
+        });
+
+        return this;
+    },
+
+    /**
+     * Resets this form.
+     * @return {BasicForm} this
+     */
+    reset : function(){
+        var items = this.getItems();
+        items.each(function(f){
+            f.reset();
+        });
+
+        Roo.each(this.childForms || [], function (f) {
+            f.reset();
+        });
+
+
+        return this;
     },
     
-    indicatorEl : function()
+    getItems : function()
     {
-        if (Roo.bootstrap.version == 4) {
-            return false; // not enabled in v4 yet.
-        }
-        
-        var indicator = this.el.select('i.roo-required-indicator',true).first();
-        
-        if(!indicator){
-            return false;
-        }
-        
-        return indicator;
-        
+        var r=new Roo.util.MixedCollection(false, function(o){
+            return o.id || (o.id = Roo.id());
+        });
+        var iter = function(el) {
+            if (el.inputEl) {
+                r.add(el);
+            }
+            if (!el.items) {
+                return;
+            }
+            Roo.each(el.items,function(e) {
+                iter(e);
+            });
+        };
+
+        iter(this);
+        return r;
     },
     
-    setDisabled : function(v)
+    hideFields : function(items)
     {
-        var i  = this.inputEl().dom;
-        if (!v) {
-            i.removeAttribute('disabled');
-            return;
+        Roo.each(items, function(i){
             
-        }
-        i.setAttribute('disabled','true');
+            var f = this.findField(i);
+            
+            if(!f){
+                return;
+            }
+            
+            f.hide();
+            
+        }, this);
     },
-    initEvents : function()
+    
+    showFields : function(items)
     {
-          
-        this.inputEl().on("keydown" , this.fireKey,  this);
-        this.inputEl().on("focus", this.onFocus,  this);
-        this.inputEl().on("blur", this.onBlur,  this);
+        Roo.each(items, function(i){
+            
+            var f = this.findField(i);
+            
+            if(!f){
+                return;
+            }
+            
+            f.show();
+            
+        }, this);
+    }
+
+});
+
+Roo.apply(Roo.bootstrap.Form, {
+    
+    popover : {
         
-        this.inputEl().relayEvent('keyup', this);
-        this.inputEl().relayEvent('paste', this);
+        padding : 5,
         
-        this.indicator = this.indicatorEl();
+        isApplied : false,
         
-        if(this.indicator){
-            this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
-        }
-        // reference to original value for reset
-        this.originalValue = this.getValue();
-        //Roo.form.TextField.superclass.initEvents.call(this);
-        if(this.validationEvent == 'keyup'){
-            this.validationTask = new Roo.util.DelayedTask(this.validate, this);
-            this.inputEl().on('keyup', this.filterValidation, this);
-        }
-        else if(this.validationEvent !== false){
-            this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
-        }
+        isMasked : false,
         
-        if(this.selectOnFocus){
-            this.on("focus", this.preFocus, this);
-            
-        }
-        if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
-            this.inputEl().on("keypress", this.filterKeys, this);
-        } else {
-            this.inputEl().relayEvent('keypress', this);
-        }
-       /* if(this.grow){
-            this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
-            this.el.on("click", this.autoSize,  this);
-        }
-        */
-        if(this.inputEl().is('input[type=password]') && Roo.isSafari){
-            this.inputEl().on('keydown', this.SafariOnKeyDown, this);
-        }
+        form : false,
         
-        if (typeof(this.before) == 'object') {
-            this.before.render(this.el.select('.roo-input-before',true).first());
-        }
-        if (typeof(this.after) == 'object') {
-            this.after.render(this.el.select('.roo-input-after',true).first());
-        }
+        target : false,
         
-        this.inputEl().on('change', this.onChange, this);
+        toolTip : false,
         
-    },
-    filterValidation : function(e){
-        if(!e.isNavKeyPress()){
-            this.validationTask.delay(this.validationDelay);
-        }
-    },
-     /**
-     * Validates the field value
-     * @return {Boolean} True if the value is valid, else false
-     */
-    validate : function(){
-        //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
-        if(this.disabled || this.validateValue(this.getRawValue())){
-            this.markValid();
-            return true;
-        }
+        intervalID : false,
         
-        this.markInvalid();
-        return false;
-    },
-    
-    
-    /**
-     * Validates a value according to the field's validation rules and marks the field as invalid
-     * if the validation fails
-     * @param {Mixed} value The value to validate
-     * @return {Boolean} True if the value is valid, else false
-     */
-    validateValue : function(value)
-    {
-        if(this.getVisibilityEl().hasClass('hidden')){
-            return true;
-        }
+        maskEl : false,
         
-        if(value.length < 1)  { // if it's blank
-            if(this.allowBlank){
-                return true;
-            }
-            return false;
-        }
-        
-        if(value.length < this.minLength){
-            return false;
-        }
-        if(value.length > this.maxLength){
-            return false;
-        }
-        if(this.vtype){
-            var vt = Roo.form.VTypes;
-            if(!vt[this.vtype](value, this)){
-                return false;
+        apply : function()
+        {
+            if(this.isApplied){
+                return;
             }
-        }
-        if(typeof this.validator == "function"){
-            var msg = this.validator(value);
-            if(msg !== true){
-                return false;
+            
+            this.maskEl = {
+                top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
+                left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
+                bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
+                right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
+            };
+            
+            this.maskEl.top.enableDisplayMode("block");
+            this.maskEl.left.enableDisplayMode("block");
+            this.maskEl.bottom.enableDisplayMode("block");
+            this.maskEl.right.enableDisplayMode("block");
+            
+            this.toolTip = new Roo.bootstrap.Tooltip({
+                cls : 'roo-form-error-popover',
+                alignment : {
+                    'left' : ['r-l', [-2,0], 'right'],
+                    'right' : ['l-r', [2,0], 'left'],
+                    'bottom' : ['tl-bl', [0,2], 'top'],
+                    'top' : [ 'bl-tl', [0,-2], 'bottom']
+                }
+            });
+            
+            this.toolTip.render(Roo.get(document.body));
+
+            this.toolTip.el.enableDisplayMode("block");
+            
+            Roo.get(document.body).on('click', function(){
+                this.unmask();
+            }, this);
+            
+            Roo.get(document.body).on('touchstart', function(){
+                this.unmask();
+            }, this);
+            
+            this.isApplied = true
+        },
+        
+        mask : function(form, target)
+        {
+            this.form = form;
+            
+            this.target = target;
+            
+            if(!this.form.errorMask || !target.el){
+                return;
             }
-            if (typeof(msg) == 'string') {
-                this.invalidText = msg;
+            
+            var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
+            
+            Roo.log(scrollable);
+            
+            var ot = this.target.el.calcOffsetsTo(scrollable);
+            
+            var scrollTo = ot[1] - this.form.maskOffset;
+            
+            scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
+            
+            scrollable.scrollTo('top', scrollTo);
+            
+            var box = this.target.el.getBox();
+            Roo.log(box);
+            var zIndex = Roo.bootstrap.Modal.zIndex++;
+
+            
+            this.maskEl.top.setStyle('position', 'absolute');
+            this.maskEl.top.setStyle('z-index', zIndex);
+            this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
+            this.maskEl.top.setLeft(0);
+            this.maskEl.top.setTop(0);
+            this.maskEl.top.show();
+            
+            this.maskEl.left.setStyle('position', 'absolute');
+            this.maskEl.left.setStyle('z-index', zIndex);
+            this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
+            this.maskEl.left.setLeft(0);
+            this.maskEl.left.setTop(box.y - this.padding);
+            this.maskEl.left.show();
+
+            this.maskEl.bottom.setStyle('position', 'absolute');
+            this.maskEl.bottom.setStyle('z-index', zIndex);
+            this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
+            this.maskEl.bottom.setLeft(0);
+            this.maskEl.bottom.setTop(box.bottom + this.padding);
+            this.maskEl.bottom.show();
+
+            this.maskEl.right.setStyle('position', 'absolute');
+            this.maskEl.right.setStyle('z-index', zIndex);
+            this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
+            this.maskEl.right.setLeft(box.right + this.padding);
+            this.maskEl.right.setTop(box.y - this.padding);
+            this.maskEl.right.show();
+
+            this.toolTip.bindEl = this.target.el;
+
+            this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+
+            var tip = this.target.blankText;
+
+            if(this.target.getValue() !== '' ) {
+                
+                if (this.target.invalidText.length) {
+                    tip = this.target.invalidText;
+                } else if (this.target.regexText.length){
+                    tip = this.target.regexText;
+                }
             }
-        }
-        
-        if(this.regex && !this.regex.test(value)){
-            return false;
-        }
+
+            this.toolTip.show(tip);
+
+            this.intervalID = window.setInterval(function() {
+                Roo.bootstrap.Form.popover.unmask();
+            }, 10000);
+
+            window.onwheel = function(){ return false;};
+            
+            (function(){ this.isMasked = true; }).defer(500, this);
+            
+        },
         
-        return true;
-    },
-    
-     // private
-    fireKey : function(e){
-        //Roo.log('field ' + e.getKey());
-        if(e.isNavKeyPress()){
-            this.fireEvent("specialkey", this, e);
-        }
-    },
-    focus : function (selectText){
-        if(this.rendered){
-            this.inputEl().focus();
-            if(selectText === true){
-                this.inputEl().dom.select();
+        unmask : function()
+        {
+            if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
+                return;
             }
-        }
-        return this;
-    } ,
-    
-    onFocus : function(){
-        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
-           // this.el.addClass(this.focusClass);
-        }
-        if(!this.hasFocus){
-            this.hasFocus = true;
-            this.startValue = this.getValue();
-            this.fireEvent("focus", this);
-        }
-    },
-    
-    beforeBlur : Roo.emptyFn,
+            
+            this.maskEl.top.setStyle('position', 'absolute');
+            this.maskEl.top.setSize(0, 0).setXY([0, 0]);
+            this.maskEl.top.hide();
 
-    
-    // private
-    onBlur : function(){
-        this.beforeBlur();
-        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
-            //this.el.removeClass(this.focusClass);
-        }
-        this.hasFocus = false;
-        if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
-            this.validate();
-        }
-        var v = this.getValue();
-        if(String(v) !== String(this.startValue)){
-            this.fireEvent('change', this, v, this.startValue);
-        }
-        this.fireEvent("blur", this);
-    },
-    
-    onChange : function(e)
-    {
-        var v = this.getValue();
-        if(String(v) !== String(this.startValue)){
-            this.fireEvent('change', this, v, this.startValue);
+            this.maskEl.left.setStyle('position', 'absolute');
+            this.maskEl.left.setSize(0, 0).setXY([0, 0]);
+            this.maskEl.left.hide();
+
+            this.maskEl.bottom.setStyle('position', 'absolute');
+            this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
+            this.maskEl.bottom.hide();
+
+            this.maskEl.right.setStyle('position', 'absolute');
+            this.maskEl.right.setSize(0, 0).setXY([0, 0]);
+            this.maskEl.right.hide();
+            
+            this.toolTip.hide();
+            
+            this.toolTip.el.hide();
+            
+            window.onwheel = function(){ return true;};
+            
+            if(this.intervalID){
+                window.clearInterval(this.intervalID);
+                this.intervalID = false;
+            }
+            
+            this.isMasked = false;
+            
         }
         
-    },
+    }
     
-    /**
-     * Resets the current field value to the originally loaded value and clears any validation messages
-     */
-    reset : function(){
-        this.setValue(this.originalValue);
-        this.validate();
-    },
-     /**
-     * Returns the name of the field
-     * @return {Mixed} name The name field
-     */
-    getName: function(){
-        return this.name;
-    },
-     /**
-     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
-     * @return {Mixed} value The field value
-     */
-    getValue : function(){
-        
-        var v = this.inputEl().getValue();
+});
+
+/*
+ * 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.VTypes
+ * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
+ * @singleton
+ */
+Roo.form.VTypes = function(){
+    // closure these in so they are only created once.
+    var alpha = /^[a-zA-Z_]+$/;
+    var alphanum = /^[a-zA-Z0-9_]+$/;
+    var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
+    var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
+
+    // All these messages and functions are configurable
+    return {
+        /**
+         * The function used to validate email addresses
+         * @param {String} value The email address
+         */
+        'email' : function(v){
+            return email.test(v);
+        },
+        /**
+         * The error text to display when the email validation function returns false
+         * @type String
+         */
+        'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
+        /**
+         * The keystroke filter mask to be applied on email input
+         * @type RegExp
+         */
+        'emailMask' : /[a-z0-9_\.\-@]/i,
+
+        /**
+         * The function used to validate URLs
+         * @param {String} value The URL
+         */
+        'url' : function(v){
+            return url.test(v);
+        },
+        /**
+         * The error text to display when the url validation function returns false
+         * @type String
+         */
+        'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
         
-        return v;
-    },
+        /**
+         * The function used to validate alpha values
+         * @param {String} value The value
+         */
+        'alpha' : function(v){
+            return alpha.test(v);
+        },
+        /**
+         * The error text to display when the alpha validation function returns false
+         * @type String
+         */
+        'alphaText' : 'This field should only contain letters and _',
+        /**
+         * The keystroke filter mask to be applied on alpha input
+         * @type RegExp
+         */
+        'alphaMask' : /[a-z_]/i,
+
+        /**
+         * The function used to validate alphanumeric values
+         * @param {String} value The value
+         */
+        'alphanum' : function(v){
+            return alphanum.test(v);
+        },
+        /**
+         * The error text to display when the alphanumeric validation function returns false
+         * @type String
+         */
+        'alphanumText' : 'This field should only contain letters, numbers and _',
+        /**
+         * The keystroke filter mask to be applied on alphanumeric input
+         * @type RegExp
+         */
+        'alphanumMask' : /[a-z0-9_]/i
+    };
+}();/*
+ * - LGPL
+ *
+ * Input
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.Input
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Input class
+ * @cfg {Boolean} disabled is it disabled
+ * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
+ * @cfg {String} name name of the input
+ * @cfg {string} fieldLabel - the label associated
+ * @cfg {string} placeholder - placeholder to put in text.
+ * @cfg {string}  before - input group add on before
+ * @cfg {string} after - input group add on after
+ * @cfg {string} size - (lg|sm) or leave empty..
+ * @cfg {Number} xs colspan out of 12 for mobile-sized screens
+ * @cfg {Number} sm colspan out of 12 for tablet-sized screens
+ * @cfg {Number} md colspan out of 12 for computer-sized screens
+ * @cfg {Number} lg colspan out of 12 for large computer-sized screens
+ * @cfg {string} value default value of the input
+ * @cfg {Number} labelWidth set the width of label 
+ * @cfg {Number} labellg set the width of label (1-12)
+ * @cfg {Number} labelmd set the width of label (1-12)
+ * @cfg {Number} labelsm set the width of label (1-12)
+ * @cfg {Number} labelxs set the width of label (1-12)
+ * @cfg {String} labelAlign (top|left)
+ * @cfg {Boolean} readOnly Specifies that the field should be read-only
+ * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
+ * @cfg {String} indicatorpos (left|right) default left
+ * @cfg {String} capture (user|camera) use for file input only. (default empty)
+ * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
+ * @cfg {Boolean} preventMark Do not show tick or cross if error/success
+
+ * @cfg {String} align (left|center|right) Default left
+ * @cfg {Boolean} forceFeedback (true|false) Default false
+ * 
+ * @constructor
+ * Create a new Input
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Input = function(config){
+    
+    Roo.bootstrap.Input.superclass.constructor.call(this, config);
+    
+    this.addEvents({
+        /**
+         * @event focus
+         * Fires when this field receives input focus.
+         * @param {Roo.form.Field} this
+         */
+        focus : true,
+        /**
+         * @event blur
+         * Fires when this field loses input focus.
+         * @param {Roo.form.Field} this
+         */
+        blur : true,
+        /**
+         * @event specialkey
+         * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
+         * {@link Roo.EventObject#getKey} to determine which key was pressed.
+         * @param {Roo.form.Field} this
+         * @param {Roo.EventObject} e The event object
+         */
+        specialkey : true,
+        /**
+         * @event change
+         * Fires just before the field blurs if the field value has changed.
+         * @param {Roo.form.Field} this
+         * @param {Mixed} newValue The new value
+         * @param {Mixed} oldValue The original value
+         */
+        change : true,
+        /**
+         * @event invalid
+         * Fires after the field has been marked as invalid.
+         * @param {Roo.form.Field} this
+         * @param {String} msg The validation message
+         */
+        invalid : true,
+        /**
+         * @event valid
+         * Fires after the field has been validated with no errors.
+         * @param {Roo.form.Field} this
+         */
+        valid : true,
+         /**
+         * @event keyup
+         * Fires after the key up
+         * @param {Roo.form.Field} this
+         * @param {Roo.EventObject}  e The event Object
+         */
+        keyup : true,
+        /**
+         * @event paste
+         * Fires after the user pastes into input
+         * @param {Roo.form.Field} this
+         * @param {Roo.EventObject}  e The event Object
+         */
+        paste : true
+    });
+};
+
+Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
+     /**
+     * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
+      automatic validation (defaults to "keyup").
+     */
+    validationEvent : "keyup",
+     /**
+     * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
+     */
+    validateOnBlur : true,
     /**
-     * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
-     * @return {Mixed} value The field value
+     * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
      */
-    getRawValue : function(){
-        var v = this.inputEl().getValue();
-        
-        return v;
-    },
+    validationDelay : 250,
+     /**
+     * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
+     */
+    focusClass : "x-form-focus",  // not needed???
     
+       
     /**
-     * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
-     * @param {Mixed} value The value to set
+     * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
      */
-    setRawValue : function(v){
-        return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
-    },
+    invalidClass : "has-warning",
     
-    selectText : function(start, end){
-        var v = this.getRawValue();
-        if(v.length > 0){
-            start = start === undefined ? 0 : start;
-            end = end === undefined ? v.length : end;
-            var d = this.inputEl().dom;
-            if(d.setSelectionRange){
-                d.setSelectionRange(start, end);
-            }else if(d.createTextRange){
-                var range = d.createTextRange();
-                range.moveStart("character", start);
-                range.moveEnd("character", v.length-end);
-                range.select();
-            }
-        }
-    },
+    /**
+     * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
+     */
+    validClass : "has-success",
     
     /**
-     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
-     * @param {Mixed} value The value to set
+     * @cfg {Boolean} hasFeedback (true|false) default true
      */
-    setValue : function(v){
-        this.value = v;
-        if(this.rendered){
-            this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
-            this.validate();
-        }
-    },
+    hasFeedback : true,
     
-    /*
-    processValue : function(value){
-        if(this.stripCharsRe){
-            var newValue = value.replace(this.stripCharsRe, '');
-            if(newValue !== value){
-                this.setRawValue(newValue);
-                return newValue;
+    /**
+     * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
+     */
+    invalidFeedbackClass : "glyphicon-warning-sign",
+    
+    /**
+     * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
+     */
+    validFeedbackClass : "glyphicon-ok",
+    
+    /**
+     * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
+     */
+    selectOnFocus : false,
+    
+     /**
+     * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
+     */
+    maskRe : null,
+       /**
+     * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
+     */
+    vtype : null,
+    
+      /**
+     * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
+     */
+    disableKeyFilter : false,
+    
+       /**
+     * @cfg {Boolean} disabled True to disable the field (defaults to false).
+     */
+    disabled : false,
+     /**
+     * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
+     */
+    allowBlank : true,
+    /**
+     * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
+     */
+    blankText : "Please complete this mandatory field",
+    
+     /**
+     * @cfg {Number} minLength Minimum input field length required (defaults to 0)
+     */
+    minLength : 0,
+    /**
+     * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
+     */
+    maxLength : Number.MAX_VALUE,
+    /**
+     * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
+     */
+    minLengthText : "The minimum length for this field is {0}",
+    /**
+     * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
+     */
+    maxLengthText : "The maximum length for this field is {0}",
+  
+    
+    /**
+     * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
+     * If available, this function will be called only after the basic validators all return true, and will be passed the
+     * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
+     */
+    validator : null,
+    /**
+     * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
+     * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
+     * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
+     */
+    regex : null,
+    /**
+     * @cfg {String} regexText -- Depricated - use Invalid Text
+     */
+    regexText : "",
+    
+    /**
+     * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
+     */
+    invalidText : "",
+    
+    
+    
+    autocomplete: false,
+    
+    
+    fieldLabel : '',
+    inputType : 'text',
+    
+    name : false,
+    placeholder: false,
+    before : false,
+    after : false,
+    size : false,
+    hasFocus : false,
+    preventMark: false,
+    isFormField : true,
+    value : '',
+    labelWidth : 2,
+    labelAlign : false,
+    readOnly : false,
+    align : false,
+    formatedValue : false,
+    forceFeedback : false,
+    
+    indicatorpos : 'left',
+    
+    labellg : 0,
+    labelmd : 0,
+    labelsm : 0,
+    labelxs : 0,
+    
+    capture : '',
+    accept : '',
+    
+    parentLabelAlign : function()
+    {
+        var parent = this;
+        while (parent.parent()) {
+            parent = parent.parent();
+            if (typeof(parent.labelAlign) !='undefined') {
+                return parent.labelAlign;
             }
         }
-        return value;
-    },
-  */
-    preFocus : function(){
+        return 'left';
         
-        if(this.selectOnFocus){
-            this.inputEl().dom.select();
-        }
-    },
-    filterKeys : function(e){
-        var k = e.getKey();
-        if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
-            return;
-        }
-        var c = e.getCharCode(), cc = String.fromCharCode(c);
-        if(Roo.isIE && (e.isSpecialKey() || !cc)){
-            return;
-        }
-        if(!this.maskRe.test(cc)){
-            e.stopEvent();
-        }
     },
-     /**
-     * Clear any invalid styles/messages for this field
-     */
-    clearInvalid : function(){
+    
+    getAutoCreate : function()
+    {
+        var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
         
-        if(!this.el || this.preventMark){ // not rendered
-            return;
-        }
+        var id = Roo.id();
         
+        var cfg = {};
         
-        this.el.removeClass([this.invalidClass, 'is-invalid']);
+        if(this.inputType != 'hidden'){
+            cfg.cls = 'form-group' //input-group
+        }
         
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-            
-            var feedback = this.el.select('.form-control-feedback', true).first();
-            
-            if(feedback){
-                this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
-            }
-            
+        var input =  {
+            tag: 'input',
+            id : id,
+            type : this.inputType,
+            value : this.value,
+            cls : 'form-control',
+            placeholder : this.placeholder || '',
+            autocomplete : this.autocomplete || 'new-password'
+        };
+        if (this.inputType == 'file') {
+            input.style = 'overflow:hidden'; // why not in CSS?
         }
         
-        if(this.indicator){
-            this.indicator.removeClass('visible');
-            this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+        if(this.capture.length){
+            input.capture = this.capture;
         }
         
-        this.fireEvent('valid', this);
-    },
-    
-     /**
-     * Mark this field as valid
-     */
-    markValid : function()
-    {
-        if(!this.el  || this.preventMark){ // not rendered...
-            return;
+        if(this.accept.length){
+            input.accept = this.accept + "/*";
         }
         
-        this.el.removeClass([this.invalidClass, this.validClass]);
-        this.inputEl().removeClass(['is-valid', 'is-invalid']);
-
-        var feedback = this.el.select('.form-control-feedback', true).first();
-            
-        if(feedback){
-            this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+        if(this.align){
+            input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
         }
         
-        if(this.indicator){
-            this.indicator.removeClass('visible');
-            this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+        if(this.maxLength && this.maxLength != Number.MAX_VALUE){
+            input.maxLength = this.maxLength;
         }
         
-        if(this.disabled){
-            return;
+        if (this.disabled) {
+            input.disabled=true;
         }
         
-           
-        if(this.allowBlank && !this.getRawValue().length){
-            return;
+        if (this.readOnly) {
+            input.readonly=true;
         }
-        if (Roo.bootstrap.version == 3) {
-            this.el.addClass(this.validClass);
-        } else {
-            this.inputEl().addClass('is-valid');
+        
+        if (this.name) {
+            input.name = this.name;
         }
-
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
-            
-            var feedback = this.el.select('.form-control-feedback', true).first();
-            
-            if(feedback){
-                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
-                this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
-            }
-            
-        }
-        
-        this.fireEvent('valid', this);
-    },
-    
-     /**
-     * Mark this field as invalid
-     * @param {String} msg The validation message
-     */
-    markInvalid : function(msg)
-    {
-        if(!this.el  || this.preventMark){ // not rendered
-            return;
-        }
-        
-        this.el.removeClass([this.invalidClass, this.validClass]);
-        this.inputEl().removeClass(['is-valid', 'is-invalid']);
-        
-        var feedback = this.el.select('.form-control-feedback', true).first();
-            
-        if(feedback){
-            this.el.select('.form-control-feedback', true).first().removeClass(
-                    [this.invalidFeedbackClass, this.validFeedbackClass]);
-        }
-
-        if(this.disabled){
-            return;
-        }
-        
-        if(this.allowBlank && !this.getRawValue().length){
-            return;
-        }
-        
-        if(this.indicator){
-            this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
-            this.indicator.addClass('visible');
-        }
-        if (Roo.bootstrap.version == 3) {
-            this.el.addClass(this.invalidClass);
-        } else {
-            this.inputEl().addClass('is-invalid');
-        }
-        
-        
-        
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-            
-            var feedback = this.el.select('.form-control-feedback', true).first();
-            
-            if(feedback){
-                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
-                
-                if(this.getValue().length || this.forceFeedback){
-                    this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
-                }
-                
-            }
-            
-        }
-        
-        this.fireEvent('invalid', this, msg);
-    },
-    // private
-    SafariOnKeyDown : function(event)
-    {
-        // this is a workaround for a password hang bug on chrome/ webkit.
-        if (this.inputEl().dom.type != 'password') {
-            return;
-        }
-        
-        var isSelectAll = false;
-        
-        if(this.inputEl().dom.selectionEnd > 0){
-            isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
-        }
-        if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
-            event.preventDefault();
-            this.setValue('');
-            return;
-        }
-        
-        if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
-            
-            event.preventDefault();
-            // this is very hacky as keydown always get's upper case.
-            //
-            var cc = String.fromCharCode(event.getCharCode());
-            this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
-            
-        }
-    },
-    adjustWidth : function(tag, w){
-        tag = tag.toLowerCase();
-        if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
-            if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
-                if(tag == 'input'){
-                    return w + 2;
-                }
-                if(tag == 'textarea'){
-                    return w-2;
-                }
-            }else if(Roo.isOpera){
-                if(tag == 'input'){
-                    return w + 2;
-                }
-                if(tag == 'textarea'){
-                    return w-2;
-                }
-            }
-        }
-        return w;
-    },
-    
-    setFieldLabel : function(v)
-    {
-        if(!this.rendered){
-            return;
-        }
-        
-        if(this.indicatorEl()){
-            var ar = this.el.select('label > span',true);
-            
-            if (ar.elements.length) {
-                this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
-                this.fieldLabel = v;
-                return;
-            }
-            
-            var br = this.el.select('label',true);
-            
-            if(br.elements.length) {
-                this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
-                this.fieldLabel = v;
-                return;
-            }
-            
-            Roo.log('Cannot Found any of label > span || label in input');
-            return;
-        }
-        
-        this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
-        this.fieldLabel = v;
-        
-        
-    }
-});
-
-/*
- * - LGPL
- *
- * Input
- * 
- */
-
-/**
- * @class Roo.bootstrap.TextArea
- * @extends Roo.bootstrap.Input
- * Bootstrap TextArea class
- * @cfg {Number} cols Specifies the visible width of a text area
- * @cfg {Number} rows Specifies the visible number of lines in a text area
- * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
- * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
- * @cfg {string} html text
- * 
- * @constructor
- * Create a new TextArea
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TextArea = function(config){
-    Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
-   
-};
-
-Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
-     
-    cols : false,
-    rows : 5,
-    readOnly : false,
-    warp : 'soft',
-    resize : false,
-    value: false,
-    html: false,
-    
-    getAutoCreate : function(){
-        
-        var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-        
-        var id = Roo.id();
-        
-        var cfg = {};
-        
-        if(this.inputType != 'hidden'){
-            cfg.cls = 'form-group' //input-group
-        }
-        
-        var input =  {
-            tag: 'textarea',
-            id : id,
-            warp : this.warp,
-            rows : this.rows,
-            value : this.value || '',
-            html: this.html || '',
-            cls : 'form-control',
-            placeholder : this.placeholder || '' 
-            
-        };
-        
-        if(this.maxLength && this.maxLength != Number.MAX_VALUE){
-            input.maxLength = this.maxLength;
-        }
-        
-        if(this.resize){
-            input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
-        }
-        
-        if(this.cols){
-            input.cols = this.cols;
-        }
-        
-        if (this.readOnly) {
-            input.readonly = true;
-        }
-        
-        if (this.name) {
-            input.name = this.name;
-        }
-        
-        if (this.size) {
-            input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
+        
+        if (this.size) {
+            input.cls += ' input-' + this.size;
         }
         
         var settings=this;
@@ -11950,13 +12114,13 @@ Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
         
         var inputblock = input;
         
-        if(this.hasFeedback && !this.allowBlank){
+        var feedback = {
+            tag: 'span',
+            cls: 'glyphicon form-control-feedback'
+        };
+            
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
             
-            var feedback = {
-                tag: 'span',
-                cls: 'glyphicon form-control-feedback'
-            };
-
             inputblock = {
                 cls : 'has-feedback',
                 cn :  [
@@ -11966,2686 +12130,2118 @@ Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
             };  
         }
         
-        
         if (this.before || this.after) {
             
             inputblock = {
                 cls : 'input-group',
                 cn :  [] 
             };
-            if (this.before) {
+            
+            if (this.before && typeof(this.before) == 'string') {
+                
                 inputblock.cn.push({
                     tag :'span',
-                    cls : 'input-group-addon',
+                    cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
                     html : this.before
                 });
             }
+            if (this.before && typeof(this.before) == 'object') {
+                this.before = Roo.factory(this.before);
+                
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'roo-input-before input-group-prepend   input-group-' +
+                        (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
+                });
+            }
             
-            inputblock.cn.push(input);
-            
-            if(this.hasFeedback && !this.allowBlank){
-                inputblock.cls += ' has-feedback';
-                inputblock.cn.push(feedback);
-            }
-            
-            if (this.after) {
+            inputblock.cn.push(input);
+            
+            if (this.after && typeof(this.after) == 'string') {
                 inputblock.cn.push({
                     tag :'span',
-                    cls : 'input-group-addon',
+                    cls : 'roo-input-after input-group-append input-group-text input-group-addon',
                     html : this.after
                 });
             }
+            if (this.after && typeof(this.after) == 'object') {
+                this.after = Roo.factory(this.after);
+                
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'roo-input-after input-group-append  input-group-' +
+                        (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
+                });
+            }
             
+            if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+                inputblock.cls += ' has-feedback';
+                inputblock.cn.push(feedback);
+            }
+        };
+        var indicator = {
+            tag : 'i',
+            cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+            tooltip : 'This field is required'
+        };
+        if (this.allowBlank ) {
+            indicator.style = this.allowBlank ? ' display:none' : '';
         }
-        
         if (align ==='left' && this.fieldLabel.length) {
+            
+            cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
+            
             cfg.cn = [
+                indicator,
                 {
                     tag: 'label',
                     'for' :  id,
-                    cls : 'control-label',
+                    cls : 'control-label col-form-label',
                     html : this.fieldLabel
+
                 },
                 {
-                    cls : "",
+                    cls : "", 
                     cn: [
                         inputblock
                     ]
                 }
-
             ];
             
+            var labelCfg = cfg.cn[1];
+            var contentCfg = cfg.cn[2];
+            
+            if(this.indicatorpos == 'right'){
+                cfg.cn = [
+                    {
+                        tag: 'label',
+                        'for' :  id,
+                        cls : 'control-label col-form-label',
+                        cn : [
+                            {
+                                tag : 'span',
+                                html : this.fieldLabel
+                            },
+                            indicator
+                        ]
+                    },
+                    {
+                        cls : "",
+                        cn: [
+                            inputblock
+                        ]
+                    }
+
+                ];
+                
+                labelCfg = cfg.cn[0];
+                contentCfg = cfg.cn[1];
+            
+            }
+            
             if(this.labelWidth > 12){
-                cfg.cn[0].style = "width: " + this.labelWidth + 'px';
+                labelCfg.style = "width: " + this.labelWidth + 'px';
             }
-
+            
             if(this.labelWidth < 13 && this.labelmd == 0){
-                this.labelmd = this.labelWidth;
+                this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
             }
-
+            
             if(this.labellg > 0){
-                cfg.cn[0].cls += ' col-lg-' + this.labellg;
-                cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
+                labelCfg.cls += ' col-lg-' + this.labellg;
+                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
             }
-
+            
             if(this.labelmd > 0){
-                cfg.cn[0].cls += ' col-md-' + this.labelmd;
-                cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
+                labelCfg.cls += ' col-md-' + this.labelmd;
+                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
             }
-
+            
             if(this.labelsm > 0){
-                cfg.cn[0].cls += ' col-sm-' + this.labelsm;
-                cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
+                labelCfg.cls += ' col-sm-' + this.labelsm;
+                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
             }
-
+            
             if(this.labelxs > 0){
-                cfg.cn[0].cls += ' col-xs-' + this.labelxs;
-                cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
+                labelCfg.cls += ' col-xs-' + this.labelxs;
+                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
             }
             
+            
         } else if ( this.fieldLabel.length) {
+                
+            
+            
             cfg.cn = [
-
-               {
-                   tag: 'label',
+                {
+                    tag : 'i',
+                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
+                    tooltip : 'This field is required',
+                    style : this.allowBlank ? ' display:none' : '' 
+                },
+                {
+                    tag: 'label',
                    //cls : 'input-group-addon',
-                   html : this.fieldLabel
+                    html : this.fieldLabel
 
-               },
+                },
 
                inputblock
 
            ];
+           
+           if(this.indicatorpos == 'right'){
+       
+                cfg.cn = [
+                    {
+                        tag: 'label',
+                       //cls : 'input-group-addon',
+                        html : this.fieldLabel
 
-        } else {
+                    },
+                    {
+                        tag : 'i',
+                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
+                        tooltip : 'This field is required',
+                        style : this.allowBlank ? ' display:none' : '' 
+                    },
+
+                   inputblock
+
+               ];
+
+            }
 
+        } else {
+            
             cfg.cn = [
 
-                inputblock
+                    inputblock
 
             ];
                 
+                
+        };
+        
+        if (this.parentType === 'Navbar' &&  this.parent().bar) {
+           cfg.cls += ' navbar-form';
         }
         
-        if (this.disabled) {
-            input.disabled=true;
+        if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
+            // on BS4 we do this only if not form 
+            cfg.cls += ' navbar-form';
+            cfg.tag = 'li';
         }
         
         return cfg;
         
     },
     /**
-     * return the real textarea element.
+     * return the real input element.
      */
     inputEl: function ()
     {
-        return this.el.select('textarea.form-control',true).first();
+        return this.el.select('input.form-control',true).first();
     },
     
-    /**
-     * Clear any invalid styles/messages for this field
-     */
-    clearInvalid : function()
+    tooltipEl : function()
     {
-        
-        if(!this.el || this.preventMark){ // not rendered
-            return;
+        return this.inputEl();
+    },
+    
+    indicatorEl : function()
+    {
+        if (Roo.bootstrap.version == 4) {
+            return false; // not enabled in v4 yet.
         }
         
-        var label = this.el.select('label', true).first();
-        var icon = this.el.select('i.fa-star', true).first();
+        var indicator = this.el.select('i.roo-required-indicator',true).first();
         
-        if(label && icon){
-            icon.remove();
-        }
-        this.el.removeClass( this.validClass);
-        this.inputEl().removeClass('is-invalid');
-         
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-            
-            var feedback = this.el.select('.form-control-feedback', true).first();
-            
-            if(feedback){
-                this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
-            }
-            
+        if(!indicator){
+            return false;
         }
         
-        this.fireEvent('valid', this);
+        return indicator;
+        
     },
     
-     /**
-     * Mark this field as valid
-     */
-    markValid : function()
+    setDisabled : function(v)
     {
-        if(!this.el  || this.preventMark){ // not rendered
+        var i  = this.inputEl().dom;
+        if (!v) {
+            i.removeAttribute('disabled');
             return;
+            
         }
+        i.setAttribute('disabled','true');
+    },
+    initEvents : function()
+    {
+          
+        this.inputEl().on("keydown" , this.fireKey,  this);
+        this.inputEl().on("focus", this.onFocus,  this);
+        this.inputEl().on("blur", this.onBlur,  this);
         
-        this.el.removeClass([this.invalidClass, this.validClass]);
-        this.inputEl().removeClass(['is-valid', 'is-invalid']);
+        this.inputEl().relayEvent('keyup', this);
+        this.inputEl().relayEvent('paste', this);
         
-        var feedback = this.el.select('.form-control-feedback', true).first();
-            
-        if(feedback){
-            this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+        this.indicator = this.indicatorEl();
+        
+        if(this.indicator){
+            this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
         }
-
-        if(this.disabled || this.allowBlank){
-            return;
+        // reference to original value for reset
+        this.originalValue = this.getValue();
+        //Roo.form.TextField.superclass.initEvents.call(this);
+        if(this.validationEvent == 'keyup'){
+            this.validationTask = new Roo.util.DelayedTask(this.validate, this);
+            this.inputEl().on('keyup', this.filterValidation, this);
+        }
+        else if(this.validationEvent !== false){
+            this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
         }
         
-        var label = this.el.select('label', true).first();
-        var icon = this.el.select('i.fa-star', true).first();
-        
-        if(label && icon){
-            icon.remove();
+        if(this.selectOnFocus){
+            this.on("focus", this.preFocus, this);
+            
         }
-        if (Roo.bootstrap.version == 3) {
-            this.el.addClass(this.validClass);
+        if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
+            this.inputEl().on("keypress", this.filterKeys, this);
         } else {
-            this.inputEl().addClass('is-valid');
+            this.inputEl().relayEvent('keypress', this);
+        }
+       /* if(this.grow){
+            this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
+            this.el.on("click", this.autoSize,  this);
+        }
+        */
+        if(this.inputEl().is('input[type=password]') && Roo.isSafari){
+            this.inputEl().on('keydown', this.SafariOnKeyDown, this);
+        }
+        
+        if (typeof(this.before) == 'object') {
+            this.before.render(this.el.select('.roo-input-before',true).first());
+        }
+        if (typeof(this.after) == 'object') {
+            this.after.render(this.el.select('.roo-input-after',true).first());
         }
         
+        this.inputEl().on('change', this.onChange, this);
         
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
-            
-            var feedback = this.el.select('.form-control-feedback', true).first();
-            
-            if(feedback){
-                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
-                this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
-            }
-            
+    },
+    filterValidation : function(e){
+        if(!e.isNavKeyPress()){
+            this.validationTask.delay(this.validationDelay);
+        }
+    },
+     /**
+     * Validates the field value
+     * @return {Boolean} True if the value is valid, else false
+     */
+    validate : function(){
+        //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
+        if(this.disabled || this.validateValue(this.getRawValue())){
+            this.markValid();
+            return true;
         }
         
-        this.fireEvent('valid', this);
+        this.markInvalid();
+        return false;
     },
     
-     /**
-     * Mark this field as invalid
-     * @param {String} msg The validation message
+    
+    /**
+     * Validates a value according to the field's validation rules and marks the field as invalid
+     * if the validation fails
+     * @param {Mixed} value The value to validate
+     * @return {Boolean} True if the value is valid, else false
      */
-    markInvalid : function(msg)
+    validateValue : function(value)
     {
-        if(!this.el  || this.preventMark){ // not rendered
-            return;
+        if(this.getVisibilityEl().hasClass('hidden')){
+            return true;
         }
         
-        this.el.removeClass([this.invalidClass, this.validClass]);
-        this.inputEl().removeClass(['is-valid', 'is-invalid']);
+        if(value.length < 1)  { // if it's blank
+            if(this.allowBlank){
+                return true;
+            }
+            return false;
+        }
         
-        var feedback = this.el.select('.form-control-feedback', true).first();
-            
-        if(feedback){
-            this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+        if(value.length < this.minLength){
+            return false;
         }
-
-        if(this.disabled || this.allowBlank){
-            return;
+        if(value.length > this.maxLength){
+            return false;
         }
-        
-        var label = this.el.select('label', true).first();
-        var icon = this.el.select('i.fa-star', true).first();
-        
-        if(!this.getValue().length && label && !icon){
-            this.el.createChild({
-                tag : 'i',
-                cls : 'text-danger fa fa-lg fa-star',
-                tooltip : 'This field is required',
-                style : 'margin-right:5px;'
-            }, label, true);
+        if(this.vtype){
+            var vt = Roo.form.VTypes;
+            if(!vt[this.vtype](value, this)){
+                return false;
+            }
+        }
+        if(typeof this.validator == "function"){
+            var msg = this.validator(value);
+            if(msg !== true){
+                return false;
+            }
+            if (typeof(msg) == 'string') {
+                this.invalidText = msg;
+            }
         }
         
-        if (Roo.bootstrap.version == 3) {
-            this.el.addClass(this.invalidClass);
-        } else {
-            this.inputEl().addClass('is-invalid');
+        if(this.regex && !this.regex.test(value)){
+            return false;
         }
         
-        // fixme ... this may be depricated need to test..
-        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-            
-            var feedback = this.el.select('.form-control-feedback', true).first();
-            
-            if(feedback){
-                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
-                
-                if(this.getValue().length || this.forceFeedback){
-                    this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
-                }
-                
+        return true;
+    },
+    
+     // private
+    fireKey : function(e){
+        //Roo.log('field ' + e.getKey());
+        if(e.isNavKeyPress()){
+            this.fireEvent("specialkey", this, e);
+        }
+    },
+    focus : function (selectText){
+        if(this.rendered){
+            this.inputEl().focus();
+            if(selectText === true){
+                this.inputEl().dom.select();
             }
-            
         }
-        
-        this.fireEvent('invalid', this, msg);
-    }
-});
-
-/*
- * - LGPL
- *
- * trigger field - base class for combo..
- * 
- */
-/**
- * @class Roo.bootstrap.TriggerField
- * @extends Roo.bootstrap.Input
- * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
- * The trigger has no default action, so you must assign a function to implement the trigger click handler by
- * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
- * for which you can provide a custom implementation.  For example:
- * <pre><code>
-var trigger = new Roo.bootstrap.TriggerField();
-trigger.onTriggerClick = myTriggerFn;
-trigger.applyTo('my-field');
-</code></pre>
- *
- * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
- * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
- * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
- * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
- * @cfg {String} caret (search|calendar) BS3 only - carat fa name
-
- * @constructor
- * Create a new TriggerField.
- * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
- * to the base TextField)
- */
-Roo.bootstrap.TriggerField = function(config){
-    this.mimicing = false;
-    Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
-};
+        return this;
+    } ,
+    
+    onFocus : function(){
+        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+           // this.el.addClass(this.focusClass);
+        }
+        if(!this.hasFocus){
+            this.hasFocus = true;
+            this.startValue = this.getValue();
+            this.fireEvent("focus", this);
+        }
+    },
+    
+    beforeBlur : Roo.emptyFn,
 
-Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
+    
+    // private
+    onBlur : function(){
+        this.beforeBlur();
+        if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+            //this.el.removeClass(this.focusClass);
+        }
+        this.hasFocus = false;
+        if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
+            this.validate();
+        }
+        var v = this.getValue();
+        if(String(v) !== String(this.startValue)){
+            this.fireEvent('change', this, v, this.startValue);
+        }
+        this.fireEvent("blur", this);
+    },
+    
+    onChange : function(e)
+    {
+        var v = this.getValue();
+        if(String(v) !== String(this.startValue)){
+            this.fireEvent('change', this, v, this.startValue);
+        }
+        
+    },
+    
     /**
-     * @cfg {String} triggerClass A CSS class to apply to the trigger
+     * Resets the current field value to the originally loaded value and clears any validation messages
      */
+    reset : function(){
+        this.setValue(this.originalValue);
+        this.validate();
+    },
      /**
-     * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
+     * Returns the name of the field
+     * @return {Mixed} name The name field
      */
-    hideTrigger:false,
-
+    getName: function(){
+        return this.name;
+    },
+     /**
+     * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
+     * @return {Mixed} value The field value
+     */
+    getValue : function(){
+        
+        var v = this.inputEl().getValue();
+        
+        return v;
+    },
     /**
-     * @cfg {Boolean} removable (true|false) special filter default false
+     * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
+     * @return {Mixed} value The field value
      */
-    removable : false,
-    
-    /** @cfg {Boolean} grow @hide */
-    /** @cfg {Number} growMin @hide */
-    /** @cfg {Number} growMax @hide */
-
+    getRawValue : function(){
+        var v = this.inputEl().getValue();
+        
+        return v;
+    },
+    
     /**
-     * @hide 
-     * @method
+     * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
+     * @param {Mixed} value The value to set
      */
-    autoSize: Roo.emptyFn,
-    // private
-    monitorTab : true,
-    // private
-    deferHeight : true,
-
-    
-    actionMode : 'wrap',
+    setRawValue : function(v){
+        return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
+    },
     
-    caret : false,
+    selectText : function(start, end){
+        var v = this.getRawValue();
+        if(v.length > 0){
+            start = start === undefined ? 0 : start;
+            end = end === undefined ? v.length : end;
+            var d = this.inputEl().dom;
+            if(d.setSelectionRange){
+                d.setSelectionRange(start, end);
+            }else if(d.createTextRange){
+                var range = d.createTextRange();
+                range.moveStart("character", start);
+                range.moveEnd("character", v.length-end);
+                range.select();
+            }
+        }
+    },
     
+    /**
+     * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
+     * @param {Mixed} value The value to set
+     */
+    setValue : function(v){
+        this.value = v;
+        if(this.rendered){
+            this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
+            this.validate();
+        }
+    },
     
-    getAutoCreate : function(){
-       
-        var align = this.labelAlign || this.parentLabelAlign();
-        
-        var id = Roo.id();
-        
-        var cfg = {
-            cls: 'form-group' //input-group
-        };
-        
+    /*
+    processValue : function(value){
+        if(this.stripCharsRe){
+            var newValue = value.replace(this.stripCharsRe, '');
+            if(newValue !== value){
+                this.setRawValue(newValue);
+                return newValue;
+            }
+        }
+        return value;
+    },
+  */
+    preFocus : function(){
         
-        var input =  {
-            tag: 'input',
-            id : id,
-            type : this.inputType,
-            cls : 'form-control',
-            autocomplete: 'new-password',
-            placeholder : this.placeholder || '' 
-            
-        };
-        if (this.name) {
-            input.name = this.name;
+        if(this.selectOnFocus){
+            this.inputEl().dom.select();
         }
-        if (this.size) {
-            input.cls += ' input-' + this.size;
+    },
+    filterKeys : function(e){
+        var k = e.getKey();
+        if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
+            return;
+        }
+        var c = e.getCharCode(), cc = String.fromCharCode(c);
+        if(Roo.isIE && (e.isSpecialKey() || !cc)){
+            return;
+        }
+        if(!this.maskRe.test(cc)){
+            e.stopEvent();
         }
+    },
+     /**
+     * Clear any invalid styles/messages for this field
+     */
+    clearInvalid : function(){
         
-        if (this.disabled) {
-            input.disabled=true;
+        if(!this.el || this.preventMark){ // not rendered
+            return;
         }
         
-        var inputblock = input;
         
-        if(this.hasFeedback && !this.allowBlank){
+        this.el.removeClass([this.invalidClass, 'is-invalid']);
+        
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
             
-            var feedback = {
-                tag: 'span',
-                cls: 'glyphicon form-control-feedback'
-            };
+            var feedback = this.el.select('.form-control-feedback', true).first();
             
-            if(this.removable && !this.editable  ){
-                inputblock = {
-                    cls : 'has-feedback',
-                    cn :  [
-                        inputblock,
-                        {
-                            tag: 'button',
-                            html : 'x',
-                            cls : 'roo-combo-removable-btn close'
-                        },
-                        feedback
-                    ] 
-                };
-            } else {
-                inputblock = {
-                    cls : 'has-feedback',
-                    cn :  [
-                        inputblock,
-                        feedback
-                    ] 
-                };
-            }
-
-        } else {
-            if(this.removable && !this.editable ){
-                inputblock = {
-                    cls : 'roo-removable',
-                    cn :  [
-                        inputblock,
-                        {
-                            tag: 'button',
-                            html : 'x',
-                            cls : 'roo-combo-removable-btn close'
-                        }
-                    ] 
-                };
+            if(feedback){
+                this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
             }
+            
         }
         
-        if (this.before || this.after) {
-            
-            inputblock = {
-                cls : 'input-group',
-                cn :  [] 
-            };
-            if (this.before) {
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'input-group-addon input-group-prepend input-group-text',
-                    html : this.before
-                });
-            }
+        if(this.indicator){
+            this.indicator.removeClass('visible');
+            this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+        }
+        
+        this.fireEvent('valid', this);
+    },
+    
+     /**
+     * Mark this field as valid
+     */
+    markValid : function()
+    {
+        if(!this.el  || this.preventMark){ // not rendered...
+            return;
+        }
+        
+        this.el.removeClass([this.invalidClass, this.validClass]);
+        this.inputEl().removeClass(['is-valid', 'is-invalid']);
+
+        var feedback = this.el.select('.form-control-feedback', true).first();
             
-            inputblock.cn.push(input);
+        if(feedback){
+            this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+        }
+        
+        if(this.indicator){
+            this.indicator.removeClass('visible');
+            this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+        }
+        
+        if(this.disabled){
+            return;
+        }
+        
+           
+        if(this.allowBlank && !this.getRawValue().length){
+            return;
+        }
+        if (Roo.bootstrap.version == 3) {
+            this.el.addClass(this.validClass);
+        } else {
+            this.inputEl().addClass('is-valid');
+        }
+
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
             
-            if(this.hasFeedback && !this.allowBlank){
-                inputblock.cls += ' has-feedback';
-                inputblock.cn.push(feedback);
-            }
+            var feedback = this.el.select('.form-control-feedback', true).first();
             
-            if (this.after) {
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'input-group-addon input-group-append input-group-text',
-                    html : this.after
-                });
+            if(feedback){
+                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+                this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
             }
             
-        };
+        }
         
-      
+        this.fireEvent('valid', this);
+    },
+    
+     /**
+     * Mark this field as invalid
+     * @param {String} msg The validation message
+     */
+    markInvalid : function(msg)
+    {
+        if(!this.el  || this.preventMark){ // not rendered
+            return;
+        }
         
-        var ibwrap = inputblock;
+        this.el.removeClass([this.invalidClass, this.validClass]);
+        this.inputEl().removeClass(['is-valid', 'is-invalid']);
         
-        if(this.multiple){
-            ibwrap = {
-                tag: 'ul',
-                cls: 'roo-select2-choices',
-                cn:[
-                    {
-                        tag: 'li',
-                        cls: 'roo-select2-search-field',
-                        cn: [
+        var feedback = this.el.select('.form-control-feedback', true).first();
+            
+        if(feedback){
+            this.el.select('.form-control-feedback', true).first().removeClass(
+                    [this.invalidFeedbackClass, this.validFeedbackClass]);
+        }
 
-                            inputblock
-                        ]
-                    }
-                ]
-            };
-                
+        if(this.disabled){
+            return;
         }
         
-        var combobox = {
-            cls: 'roo-select2-container input-group',
-            cn: [
-                 {
-                    tag: 'input',
-                    type : 'hidden',
-                    cls: 'form-hidden-field'
-                },
-                ibwrap
-            ]
-        };
-        
-        if(!this.multiple && this.showToggleBtn){
-            
-            var caret = {
-                        tag: 'span',
-                        cls: 'caret'
-             };
-            if (this.caret != false) {
-                caret = {
-                     tag: 'i',
-                     cls: 'fa fa-' + this.caret
-                };
-                
-            }
-            
-            combobox.cn.push({
-                tag :'span',
-                cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
-                cn : [
-                    Roo.bootstrap.version == 3 ? caret : '',
-                    {
-                        tag: 'span',
-                        cls: 'combobox-clear',
-                        cn  : [
-                            {
-                                tag : 'i',
-                                cls: 'icon-remove'
-                            }
-                        ]
-                    }
-                ]
-
-            })
+        if(this.allowBlank && !this.getRawValue().length){
+            return;
         }
         
-        if(this.multiple){
-            combobox.cls += ' roo-select2-container-multi';
+        if(this.indicator){
+            this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+            this.indicator.addClass('visible');
         }
-         var indicator = {
-            tag : 'i',
-            cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
-            tooltip : 'This field is required'
-        };
-        if (Roo.bootstrap.version == 4) {
-            indicator = {
-                tag : 'i',
-                style : 'display:none'
-            };
+        if (Roo.bootstrap.version == 3) {
+            this.el.addClass(this.invalidClass);
+        } else {
+            this.inputEl().addClass('is-invalid');
         }
         
         
-        if (align ==='left' && this.fieldLabel.length) {
-            
-            cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
-
-            cfg.cn = [
-                indicator,
-                {
-                    tag: 'label',
-                    'for' :  id,
-                    cls : 'control-label',
-                    html : this.fieldLabel
-
-                },
-                {
-                    cls : "", 
-                    cn: [
-                        combobox
-                    ]
-                }
-
-            ];
+        
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
             
-            var labelCfg = cfg.cn[1];
-            var contentCfg = cfg.cn[2];
+            var feedback = this.el.select('.form-control-feedback', true).first();
             
-            if(this.indicatorpos == 'right'){
-                cfg.cn = [
-                    {
-                        tag: 'label',
-                        'for' :  id,
-                        cls : 'control-label',
-                        cn : [
-                            {
-                                tag : 'span',
-                                html : this.fieldLabel
-                            },
-                            indicator
-                        ]
-                    },
-                    {
-                        cls : "", 
-                        cn: [
-                            combobox
-                        ]
-                    }
-
-                ];
+            if(feedback){
+                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+                
+                if(this.getValue().length || this.forceFeedback){
+                    this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
+                }
                 
-                labelCfg = cfg.cn[0];
-                contentCfg = cfg.cn[1];
             }
             
-            if(this.labelWidth > 12){
-                labelCfg.style = "width: " + this.labelWidth + 'px';
-            }
+        }
+        
+        this.fireEvent('invalid', this, msg);
+    },
+    // private
+    SafariOnKeyDown : function(event)
+    {
+        // this is a workaround for a password hang bug on chrome/ webkit.
+        if (this.inputEl().dom.type != 'password') {
+            return;
+        }
+        
+        var isSelectAll = false;
+        
+        if(this.inputEl().dom.selectionEnd > 0){
+            isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
+        }
+        if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
+            event.preventDefault();
+            this.setValue('');
+            return;
+        }
+        
+        if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
             
-            if(this.labelWidth < 13 && this.labelmd == 0){
-                this.labelmd = this.labelWidth;
-            }
+            event.preventDefault();
+            // this is very hacky as keydown always get's upper case.
+            //
+            var cc = String.fromCharCode(event.getCharCode());
+            this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
             
-            if(this.labellg > 0){
-                labelCfg.cls += ' col-lg-' + this.labellg;
-                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+        }
+    },
+    adjustWidth : function(tag, w){
+        tag = tag.toLowerCase();
+        if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
+            if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
+                if(tag == 'input'){
+                    return w + 2;
+                }
+                if(tag == 'textarea'){
+                    return w-2;
+                }
+            }else if(Roo.isOpera){
+                if(tag == 'input'){
+                    return w + 2;
+                }
+                if(tag == 'textarea'){
+                    return w-2;
+                }
             }
+        }
+        return w;
+    },
+    
+    setFieldLabel : function(v)
+    {
+        if(!this.rendered){
+            return;
+        }
+        
+        if(this.indicatorEl()){
+            var ar = this.el.select('label > span',true);
             
-            if(this.labelmd > 0){
-                labelCfg.cls += ' col-md-' + this.labelmd;
-                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+            if (ar.elements.length) {
+                this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+                this.fieldLabel = v;
+                return;
             }
             
-            if(this.labelsm > 0){
-                labelCfg.cls += ' col-sm-' + this.labelsm;
-                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
-            }
+            var br = this.el.select('label',true);
             
-            if(this.labelxs > 0){
-                labelCfg.cls += ' col-xs-' + this.labelxs;
-                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+            if(br.elements.length) {
+                this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+                this.fieldLabel = v;
+                return;
             }
             
-        } else if ( this.fieldLabel.length) {
-//                Roo.log(" label");
-            cfg.cn = [
-                indicator,
-               {
-                   tag: 'label',
-                   //cls : 'input-group-addon',
-                   html : this.fieldLabel
-
-               },
+            Roo.log('Cannot Found any of label > span || label in input');
+            return;
+        }
+        
+        this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+        this.fieldLabel = v;
+        
+        
+    }
+});
 
-               combobox
+/*
+ * - LGPL
+ *
+ * Input
+ * 
+ */
 
-            ];
-            
-            if(this.indicatorpos == 'right'){
-                
-                cfg.cn = [
-                    {
-                       tag: 'label',
-                       cn : [
-                           {
-                               tag : 'span',
-                               html : this.fieldLabel
-                           },
-                           indicator
-                       ]
-
-                    },
-                    combobox
-
-                ];
+/**
+ * @class Roo.bootstrap.TextArea
+ * @extends Roo.bootstrap.Input
+ * Bootstrap TextArea class
+ * @cfg {Number} cols Specifies the visible width of a text area
+ * @cfg {Number} rows Specifies the visible number of lines in a text area
+ * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
+ * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
+ * @cfg {string} html text
+ * 
+ * @constructor
+ * Create a new TextArea
+ * @param {Object} config The config object
+ */
 
-            }
+Roo.bootstrap.TextArea = function(config){
+    Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
+   
+};
 
-        } else {
-            
-//                Roo.log(" no label && no align");
-                cfg = combobox
-                     
-                
-        }
+Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
+     
+    cols : false,
+    rows : 5,
+    readOnly : false,
+    warp : 'soft',
+    resize : false,
+    value: false,
+    html: false,
+    
+    getAutoCreate : function(){
         
-        var settings=this;
-        ['xs','sm','md','lg'].map(function(size){
-            if (settings[size]) {
-                cfg.cls += ' col-' + size + '-' + settings[size];
-            }
-        });
+        var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
         
-        return cfg;
+        var id = Roo.id();
         
-    },
-    
-    
-    
-    // private
-    onResize : function(w, h){
-//        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
-//        if(typeof w == 'number'){
-//            var x = w - this.trigger.getWidth();
-//            this.inputEl().setWidth(this.adjustWidth('input', x));
-//            this.trigger.setStyle('left', x+'px');
-//        }
-    },
-
-    // private
-    adjustSize : Roo.BoxComponent.prototype.adjustSize,
-
-    // private
-    getResizeEl : function(){
-        return this.inputEl();
-    },
-
-    // private
-    getPositionEl : function(){
-        return this.inputEl();
-    },
-
-    // private
-    alignErrorIcon : function(){
-        this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
-    },
-
-    // private
-    initEvents : function(){
+        var cfg = {};
         
-        this.createList();
+        if(this.inputType != 'hidden'){
+            cfg.cls = 'form-group' //input-group
+        }
         
-        Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
-        //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
-        if(!this.multiple && this.showToggleBtn){
-            this.trigger = this.el.select('span.dropdown-toggle',true).first();
-            if(this.hideTrigger){
-                this.trigger.setDisplayed(false);
-            }
-            this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
+        var input =  {
+            tag: 'textarea',
+            id : id,
+            warp : this.warp,
+            rows : this.rows,
+            value : this.value || '',
+            html: this.html || '',
+            cls : 'form-control',
+            placeholder : this.placeholder || '' 
+            
+        };
+        
+        if(this.maxLength && this.maxLength != Number.MAX_VALUE){
+            input.maxLength = this.maxLength;
         }
         
-        if(this.multiple){
-            this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
+        if(this.resize){
+            input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
         }
         
-        if(this.removable && !this.editable && !this.tickable){
-            var close = this.closeTriggerEl();
-            
-            if(close){
-                close.setVisibilityMode(Roo.Element.DISPLAY).hide();
-                close.on('click', this.removeBtnClick, this, close);
-            }
+        if(this.cols){
+            input.cols = this.cols;
         }
         
-        //this.trigger.addClassOnOver('x-form-trigger-over');
-        //this.trigger.addClassOnClick('x-form-trigger-click');
+        if (this.readOnly) {
+            input.readonly = true;
+        }
         
-        //if(!this.width){
-        //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
-        //}
-    },
-    
-    closeTriggerEl : function()
-    {
-        var close = this.el.select('.roo-combo-removable-btn', true).first();
-        return close ? close : false;
-    },
-    
-    removeBtnClick : function(e, h, el)
-    {
-        e.preventDefault();
+        if (this.name) {
+            input.name = this.name;
+        }
         
-        if(this.fireEvent("remove", this) !== false){
-            this.reset();
-            this.fireEvent("afterremove", this)
+        if (this.size) {
+            input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
         }
-    },
-    
-    createList : function()
-    {
-        this.list = Roo.get(document.body).createChild({
-            tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
-            cls: 'typeahead typeahead-long dropdown-menu shadow',
-            style: 'display:none'
+        
+        var settings=this;
+        ['xs','sm','md','lg'].map(function(size){
+            if (settings[size]) {
+                cfg.cls += ' col-' + size + '-' + settings[size];
+            }
         });
         
-        this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
+        var inputblock = input;
         
-    },
-
-    // private
-    initTrigger : function(){
-       
-    },
+        if(this.hasFeedback && !this.allowBlank){
+            
+            var feedback = {
+                tag: 'span',
+                cls: 'glyphicon form-control-feedback'
+            };
 
-    // private
-    onDestroy : function(){
-        if(this.trigger){
-            this.trigger.removeAllListeners();
-          //  this.trigger.remove();
+            inputblock = {
+                cls : 'has-feedback',
+                cn :  [
+                    input,
+                    feedback
+                ] 
+            };  
         }
-        //if(this.wrap){
-        //    this.wrap.remove();
-        //}
-        Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
-    },
-
-    // private
-    onFocus : function(){
-        Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
-        /*
-        if(!this.mimicing){
-            this.wrap.addClass('x-trigger-wrap-focus');
-            this.mimicing = true;
-            Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
-            if(this.monitorTab){
-                this.el.on("keydown", this.checkTab, this);
+        
+        
+        if (this.before || this.after) {
+            
+            inputblock = {
+                cls : 'input-group',
+                cn :  [] 
+            };
+            if (this.before) {
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'input-group-addon',
+                    html : this.before
+                });
+            }
+            
+            inputblock.cn.push(input);
+            
+            if(this.hasFeedback && !this.allowBlank){
+                inputblock.cls += ' has-feedback';
+                inputblock.cn.push(feedback);
+            }
+            
+            if (this.after) {
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'input-group-addon',
+                    html : this.after
+                });
             }
+            
         }
-        */
-    },
-
-    // private
-    checkTab : function(e){
-        if(e.getKey() == e.TAB){
-            this.triggerBlur();
-        }
-    },
+        
+        if (align ==='left' && this.fieldLabel.length) {
+            cfg.cn = [
+                {
+                    tag: 'label',
+                    'for' :  id,
+                    cls : 'control-label',
+                    html : this.fieldLabel
+                },
+                {
+                    cls : "",
+                    cn: [
+                        inputblock
+                    ]
+                }
 
-    // private
-    onBlur : function(){
-        // do nothing
-    },
+            ];
+            
+            if(this.labelWidth > 12){
+                cfg.cn[0].style = "width: " + this.labelWidth + 'px';
+            }
 
-    // private
-    mimicBlur : function(e, t){
-        /*
-        if(!this.wrap.contains(t) && this.validateBlur()){
-            this.triggerBlur();
-        }
-        */
-    },
+            if(this.labelWidth < 13 && this.labelmd == 0){
+                this.labelmd = this.labelWidth;
+            }
 
-    // private
-    triggerBlur : function(){
-        this.mimicing = false;
-        Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
-        if(this.monitorTab){
-            this.el.un("keydown", this.checkTab, this);
-        }
-        //this.wrap.removeClass('x-trigger-wrap-focus');
-        Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
-    },
+            if(this.labellg > 0){
+                cfg.cn[0].cls += ' col-lg-' + this.labellg;
+                cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
+            }
 
-    // private
-    // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
-    validateBlur : function(e, t){
-        return true;
-    },
+            if(this.labelmd > 0){
+                cfg.cn[0].cls += ' col-md-' + this.labelmd;
+                cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
+            }
 
-    // private
-    onDisable : function(){
-        this.inputEl().dom.disabled = true;
-        //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
-        //if(this.wrap){
-        //    this.wrap.addClass('x-item-disabled');
-        //}
-    },
+            if(this.labelsm > 0){
+                cfg.cn[0].cls += ' col-sm-' + this.labelsm;
+                cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
+            }
 
-    // private
-    onEnable : function(){
-        this.inputEl().dom.disabled = false;
-        //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
-        //if(this.wrap){
-        //    this.el.removeClass('x-item-disabled');
-        //}
-    },
+            if(this.labelxs > 0){
+                cfg.cn[0].cls += ' col-xs-' + this.labelxs;
+                cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
+            }
+            
+        } else if ( this.fieldLabel.length) {
+            cfg.cn = [
 
-    // private
-    onShow : function(){
-        var ae = this.getActionEl();
-        
-        if(ae){
-            ae.dom.style.display = '';
-            ae.dom.style.visibility = 'visible';
-        }
-    },
+               {
+                   tag: 'label',
+                   //cls : 'input-group-addon',
+                   html : this.fieldLabel
 
-    // private
-    
-    onHide : function(){
-        var ae = this.getActionEl();
-        ae.dom.style.display = 'none';
-    },
+               },
 
-    /**
-     * The function that should handle the trigger's click event.  This method does nothing by default until overridden
-     * by an implementing function.
-     * @method
-     * @param {EventObject} e
-     */
-    onTriggerClick : Roo.emptyFn
-});
-/*
-* Licence: LGPL
-*/
+               inputblock
 
-/**
- * @class Roo.bootstrap.CardUploader
- * @extends Roo.bootstrap.Button
- * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
- * @cfg {Number} errorTimeout default 3000
- * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
- * @cfg {Array}  html The button text.
+           ];
 
- *
- * @constructor
- * Create a new CardUploader
- * @param {Object} config The config object
- */
+        } else {
 
-Roo.bootstrap.CardUploader = function(config){
-    
-    
-    Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
-    
-    
-    this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
-        return r.data.id
-     });
-    
-     this.addEvents({
-         // raw events
-        /**
-         * @event preview
-         * When a image is clicked on - and needs to display a slideshow or similar..
-         * @param {Roo.bootstrap.Card} this
-         * @param {Object} The image information data 
-         *
-         */
-        'preview' : true,
-         /**
-         * @event download
-         * When a the download link is clicked
-         * @param {Roo.bootstrap.Card} this
-         * @param {Object} The image information data  contains 
-         */
-        'download' : true
-        
-    });
-};
-Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
-    
-     
-    errorTimeout : 3000,
-     
-    images : false,
-   
-    fileCollection : false,
-    allowBlank : true,
-    
-    getAutoCreate : function()
-    {
-        
-        var cfg =  {
-            cls :'form-group' ,
-            cn : [
-               
-                {
-                    tag: 'label',
-                   //cls : 'input-group-addon',
-                    html : this.fieldLabel
+            cfg.cn = [
 
-                },
+                inputblock
 
-                {
-                    tag: 'input',
-                    type : 'hidden',
-                    name : this.name,
-                    value : this.value,
-                    cls : 'd-none  form-control'
-                },
-                
-                {
-                    tag: 'input',
-                    multiple : 'multiple',
-                    type : 'file',
-                    cls : 'd-none  roo-card-upload-selector'
-                },
+            ];
                 
-                {
-                    cls : 'roo-card-uploader-button-container w-100 mb-2'
-                },
-                {
-                    cls : 'card-columns roo-card-uploader-container'
-                }
-
-            ]
-        };
-           
-         
+        }
+        
+        if (this.disabled) {
+            input.disabled=true;
+        }
+        
         return cfg;
+        
     },
-    
-    getChildContainer : function() /// what children are added to.
-    {
-        return this.containerEl;
-    },
-   
-    getButtonContainer : function() /// what children are added to.
+    /**
+     * return the real textarea element.
+     */
+    inputEl: function ()
     {
-        return this.el.select(".roo-card-uploader-button-container").first();
+        return this.el.select('textarea.form-control',true).first();
     },
-   
-    initEvents : function()
+    
+    /**
+     * Clear any invalid styles/messages for this field
+     */
+    clearInvalid : function()
     {
         
-        Roo.bootstrap.Input.prototype.initEvents.call(this);
+        if(!this.el || this.preventMark){ // not rendered
+            return;
+        }
         
-        var t = this;
-        this.addxtype({
-            xns: Roo.bootstrap,
-
-            xtype : 'Button',
-            container_method : 'getButtonContainer' ,            
-            html :  this.html, // fix changable?
-            cls : 'w-100 ',
-            listeners : {
-                'click' : function(btn, e) {
-                    t.onClick(e);
-                }
-            }
-        });
-        
-        
-        
-        
-        this.urlAPI = (window.createObjectURL && window) || 
-                                (window.URL && URL.revokeObjectURL && URL) || 
-                                (window.webkitURL && webkitURL);
-                        
-         
-         
-         
-        this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
+        var label = this.el.select('label', true).first();
+        var icon = this.el.select('i.fa-star', true).first();
         
-        this.selectorEl.on('change', this.onFileSelected, this);
-        if (this.images) {
-            var t = this;
-            this.images.forEach(function(img) {
-                t.addCard(img)
-            });
-            this.images = false;
+        if(label && icon){
+            icon.remove();
         }
-        this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
-         
-       
-    },
-    
-   
-    onClick : function(e)
-    {
-        e.preventDefault();
-         
-        this.selectorEl.dom.click();
+        this.el.removeClass( this.validClass);
+        this.inputEl().removeClass('is-invalid');
          
-    },
-    
-    onFileSelected : function(e)
-    {
-        e.preventDefault();
-        
-        if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
-            return;
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+            
+            var feedback = this.el.select('.form-control-feedback', true).first();
+            
+            if(feedback){
+                this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
+            }
+            
         }
         
-        Roo.each(this.selectorEl.dom.files, function(file){    
-            this.addFile(file);
-        }, this);
-         
+        this.fireEvent('valid', this);
     },
     
-      
-    
-      
-    
-    addFile : function(file)
+     /**
+     * Mark this field as valid
+     */
+    markValid : function()
     {
-           
-        if(typeof(file) === 'string'){
-            throw "Add file by name?"; // should not happen
+        if(!this.el  || this.preventMark){ // not rendered
             return;
         }
         
-        if(!file || !this.urlAPI){
+        this.el.removeClass([this.invalidClass, this.validClass]);
+        this.inputEl().removeClass(['is-valid', 'is-invalid']);
+        
+        var feedback = this.el.select('.form-control-feedback', true).first();
+            
+        if(feedback){
+            this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+        }
+
+        if(this.disabled || this.allowBlank){
             return;
         }
         
-        // file;
-        // file.type;
+        var label = this.el.select('label', true).first();
+        var icon = this.el.select('i.fa-star', true).first();
         
-        var _this = this;
+        if(label && icon){
+            icon.remove();
+        }
+        if (Roo.bootstrap.version == 3) {
+            this.el.addClass(this.validClass);
+        } else {
+            this.inputEl().addClass('is-valid');
+        }
         
         
-        var url = _this.urlAPI.createObjectURL( file);
-           
-        this.addCard({
-            id : Roo.bootstrap.CardUploader.ID--,
-            is_uploaded : false,
-            src : url,
-            srcfile : file,
-            title : file.name,
-            mimetype : file.type,
-            preview : false,
-            is_deleted : 0
-        });
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
+            
+            var feedback = this.el.select('.form-control-feedback', true).first();
+            
+            if(feedback){
+                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+                this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
+            }
+            
+        }
         
+        this.fireEvent('valid', this);
     },
     
-    /**
-     * addCard - add an Attachment to the uploader
-     * @param data - the data about the image to upload
-     *
-     * {
-          id : 123
-          title : "Title of file",
-          is_uploaded : false,
-          src : "http://.....",
-          srcfile : { the File upload object },
-          mimetype : file.type,
-          preview : false,
-          is_deleted : 0
-          .. any other data...
-        }
-     *
-     * 
-    */
-    
-    addCard : function (data)
+     /**
+     * Mark this field as invalid
+     * @param {String} msg The validation message
+     */
+    markInvalid : function(msg)
     {
-        // hidden input element?
-        // if the file is not an image...
-        //then we need to use something other that and header_image
-        var t = this;
-        //   remove.....
-        var footer = [
-            {
-                xns : Roo.bootstrap,
-                xtype : 'CardFooter',
-                 items: [
-                    {
-                        xns : Roo.bootstrap,
-                        xtype : 'Element',
-                        cls : 'd-flex',
-                        items : [
-                            
-                            {
-                                xns : Roo.bootstrap,
-                                xtype : 'Button',
-                                html : String.format("<small>{0}</small>", data.title),
-                                cls : 'col-10 text-left',
-                                size: 'sm',
-                                weight: 'link',
-                                fa : 'download',
-                                listeners : {
-                                    click : function() {
-                                     
-                                        t.fireEvent( "download", t, data );
-                                    }
-                                }
-                            },
-                          
-                            {
-                                xns : Roo.bootstrap,
-                                xtype : 'Button',
-                                style: 'max-height: 28px; ',
-                                size : 'sm',
-                                weight: 'danger',
-                                cls : 'col-2',
-                                fa : 'times',
-                                listeners : {
-                                    click : function() {
-                                        t.removeCard(data.id)
-                                    }
-                                }
-                            }
-                        ]
-                    }
-                    
-                ] 
-            }
-            
-        ];
+        if(!this.el  || this.preventMark){ // not rendered
+            return;
+        }
         
-        var cn = this.addxtype(
-            {
-                 
-                xns : Roo.bootstrap,
-                xtype : 'Card',
-                closeable : true,
-                header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
-                header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
-                header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
-                data : data,
-                html : false,
-                 
-                items : footer,
-                initEvents : function() {
-                    Roo.bootstrap.Card.prototype.initEvents.call(this);
-                    var card = this;
-                    this.imgEl = this.el.select('.card-img-top').first();
-                    if (this.imgEl) {
-                        this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
-                        this.imgEl.set({ 'pointer' : 'cursor' });
-                                  
-                    }
-                    this.getCardFooter().addClass('p-1');
-                    
-                  
-                }
-                
-            }
-        );
-        // dont' really need ot update items.
-        // this.items.push(cn);
-        this.fileCollection.add(cn);
+        this.el.removeClass([this.invalidClass, this.validClass]);
+        this.inputEl().removeClass(['is-valid', 'is-invalid']);
         
-        if (!data.srcfile) {
-            this.updateInput();
+        var feedback = this.el.select('.form-control-feedback', true).first();
+            
+        if(feedback){
+            this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+        }
+
+        if(this.disabled || this.allowBlank){
             return;
         }
-            
-        var _t = this;
-        var reader = new FileReader();
-        reader.addEventListener("load", function() {  
-            data.srcdata =  reader.result;
-            _t.updateInput();
-        });
-        reader.readAsDataURL(data.srcfile);
-        
         
+        var label = this.el.select('label', true).first();
+        var icon = this.el.select('i.fa-star', true).first();
         
-    },
-    removeCard : function(id)
-    {
+        if(!this.getValue().length && label && !icon){
+            this.el.createChild({
+                tag : 'i',
+                cls : 'text-danger fa fa-lg fa-star',
+                tooltip : 'This field is required',
+                style : 'margin-right:5px;'
+            }, label, true);
+        }
         
-        var card  = this.fileCollection.get(id);
-        card.data.is_deleted = 1;
-        card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
-        //this.fileCollection.remove(card);
-        //this.items = this.items.filter(function(e) { return e != card });
-        // dont' really need ot update items.
-        card.el.dom.parentNode.removeChild(card.el.dom);
-        this.updateInput();
-
+        if (Roo.bootstrap.version == 3) {
+            this.el.addClass(this.invalidClass);
+        } else {
+            this.inputEl().addClass('is-invalid');
+        }
         
-    },
-    reset: function()
-    {
-        this.fileCollection.each(function(card) {
-            if (card.el.dom && card.el.dom.parentNode) {
-                card.el.dom.parentNode.removeChild(card.el.dom);
+        // fixme ... this may be depricated need to test..
+        if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+            
+            var feedback = this.el.select('.form-control-feedback', true).first();
+            
+            if(feedback){
+                this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+                
+                if(this.getValue().length || this.forceFeedback){
+                    this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
+                }
+                
             }
-        });
-        this.fileCollection.clear();
-        this.updateInput();
-    },
-    
-    updateInput : function()
-    {
-         var data = [];
-        this.fileCollection.each(function(e) {
-            data.push(e.data);
             
-        });
-        this.inputEl().dom.value = JSON.stringify(data);
-        
-        
+        }
         
+        this.fireEvent('invalid', this, msg);
     }
-    
-    
 });
 
-
-Roo.bootstrap.CardUploader.ID = -1;/*
- * 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.
+/*
+ * - LGPL
  *
- * Fork - LGPL
- * <script type="text/javascript">
+ * trigger field - base class for combo..
+ * 
  */
-
-
 /**
- * @class Roo.data.SortTypes
- * @singleton
- * Defines the default sorting (casting?) comparison functions used when sorting data.
+ * @class Roo.bootstrap.TriggerField
+ * @extends Roo.bootstrap.Input
+ * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
+ * The trigger has no default action, so you must assign a function to implement the trigger click handler by
+ * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
+ * for which you can provide a custom implementation.  For example:
+ * <pre><code>
+var trigger = new Roo.bootstrap.TriggerField();
+trigger.onTriggerClick = myTriggerFn;
+trigger.applyTo('my-field');
+</code></pre>
+ *
+ * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
+ * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
+ * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
+ * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
+ * @cfg {String} caret (search|calendar) BS3 only - carat fa name
+
+ * @constructor
+ * Create a new TriggerField.
+ * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
+ * to the base TextField)
  */
-Roo.data.SortTypes = {
+Roo.bootstrap.TriggerField = function(config){
+    this.mimicing = false;
+    Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
     /**
-     * Default sort that does nothing
-     * @param {Mixed} s The value being converted
-     * @return {Mixed} The comparison value
+     * @cfg {String} triggerClass A CSS class to apply to the trigger
      */
-    none : function(s){
-        return s;
-    },
-    
-    /**
-     * The regular expression used to strip tags
-     * @type {RegExp}
-     * @property
+     /**
+     * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
      */
-    stripTagsRE : /<\/?[^>]+>/gi,
-    
+    hideTrigger:false,
+
     /**
-     * Strips all HTML tags to sort on text only
-     * @param {Mixed} s The value being converted
-     * @return {String} The comparison value
+     * @cfg {Boolean} removable (true|false) special filter default false
      */
-    asText : function(s){
-        return String(s).replace(this.stripTagsRE, "");
-    },
+    removable : false,
     
+    /** @cfg {Boolean} grow @hide */
+    /** @cfg {Number} growMin @hide */
+    /** @cfg {Number} growMax @hide */
+
     /**
-     * Strips all HTML tags to sort on text only - Case insensitive
-     * @param {Mixed} s The value being converted
-     * @return {String} The comparison value
+     * @hide 
+     * @method
      */
-    asUCText : function(s){
-        return String(s).toUpperCase().replace(this.stripTagsRE, "");
-    },
+    autoSize: Roo.emptyFn,
+    // private
+    monitorTab : true,
+    // private
+    deferHeight : true,
+
     
-    /**
-     * Case insensitive string
-     * @param {Mixed} s The value being converted
-     * @return {String} The comparison value
-     */
-    asUCString : function(s) {
-       return String(s).toUpperCase();
-    },
+    actionMode : 'wrap',
     
-    /**
-     * Date sorting
-     * @param {Mixed} s The value being converted
-     * @return {Number} The comparison value
-     */
-    asDate : function(s) {
-        if(!s){
-            return 0;
-        }
-        if(s instanceof Date){
-            return s.getTime();
-        }
-       return Date.parse(String(s));
-    },
+    caret : false,
     
-    /**
-     * Float sorting
-     * @param {Mixed} s The value being converted
-     * @return {Float} The comparison value
-     */
-    asFloat : function(s) {
-       var val = parseFloat(String(s).replace(/,/g, ""));
-        if(isNaN(val)) {
-            val = 0;
-        }
-       return val;
-    },
     
-    /**
-     * Integer sorting
-     * @param {Mixed} s The value being converted
-     * @return {Number} The comparison value
-     */
-    asInt : function(s) {
-        var val = parseInt(String(s).replace(/,/g, ""));
-        if(isNaN(val)) {
-            val = 0;
+    getAutoCreate : function(){
+       
+        var align = this.labelAlign || this.parentLabelAlign();
+        
+        var id = Roo.id();
+        
+        var cfg = {
+            cls: 'form-group' //input-group
+        };
+        
+        
+        var input =  {
+            tag: 'input',
+            id : id,
+            type : this.inputType,
+            cls : 'form-control',
+            autocomplete: 'new-password',
+            placeholder : this.placeholder || '' 
+            
+        };
+        if (this.name) {
+            input.name = this.name;
         }
-       return val;
-    }
-};/*
- * 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.data.Record
- * Instances of this class encapsulate both record <em>definition</em> information, and record
- * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
- * to access Records cached in an {@link Roo.data.Store} object.<br>
- * <p>
- * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
- * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
- * objects.<br>
- * <p>
- * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
- * @constructor
- * This constructor should not be used to create Record objects. Instead, use the constructor generated by
- * {@link #create}. The parameters are the same.
- * @param {Array} data An associative Array of data values keyed by the field name.
- * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
- * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
- * not specified an integer id is generated.
- */
-Roo.data.Record = function(data, id){
-    this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
-    this.data = data;
-};
-
-/**
- * Generate a constructor for a specific record layout.
- * @param {Array} o An Array of field definition objects which specify field names, and optionally,
- * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
- * Each field definition object may contain the following properties: <ul>
- * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
- * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
- * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
- * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
- * is being used, then this is a string containing the javascript expression to reference the data relative to 
- * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
- * to the data item relative to the record element. If the mapping expression is the same as the field name,
- * this may be omitted.</p></li>
- * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
- * <ul><li>auto (Default, implies no conversion)</li>
- * <li>string</li>
- * <li>int</li>
- * <li>float</li>
- * <li>boolean</li>
- * <li>date</li></ul></p></li>
- * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
- * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
- * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
- * by the Reader into an object that will be stored in the Record. It is passed the
- * following parameters:<ul>
- * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
- * </ul></p></li>
- * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
- * </ul>
- * <br>usage:<br><pre><code>
-var TopicRecord = Roo.data.Record.create(
-    {name: 'title', mapping: 'topic_title'},
-    {name: 'author', mapping: 'username'},
-    {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
-    {name: 'lastPost', mapping: 'post_time', type: 'date'},
-    {name: 'lastPoster', mapping: 'user2'},
-    {name: 'excerpt', mapping: 'post_text'}
-);
-
-var myNewRecord = new TopicRecord({
-    title: 'Do my job please',
-    author: 'noobie',
-    totalPosts: 1,
-    lastPost: new Date(),
-    lastPoster: 'Animal',
-    excerpt: 'No way dude!'
-});
-myStore.add(myNewRecord);
-</code></pre>
- * @method create
- * @static
- */
-Roo.data.Record.create = function(o){
-    var f = function(){
-        f.superclass.constructor.apply(this, arguments);
-    };
-    Roo.extend(f, Roo.data.Record);
-    var p = f.prototype;
-    p.fields = new Roo.util.MixedCollection(false, function(field){
-        return field.name;
-    });
-    for(var i = 0, len = o.length; i < len; i++){
-        p.fields.add(new Roo.data.Field(o[i]));
-    }
-    f.getField = function(name){
-        return p.fields.get(name);  
-    };
-    return f;
-};
-
-Roo.data.Record.AUTO_ID = 1000;
-Roo.data.Record.EDIT = 'edit';
-Roo.data.Record.REJECT = 'reject';
-Roo.data.Record.COMMIT = 'commit';
-
-Roo.data.Record.prototype = {
-    /**
-     * Readonly flag - true if this record has been modified.
-     * @type Boolean
-     */
-    dirty : false,
-    editing : false,
-    error: null,
-    modified: null,
-
-    // private
-    join : function(store){
-        this.store = store;
-    },
-
-    /**
-     * Set the named field to the specified value.
-     * @param {String} name The name of the field to set.
-     * @param {Object} value The value to set the field to.
-     */
-    set : function(name, value){
-        if(this.data[name] == value){
-            return;
-        }
-        this.dirty = true;
-        if(!this.modified){
-            this.modified = {};
-        }
-        if(typeof this.modified[name] == 'undefined'){
-            this.modified[name] = this.data[name];
+        if (this.size) {
+            input.cls += ' input-' + this.size;
         }
-        this.data[name] = value;
-        if(!this.editing && this.store){
-            this.store.afterEdit(this);
-        }       
-    },
-
-    /**
-     * Get the value of the named field.
-     * @param {String} name The name of the field to get the value of.
-     * @return {Object} The value of the field.
-     */
-    get : function(name){
-        return this.data[name]; 
-    },
-
-    // private
-    beginEdit : function(){
-        this.editing = true;
-        this.modified = {}; 
-    },
-
-    // private
-    cancelEdit : function(){
-        this.editing = false;
-        delete this.modified;
-    },
-
-    // private
-    endEdit : function(){
-        this.editing = false;
-        if(this.dirty && this.store){
-            this.store.afterEdit(this);
+        
+        if (this.disabled) {
+            input.disabled=true;
         }
-    },
-
-    /**
-     * Usually called by the {@link Roo.data.Store} which owns the Record.
-     * Rejects all changes made to the Record since either creation, or the last commit operation.
-     * Modified fields are reverted to their original values.
-     * <p>
-     * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
-     * of reject operations.
-     */
-    reject : function(){
-        var m = this.modified;
-        for(var n in m){
-            if(typeof m[n] != "function"){
-                this.data[n] = m[n];
+        
+        var inputblock = input;
+        
+        if(this.hasFeedback && !this.allowBlank){
+            
+            var feedback = {
+                tag: 'span',
+                cls: 'glyphicon form-control-feedback'
+            };
+            
+            if(this.removable && !this.editable  ){
+                inputblock = {
+                    cls : 'has-feedback',
+                    cn :  [
+                        inputblock,
+                        {
+                            tag: 'button',
+                            html : 'x',
+                            cls : 'roo-combo-removable-btn close'
+                        },
+                        feedback
+                    ] 
+                };
+            } else {
+                inputblock = {
+                    cls : 'has-feedback',
+                    cn :  [
+                        inputblock,
+                        feedback
+                    ] 
+                };
             }
-        }
-        this.dirty = false;
-        delete this.modified;
-        this.editing = false;
-        if(this.store){
-            this.store.afterReject(this);
-        }
-    },
 
-    /**
-     * Usually called by the {@link Roo.data.Store} which owns the Record.
-     * Commits all changes made to the Record since either creation, or the last commit operation.
-     * <p>
-     * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
-     * of commit operations.
-     */
-    commit : function(){
-        this.dirty = false;
-        delete this.modified;
-        this.editing = false;
-        if(this.store){
-            this.store.afterCommit(this);
+        } else {
+            if(this.removable && !this.editable ){
+                inputblock = {
+                    cls : 'roo-removable',
+                    cn :  [
+                        inputblock,
+                        {
+                            tag: 'button',
+                            html : 'x',
+                            cls : 'roo-combo-removable-btn close'
+                        }
+                    ] 
+                };
+            }
         }
-    },
-
-    // private
-    hasError : function(){
-        return this.error != null;
-    },
-
-    // private
-    clearError : function(){
-        this.error = null;
-    },
-
-    /**
-     * Creates a copy of this record.
-     * @param {String} id (optional) A new record id if you don't want to use this record's id
-     * @return {Record}
-     */
-    copy : function(newId) {
-        return new this.constructor(Roo.apply({}, this.data), newId || this.id);
-    }
-};/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
+        
+        if (this.before || this.after) {
+            
+            inputblock = {
+                cls : 'input-group',
+                cn :  [] 
+            };
+            if (this.before) {
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'input-group-addon input-group-prepend input-group-text',
+                    html : this.before
+                });
+            }
+            
+            inputblock.cn.push(input);
+            
+            if(this.hasFeedback && !this.allowBlank){
+                inputblock.cls += ' has-feedback';
+                inputblock.cn.push(feedback);
+            }
+            
+            if (this.after) {
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'input-group-addon input-group-append input-group-text',
+                    html : this.after
+                });
+            }
+            
+        };
+        
+      
+        
+        var ibwrap = inputblock;
+        
+        if(this.multiple){
+            ibwrap = {
+                tag: 'ul',
+                cls: 'roo-select2-choices',
+                cn:[
+                    {
+                        tag: 'li',
+                        cls: 'roo-select2-search-field',
+                        cn: [
 
+                            inputblock
+                        ]
+                    }
+                ]
+            };
+                
+        }
+        
+        var combobox = {
+            cls: 'roo-select2-container input-group',
+            cn: [
+                 {
+                    tag: 'input',
+                    type : 'hidden',
+                    cls: 'form-hidden-field'
+                },
+                ibwrap
+            ]
+        };
+        
+        if(!this.multiple && this.showToggleBtn){
+            
+            var caret = {
+                        tag: 'span',
+                        cls: 'caret'
+             };
+            if (this.caret != false) {
+                caret = {
+                     tag: 'i',
+                     cls: 'fa fa-' + this.caret
+                };
+                
+            }
+            
+            combobox.cn.push({
+                tag :'span',
+                cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
+                cn : [
+                    Roo.bootstrap.version == 3 ? caret : '',
+                    {
+                        tag: 'span',
+                        cls: 'combobox-clear',
+                        cn  : [
+                            {
+                                tag : 'i',
+                                cls: 'icon-remove'
+                            }
+                        ]
+                    }
+                ]
 
+            })
+        }
+        
+        if(this.multiple){
+            combobox.cls += ' roo-select2-container-multi';
+        }
+         var indicator = {
+            tag : 'i',
+            cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+            tooltip : 'This field is required'
+        };
+        if (Roo.bootstrap.version == 4) {
+            indicator = {
+                tag : 'i',
+                style : 'display:none'
+            };
+        }
+        
+        
+        if (align ==='left' && this.fieldLabel.length) {
+            
+            cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
 
-/**
- * @class Roo.data.Store
- * @extends Roo.util.Observable
- * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
- * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
- * <p>
- * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
- * has no knowledge of the format of the data returned by the Proxy.<br>
- * <p>
- * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
- * instances from the data object. These records are cached and made available through accessor functions.
- * @constructor
- * Creates a new Store.
- * @param {Object} config A config object containing the objects needed for the Store to access data,
- * and read the data into Records.
- */
-Roo.data.Store = function(config){
-    this.data = new Roo.util.MixedCollection(false);
-    this.data.getKey = function(o){
-        return o.id;
-    };
-    this.baseParams = {};
-    // private
-    this.paramNames = {
-        "start" : "start",
-        "limit" : "limit",
-        "sort" : "sort",
-        "dir" : "dir",
-        "multisort" : "_multisort"
-    };
+            cfg.cn = [
+                indicator,
+                {
+                    tag: 'label',
+                    'for' :  id,
+                    cls : 'control-label',
+                    html : this.fieldLabel
 
-    if(config && config.data){
-        this.inlineData = config.data;
-        delete config.data;
-    }
+                },
+                {
+                    cls : "", 
+                    cn: [
+                        combobox
+                    ]
+                }
 
-    Roo.apply(this, config);
-    
-    if(this.reader){ // reader passed
-        this.reader = Roo.factory(this.reader, Roo.data);
-        this.reader.xmodule = this.xmodule || false;
-        if(!this.recordType){
-            this.recordType = this.reader.recordType;
-        }
-        if(this.reader.onMetaChange){
-            this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
-        }
-    }
+            ];
+            
+            var labelCfg = cfg.cn[1];
+            var contentCfg = cfg.cn[2];
+            
+            if(this.indicatorpos == 'right'){
+                cfg.cn = [
+                    {
+                        tag: 'label',
+                        'for' :  id,
+                        cls : 'control-label',
+                        cn : [
+                            {
+                                tag : 'span',
+                                html : this.fieldLabel
+                            },
+                            indicator
+                        ]
+                    },
+                    {
+                        cls : "", 
+                        cn: [
+                            combobox
+                        ]
+                    }
 
-    if(this.recordType){
-        this.fields = this.recordType.prototype.fields;
-    }
-    this.modified = [];
+                ];
+                
+                labelCfg = cfg.cn[0];
+                contentCfg = cfg.cn[1];
+            }
+            
+            if(this.labelWidth > 12){
+                labelCfg.style = "width: " + this.labelWidth + 'px';
+            }
+            
+            if(this.labelWidth < 13 && this.labelmd == 0){
+                this.labelmd = this.labelWidth;
+            }
+            
+            if(this.labellg > 0){
+                labelCfg.cls += ' col-lg-' + this.labellg;
+                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+            }
+            
+            if(this.labelmd > 0){
+                labelCfg.cls += ' col-md-' + this.labelmd;
+                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+            }
+            
+            if(this.labelsm > 0){
+                labelCfg.cls += ' col-sm-' + this.labelsm;
+                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+            }
+            
+            if(this.labelxs > 0){
+                labelCfg.cls += ' col-xs-' + this.labelxs;
+                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+            }
+            
+        } else if ( this.fieldLabel.length) {
+//                Roo.log(" label");
+            cfg.cn = [
+                indicator,
+               {
+                   tag: 'label',
+                   //cls : 'input-group-addon',
+                   html : this.fieldLabel
 
-    this.addEvents({
-        /**
-         * @event datachanged
-         * Fires when the data cache has changed, and a widget which is using this Store
-         * as a Record cache should refresh its view.
-         * @param {Store} this
-         */
-        datachanged : true,
-        /**
-         * @event metachange
-         * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
-         * @param {Store} this
-         * @param {Object} meta The JSON metadata
-         */
-        metachange : true,
-        /**
-         * @event add
-         * Fires when Records have been added to the Store
-         * @param {Store} this
-         * @param {Roo.data.Record[]} records The array of Records added
-         * @param {Number} index The index at which the record(s) were added
-         */
-        add : true,
-        /**
-         * @event remove
-         * Fires when a Record has been removed from the Store
-         * @param {Store} this
-         * @param {Roo.data.Record} record The Record that was removed
-         * @param {Number} index The index at which the record was removed
-         */
-        remove : true,
-        /**
-         * @event update
-         * Fires when a Record has been updated
-         * @param {Store} this
-         * @param {Roo.data.Record} record The Record that was updated
-         * @param {String} operation The update operation being performed.  Value may be one of:
-         * <pre><code>
- Roo.data.Record.EDIT
- Roo.data.Record.REJECT
- Roo.data.Record.COMMIT
-         * </code></pre>
-         */
-        update : true,
-        /**
-         * @event clear
-         * Fires when the data cache has been cleared.
-         * @param {Store} this
-         */
-        clear : true,
-        /**
-         * @event beforeload
-         * Fires before a request is made for a new data object.  If the beforeload handler returns false
-         * the load action will be canceled.
-         * @param {Store} this
-         * @param {Object} options The loading options that were specified (see {@link #load} for details)
-         */
-        beforeload : true,
-        /**
-         * @event beforeloadadd
-         * Fires after a new set of Records has been loaded.
-         * @param {Store} this
-         * @param {Roo.data.Record[]} records The Records that were loaded
-         * @param {Object} options The loading options that were specified (see {@link #load} for details)
-         */
-        beforeloadadd : true,
-        /**
-         * @event load
-         * Fires after a new set of Records has been loaded, before they are added to the store.
-         * @param {Store} this
-         * @param {Roo.data.Record[]} records The Records that were loaded
-         * @param {Object} options The loading options that were specified (see {@link #load} for details)
-         * @params {Object} return from reader
-         */
-        load : true,
-        /**
-         * @event loadexception
-         * Fires if an exception occurs in the Proxy during loading.
-         * Called with the signature of the Proxy's "loadexception" event.
-         * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
-         * 
-         * @param {Proxy} 
-         * @param {Object} return from JsonData.reader() - success, totalRecords, records
-         * @param {Object} load options 
-         * @param {Object} jsonData from your request (normally this contains the Exception)
-         */
-        loadexception : true
-    });
-    
-    if(this.proxy){
-        this.proxy = Roo.factory(this.proxy, Roo.data);
-        this.proxy.xmodule = this.xmodule || false;
-        this.relayEvents(this.proxy,  ["loadexception"]);
-    }
-    this.sortToggle = {};
-    this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
+               },
 
-    Roo.data.Store.superclass.constructor.call(this);
+               combobox
 
-    if(this.inlineData){
-        this.loadData(this.inlineData);
-        delete this.inlineData;
-    }
-};
+            ];
+            
+            if(this.indicatorpos == 'right'){
+                
+                cfg.cn = [
+                    {
+                       tag: 'label',
+                       cn : [
+                           {
+                               tag : 'span',
+                               html : this.fieldLabel
+                           },
+                           indicator
+                       ]
 
-Roo.extend(Roo.data.Store, Roo.util.Observable, {
-     /**
-    * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
-    * without a remote query - used by combo/forms at present.
-    */
-    
-    /**
-    * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
-    */
-    /**
-    * @cfg {Array} data Inline data to be loaded when the store is initialized.
-    */
-    /**
-    * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
-    * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
-    */
-    /**
-    * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
-    * on any HTTP request
-    */
-    /**
-    * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
-    */
-    /**
-    * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
-    */
-    multiSort: false,
-    /**
-    * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
-    * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
-    */
-    remoteSort : false,
+                    },
+                    combobox
 
-    /**
-    * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
-     * loaded or when a record is removed. (defaults to false).
-    */
-    pruneModifiedRecords : false,
+                ];
 
-    // private
-    lastOptions : null,
+            }
 
-    /**
-     * Add Records to the Store and fires the add event.
-     * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
-     */
-    add : function(records){
-        records = [].concat(records);
-        for(var i = 0, len = records.length; i < len; i++){
-            records[i].join(this);
+        } else {
+            
+//                Roo.log(" no label && no align");
+                cfg = combobox
+                     
+                
         }
-        var index = this.data.length;
-        this.data.addAll(records);
-        this.fireEvent("add", this, records, index);
+        
+        var settings=this;
+        ['xs','sm','md','lg'].map(function(size){
+            if (settings[size]) {
+                cfg.cls += ' col-' + size + '-' + settings[size];
+            }
+        });
+        
+        return cfg;
+        
     },
-
-    /**
-     * Remove a Record from the Store and fires the remove event.
-     * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
-     */
-    remove : function(record){
-        var index = this.data.indexOf(record);
-        this.data.removeAt(index);
-        if(this.pruneModifiedRecords){
-            this.modified.remove(record);
-        }
-        this.fireEvent("remove", this, record, index);
+    
+    
+    
+    // private
+    onResize : function(w, h){
+//        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
+//        if(typeof w == 'number'){
+//            var x = w - this.trigger.getWidth();
+//            this.inputEl().setWidth(this.adjustWidth('input', x));
+//            this.trigger.setStyle('left', x+'px');
+//        }
     },
 
-    /**
-     * Remove all Records from the Store and fires the clear event.
-     */
-    removeAll : function(){
-        this.data.clear();
-        if(this.pruneModifiedRecords){
-            this.modified = [];
-        }
-        this.fireEvent("clear", this);
-    },
+    // private
+    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 
-    /**
-     * Inserts Records to the Store at the given index and fires the add event.
-     * @param {Number} index The start index at which to insert the passed Records.
-     * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
-     */
-    insert : function(index, records){
-        records = [].concat(records);
-        for(var i = 0, len = records.length; i < len; i++){
-            this.data.insert(index, records[i]);
-            records[i].join(this);
-        }
-        this.fireEvent("add", this, records, index);
+    // private
+    getResizeEl : function(){
+        return this.inputEl();
     },
 
-    /**
-     * Get the index within the cache of the passed Record.
-     * @param {Roo.data.Record} record The Roo.data.Record object to to find.
-     * @return {Number} The index of the passed Record. Returns -1 if not found.
-     */
-    indexOf : function(record){
-        return this.data.indexOf(record);
+    // private
+    getPositionEl : function(){
+        return this.inputEl();
     },
 
-    /**
-     * Get the index within the cache of the Record with the passed id.
-     * @param {String} id The id of the Record to find.
-     * @return {Number} The index of the Record. Returns -1 if not found.
-     */
-    indexOfId : function(id){
-        return this.data.indexOfKey(id);
-    },
-
-    /**
-     * Get the Record with the specified id.
-     * @param {String} id The id of the Record to find.
-     * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
-     */
-    getById : function(id){
-        return this.data.key(id);
-    },
-
-    /**
-     * Get the Record at the specified index.
-     * @param {Number} index The index of the Record to find.
-     * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
-     */
-    getAt : function(index){
-        return this.data.itemAt(index);
-    },
-
-    /**
-     * Returns a range of Records between specified indices.
-     * @param {Number} startIndex (optional) The starting index (defaults to 0)
-     * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
-     * @return {Roo.data.Record[]} An array of Records
-     */
-    getRange : function(start, end){
-        return this.data.getRange(start, end);
-    },
-
-    // private
-    storeOptions : function(o){
-        o = Roo.apply({}, o);
-        delete o.callback;
-        delete o.scope;
-        this.lastOptions = o;
-    },
-
-    /**
-     * Loads the Record cache from the configured Proxy using the configured Reader.
-     * <p>
-     * If using remote paging, then the first load call must specify the <em>start</em>
-     * and <em>limit</em> properties in the options.params property to establish the initial
-     * position within the dataset, and the number of Records to cache on each read from the Proxy.
-     * <p>
-     * <strong>It is important to note that for remote data sources, loading is asynchronous,
-     * and this call will return before the new data has been loaded. Perform any post-processing
-     * in a callback function, or in a "load" event handler.</strong>
-     * <p>
-     * @param {Object} options An object containing properties which control loading options:<ul>
-     * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
-     * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
-     * passed the following arguments:<ul>
-     * <li>r : Roo.data.Record[]</li>
-     * <li>options: Options object from the load call</li>
-     * <li>success: Boolean success indicator</li></ul></li>
-     * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
-     * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
-     * </ul>
-     */
-    load : function(options){
-        options = options || {};
-        if(this.fireEvent("beforeload", this, options) !== false){
-            this.storeOptions(options);
-            var p = Roo.apply(options.params || {}, this.baseParams);
-            // if meta was not loaded from remote source.. try requesting it.
-            if (!this.reader.metaFromRemote) {
-                p._requestMeta = 1;
-            }
-            if(this.sortInfo && this.remoteSort){
-                var pn = this.paramNames;
-                p[pn["sort"]] = this.sortInfo.field;
-                p[pn["dir"]] = this.sortInfo.direction;
-            }
-            if (this.multiSort) {
-                var pn = this.paramNames;
-                p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
-            }
-            
-            this.proxy.load(p, this.reader, this.loadRecords, this, options);
-        }
-    },
-
-    /**
-     * Reloads the Record cache from the configured Proxy using the configured Reader and
-     * the options from the last load operation performed.
-     * @param {Object} options (optional) An object containing properties which may override the options
-     * used in the last load operation. See {@link #load} for details (defaults to null, in which case
-     * the most recently used options are reused).
-     */
-    reload : function(options){
-        this.load(Roo.applyIf(options||{}, this.lastOptions));
+    // private
+    alignErrorIcon : function(){
+        this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
     },
 
     // private
-    // Called as a callback by the Reader during a load operation.
-    loadRecords : function(o, options, success){
-        if(!o || success === false){
-            if(success !== false){
-                this.fireEvent("load", this, [], options, o);
-            }
-            if(options.callback){
-                options.callback.call(options.scope || this, [], options, false);
-            }
-            return;
-        }
-        // if data returned failure - throw an exception.
-        if (o.success === false) {
-            // show a message if no listener is registered.
-            if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
-                    Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
-            }
-            // loadmask wil be hooked into this..
-            this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
-            return;
-        }
-        var r = o.records, t = o.totalRecords || r.length;
+    initEvents : function(){
         
-        this.fireEvent("beforeloadadd", this, r, options, o);
+        this.createList();
         
-        if(!options || options.add !== true){
-            if(this.pruneModifiedRecords){
-                this.modified = [];
-            }
-            for(var i = 0, len = r.length; i < len; i++){
-                r[i].join(this);
-            }
-            if(this.snapshot){
-                this.data = this.snapshot;
-                delete this.snapshot;
+        Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
+        //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
+        if(!this.multiple && this.showToggleBtn){
+            this.trigger = this.el.select('span.dropdown-toggle',true).first();
+            if(this.hideTrigger){
+                this.trigger.setDisplayed(false);
             }
-            this.data.clear();
-            this.data.addAll(r);
-            this.totalLength = t;
-            this.applySort();
-            this.fireEvent("datachanged", this);
-        }else{
-            this.totalLength = Math.max(t, this.data.length+r.length);
-            this.add(r);
+            this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
         }
         
-        if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
-                
-            var e = new Roo.data.Record({});
-
-            e.set(this.parent.displayField, this.parent.emptyTitle);
-            e.set(this.parent.valueField, '');
-
-            this.insert(0, e);
+        if(this.multiple){
+            this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
         }
+        
+        if(this.removable && !this.editable && !this.tickable){
+            var close = this.closeTriggerEl();
             
-        this.fireEvent("load", this, r, options, o);
-        if(options.callback){
-            options.callback.call(options.scope || this, r, options, true);
+            if(close){
+                close.setVisibilityMode(Roo.Element.DISPLAY).hide();
+                close.on('click', this.removeBtnClick, this, close);
+            }
         }
+        
+        //this.trigger.addClassOnOver('x-form-trigger-over');
+        //this.trigger.addClassOnClick('x-form-trigger-click');
+        
+        //if(!this.width){
+        //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+        //}
     },
-
-
-    /**
-     * Loads data from a passed data block. A Reader which understands the format of the data
-     * must have been configured in the constructor.
-     * @param {Object} data The data block from which to read the Records.  The format of the data expected
-     * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
-     * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
-     */
-    loadData : function(o, append){
-        var r = this.reader.readRecords(o);
-        this.loadRecords(r, {add: append}, true);
+    
+    closeTriggerEl : function()
+    {
+        var close = this.el.select('.roo-combo-removable-btn', true).first();
+        return close ? close : false;
     },
     
-     /**
-     * using 'cn' the nested child reader read the child array into it's child stores.
-     * @param {Object} rec The record with a 'children array
-     */
-    loadDataFromChildren : function(rec)
+    removeBtnClick : function(e, h, el)
     {
-        this.loadData(this.reader.toLoadData(rec));
+        e.preventDefault();
+        
+        if(this.fireEvent("remove", this) !== false){
+            this.reset();
+            this.fireEvent("afterremove", this)
+        }
     },
     
-
-    /**
-     * Gets the number of cached records.
-     * <p>
-     * <em>If using paging, this may not be the total size of the dataset. If the data object
-     * used by the Reader contains the dataset size, then the getTotalCount() function returns
-     * the data set size</em>
-     */
-    getCount : function(){
-        return this.data.length || 0;
+    createList : function()
+    {
+        this.list = Roo.get(document.body).createChild({
+            tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
+            cls: 'typeahead typeahead-long dropdown-menu shadow',
+            style: 'display:none'
+        });
+        
+        this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
+        
     },
 
-    /**
-     * Gets the total number of records in the dataset as returned by the server.
-     * <p>
-     * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
-     * the dataset size</em>
-     */
-    getTotalCount : function(){
-        return this.totalLength || 0;
+    // private
+    initTrigger : function(){
+       
     },
 
-    /**
-     * Returns the sort state of the Store as an object with two properties:
-     * <pre><code>
- field {String} The name of the field by which the Records are sorted
- direction {String} The sort order, "ASC" or "DESC"
-     * </code></pre>
-     */
-    getSortState : function(){
-        return this.sortInfo;
+    // private
+    onDestroy : function(){
+        if(this.trigger){
+            this.trigger.removeAllListeners();
+          //  this.trigger.remove();
+        }
+        //if(this.wrap){
+        //    this.wrap.remove();
+        //}
+        Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
     },
 
     // private
-    applySort : function(){
-        if(this.sortInfo && !this.remoteSort){
-            var s = this.sortInfo, f = s.field;
-            var st = this.fields.get(f).sortType;
-            var fn = function(r1, r2){
-                var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
-                return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
-            };
-            this.data.sort(s.direction, fn);
-            if(this.snapshot && this.snapshot != this.data){
-                this.snapshot.sort(s.direction, fn);
+    onFocus : function(){
+        Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
+        /*
+        if(!this.mimicing){
+            this.wrap.addClass('x-trigger-wrap-focus');
+            this.mimicing = true;
+            Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
+            if(this.monitorTab){
+                this.el.on("keydown", this.checkTab, this);
             }
         }
+        */
     },
 
-    /**
-     * Sets the default sort column and order to be used by the next load operation.
-     * @param {String} fieldName The name of the field to sort by.
-     * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
-     */
-    setDefaultSort : function(field, dir){
-        this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
-    },
-
-    /**
-     * Sort the Records.
-     * If remote sorting is used, the sort is performed on the server, and the cache is
-     * reloaded. If local sorting is used, the cache is sorted internally.
-     * @param {String} fieldName The name of the field to sort by.
-     * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
-     */
-    sort : function(fieldName, dir){
-        var f = this.fields.get(fieldName);
-        if(!dir){
-            this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
-            
-            if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
-                dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
-            }else{
-                dir = f.sortDir;
-            }
-        }
-        this.sortToggle[f.name] = dir;
-        this.sortInfo = {field: f.name, direction: dir};
-        if(!this.remoteSort){
-            this.applySort();
-            this.fireEvent("datachanged", this);
-        }else{
-            this.load(this.lastOptions);
+    // private
+    checkTab : function(e){
+        if(e.getKey() == e.TAB){
+            this.triggerBlur();
         }
     },
 
-    /**
-     * Calls the specified function for each of the Records in the cache.
-     * @param {Function} fn The function to call. The Record is passed as the first parameter.
-     * Returning <em>false</em> aborts and exits the iteration.
-     * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
-     */
-    each : function(fn, scope){
-        this.data.each(fn, scope);
-    },
-
-    /**
-     * Gets all records modified since the last commit.  Modified records are persisted across load operations
-     * (e.g., during paging).
-     * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
-     */
-    getModifiedRecords : function(){
-        return this.modified;
+    // private
+    onBlur : function(){
+        // do nothing
     },
 
     // private
-    createFilterFn : function(property, value, anyMatch){
-        if(!value.exec){ // not a regex
-            value = String(value);
-            if(value.length == 0){
-                return false;
-            }
-            value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
+    mimicBlur : function(e, t){
+        /*
+        if(!this.wrap.contains(t) && this.validateBlur()){
+            this.triggerBlur();
         }
-        return function(r){
-            return value.test(r.data[property]);
-        };
+        */
     },
 
-    /**
-     * Sums the value of <i>property</i> for each record between start and end and returns the result.
-     * @param {String} property A field on your records
-     * @param {Number} start The record index to start at (defaults to 0)
-     * @param {Number} end The last record index to include (defaults to length - 1)
-     * @return {Number} The sum
-     */
-    sum : function(property, start, end){
-        var rs = this.data.items, v = 0;
-        start = start || 0;
-        end = (end || end === 0) ? end : rs.length-1;
-
-        for(var i = start; i <= end; i++){
-            v += (rs[i].data[property] || 0);
+    // private
+    triggerBlur : function(){
+        this.mimicing = false;
+        Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
+        if(this.monitorTab){
+            this.el.un("keydown", this.checkTab, this);
         }
-        return v;
-    },
-
-    /**
-     * Filter the records by a specified property.
-     * @param {String} field A field on your records
-     * @param {String/RegExp} value Either a string that the field
-     * should start with or a RegExp to test against the field
-     * @param {Boolean} anyMatch True to match any part not just the beginning
-     */
-    filter : function(property, value, anyMatch){
-        var fn = this.createFilterFn(property, value, anyMatch);
-        return fn ? this.filterBy(fn) : this.clearFilter();
-    },
-
-    /**
-     * Filter by a function. The specified function will be called with each
-     * record in this data source. If the function returns true the record is included,
-     * otherwise it is filtered.
-     * @param {Function} fn The function to be called, it will receive 2 args (record, id)
-     * @param {Object} scope (optional) The scope of the function (defaults to this)
-     */
-    filterBy : function(fn, scope){
-        this.snapshot = this.snapshot || this.data;
-        this.data = this.queryBy(fn, scope||this);
-        this.fireEvent("datachanged", this);
-    },
-
-    /**
-     * Query the records by a specified property.
-     * @param {String} field A field on your records
-     * @param {String/RegExp} value Either a string that the field
-     * should start with or a RegExp to test against the field
-     * @param {Boolean} anyMatch True to match any part not just the beginning
-     * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
-     */
-    query : function(property, value, anyMatch){
-        var fn = this.createFilterFn(property, value, anyMatch);
-        return fn ? this.queryBy(fn) : this.data.clone();
+        //this.wrap.removeClass('x-trigger-wrap-focus');
+        Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
     },
 
-    /**
-     * Query by a function. The specified function will be called with each
-     * record in this data source. If the function returns true the record is included
-     * in the results.
-     * @param {Function} fn The function to be called, it will receive 2 args (record, id)
-     * @param {Object} scope (optional) The scope of the function (defaults to this)
-      @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
-     **/
-    queryBy : function(fn, scope){
-        var data = this.snapshot || this.data;
-        return data.filterBy(fn, scope||this);
+    // private
+    // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
+    validateBlur : function(e, t){
+        return true;
     },
 
-    /**
-     * Collects unique values for a particular dataIndex from this store.
-     * @param {String} dataIndex The property to collect
-     * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
-     * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
-     * @return {Array} An array of the unique values
-     **/
-    collect : function(dataIndex, allowNull, bypassFilter){
-        var d = (bypassFilter === true && this.snapshot) ?
-                this.snapshot.items : this.data.items;
-        var v, sv, r = [], l = {};
-        for(var i = 0, len = d.length; i < len; i++){
-            v = d[i].data[dataIndex];
-            sv = String(v);
-            if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
-                l[sv] = true;
-                r[r.length] = v;
-            }
-        }
-        return r;
+    // private
+    onDisable : function(){
+        this.inputEl().dom.disabled = true;
+        //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
+        //if(this.wrap){
+        //    this.wrap.addClass('x-item-disabled');
+        //}
     },
 
-    /**
-     * Revert to a view of the Record cache with no filtering applied.
-     * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
-     */
-    clearFilter : function(suppressEvent){
-        if(this.snapshot && this.snapshot != this.data){
-            this.data = this.snapshot;
-            delete this.snapshot;
-            if(suppressEvent !== true){
-                this.fireEvent("datachanged", this);
-            }
-        }
+    // private
+    onEnable : function(){
+        this.inputEl().dom.disabled = false;
+        //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
+        //if(this.wrap){
+        //    this.el.removeClass('x-item-disabled');
+        //}
     },
 
     // private
-    afterEdit : function(record){
-        if(this.modified.indexOf(record) == -1){
-            this.modified.push(record);
+    onShow : function(){
+        var ae = this.getActionEl();
+        
+        if(ae){
+            ae.dom.style.display = '';
+            ae.dom.style.visibility = 'visible';
         }
-        this.fireEvent("update", this, record, Roo.data.Record.EDIT);
-    },
-    
-    // private
-    afterReject : function(record){
-        this.modified.remove(record);
-        this.fireEvent("update", this, record, Roo.data.Record.REJECT);
     },
 
     // private
-    afterCommit : function(record){
-        this.modified.remove(record);
-        this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
+    
+    onHide : function(){
+        var ae = this.getActionEl();
+        ae.dom.style.display = 'none';
     },
 
     /**
-     * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
-     * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
+     * The function that should handle the trigger's click event.  This method does nothing by default until overridden
+     * by an implementing function.
+     * @method
+     * @param {EventObject} e
      */
-    commitChanges : function(){
-        var m = this.modified.slice(0);
-        this.modified = [];
-        for(var i = 0, len = m.length; i < len; i++){
-            m[i].commit();
-        }
-    },
-
-    /**
-     * Cancel outstanding changes on all changed records.
-     */
-    rejectChanges : function(){
-        var m = this.modified.slice(0);
-        this.modified = [];
-        for(var i = 0, len = m.length; i < len; i++){
-            m[i].reject();
-        }
-    },
-
-    onMetaChange : function(meta, rtype, o){
-        this.recordType = rtype;
-        this.fields = rtype.prototype.fields;
-        delete this.snapshot;
-        this.sortInfo = meta.sortInfo || this.sortInfo;
-        this.modified = [];
-        this.fireEvent('metachange', this, this.reader.meta);
-    },
-    
-    moveIndex : function(data, type)
-    {
-        var index = this.indexOf(data);
-        
-        var newIndex = index + type;
-        
-        this.remove(data);
-        
-        this.insert(newIndex, data);
-        
-    }
-});/*
- * 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">
- */
+    onTriggerClick : Roo.emptyFn
+});
+/*
+* Licence: LGPL
+*/
 
 /**
- * @class Roo.data.SimpleStore
- * @extends Roo.data.Store
- * Small helper class to make creating Stores from Array data easier.
- * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
- * @cfg {Array} fields An array of field definition objects, or field name strings.
- * @cfg {Object} an existing reader (eg. copied from another store)
- * @cfg {Array} data The multi-dimensional array of data
- * @constructor
- * @param {Object} config
- */
-Roo.data.SimpleStore = function(config)
-{
-    Roo.data.SimpleStore.superclass.constructor.call(this, {
-        isLocal : true,
-        reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
-                id: config.id
-            },
-            Roo.data.Record.create(config.fields)
-        ),
-        proxy : new Roo.data.MemoryProxy(config.data)
-    });
-    this.load();
-};
-Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
- * 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.bootstrap.CardUploader
+ * @extends Roo.bootstrap.Button
+ * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
+ * @cfg {Number} errorTimeout default 3000
+ * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
+ * @cfg {Array}  html The button text.
 
-/**
-/**
- * @extends Roo.data.Store
- * @class Roo.data.JsonStore
- * Small helper class to make creating Stores for JSON data easier. <br/>
-<pre><code>
-var store = new Roo.data.JsonStore({
-    url: 'get-images.php',
-    root: 'images',
-    fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
-});
-</code></pre>
- * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
- * JsonReader and HttpProxy (unless inline data is provided).</b>
- * @cfg {Array} fields An array of field definition objects, or field name strings.
- * @constructor
- * @param {Object} config
- */
-Roo.data.JsonStore = function(c){
-    Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
-        proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
-        reader: new Roo.data.JsonReader(c, c.fields)
-    }));
-};
-Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
- * 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">
+ * @constructor
+ * Create a new CardUploader
+ * @param {Object} config The config object
  */
 
+Roo.bootstrap.CardUploader = function(config){
+    
  
-Roo.data.Field = function(config){
-    if(typeof config == "string"){
-        config = {name: config};
-    }
-    Roo.apply(this, config);
     
-    if(!this.type){
-        this.type = "auto";
-    }
+    Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
     
-    var st = Roo.data.SortTypes;
-    // named sortTypes are supported, here we look them up
-    if(typeof this.sortType == "string"){
-        this.sortType = st[this.sortType];
-    }
     
-    // set default sortType for strings and dates
-    if(!this.sortType){
-        switch(this.type){
-            case "string":
-                this.sortType = st.asUCString;
-                break;
-            case "date":
-                this.sortType = st.asDate;
-                break;
-            default:
-                this.sortType = st.none;
-        }
-    }
-
-    // define once
-    var stripRe = /[\$,%]/g;
-
-    // prebuilt conversion function for this field, instead of
-    // switching every time we're reading a value
-    if(!this.convert){
-        var cv, dateFormat = this.dateFormat;
-        switch(this.type){
-            case "":
-            case "auto":
-            case undefined:
-                cv = function(v){ return v; };
-                break;
-            case "string":
-                cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
-                break;
-            case "int":
-                cv = function(v){
-                    return v !== undefined && v !== null && v !== '' ?
-                           parseInt(String(v).replace(stripRe, ""), 10) : '';
-                    };
-                break;
-            case "float":
-                cv = function(v){
-                    return v !== undefined && v !== null && v !== '' ?
-                           parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
-                    };
-                break;
-            case "bool":
-            case "boolean":
-                cv = function(v){ return v === true || v === "true" || v == 1; };
-                break;
-            case "date":
-                cv = function(v){
-                    if(!v){
-                        return '';
-                    }
-                    if(v instanceof Date){
-                        return v;
-                    }
-                    if(dateFormat){
-                        if(dateFormat == "timestamp"){
-                            return new Date(v*1000);
-                        }
-                        return Date.parseDate(v, dateFormat);
-                    }
-                    var parsed = Date.parse(v);
-                    return parsed ? new Date(parsed) : null;
-                };
-             break;
-            
-        }
-        this.convert = cv;
-    }
+    this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
+        return r.data.id
+     });
+    
+     this.addEvents({
+         // raw events
+        /**
+         * @event preview
+         * When a image is clicked on - and needs to display a slideshow or similar..
+         * @param {Roo.bootstrap.Card} this
+         * @param {Object} The image information data 
+         *
+         */
+        'preview' : true,
+         /**
+         * @event download
+         * When a the download link is clicked
+         * @param {Roo.bootstrap.Card} this
+         * @param {Object} The image information data  contains 
+         */
+        'download' : true
+        
+    });
 };
-
-Roo.data.Field.prototype = {
-    dateFormat: null,
-    defaultValue: "",
-    mapping: null,
-    sortType : null,
-    sortDir : "ASC"
-};/*
- * 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">
- */
  
-// Base class for reading structured data from a data source.  This class is intended to be
-// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
-
-/**
- * @class Roo.data.DataReader
- * Base class for reading structured data from a data source.  This class is intended to be
- * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
- */
-
-Roo.data.DataReader = function(meta, recordType){
+Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
     
-    this.meta = meta;
+     
+    errorTimeout : 3000,
+     
+    images : false,
+   
+    fileCollection : false,
+    allowBlank : true,
     
-    this.recordType = recordType instanceof Array ? 
-        Roo.data.Record.create(recordType) : recordType;
-};
-
-Roo.data.DataReader.prototype = {
-    
-    
-    readerType : 'Data',
-     /**
-     * Create an empty record
-     * @param {Object} data (optional) - overlay some values
-     * @return {Roo.data.Record} record created.
-     */
-    newRow :  function(d) {
-        var da =  {};
-        this.recordType.prototype.fields.each(function(c) {
-            switch( c.type) {
-                case 'int' : da[c.name] = 0; break;
-                case 'date' : da[c.name] = new Date(); break;
-                case 'float' : da[c.name] = 0.0; break;
-                case 'boolean' : da[c.name] = false; break;
-                default : da[c.name] = ""; break;
-            }
-            
-        });
-        return new this.recordType(Roo.apply(da, d));
-    }
-    
-    
-};/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
+    getAutoCreate : function()
+    {
+        
+        var cfg =  {
+            cls :'form-group' ,
+            cn : [
+               
+                {
+                    tag: 'label',
+                   //cls : 'input-group-addon',
+                    html : this.fieldLabel
 
-/**
- * @class Roo.data.DataProxy
- * @extends Roo.data.Observable
- * This class is an abstract base class for implementations which provide retrieval of
- * unformatted data objects.<br>
- * <p>
- * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
- * (of the appropriate type which knows how to parse the data object) to provide a block of
- * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
- * <p>
- * Custom implementations must implement the load method as described in
- * {@link Roo.data.HttpProxy#load}.
- */
-Roo.data.DataProxy = function(){
-    this.addEvents({
-        /**
-         * @event beforeload
-         * Fires before a network request is made to retrieve a data object.
-         * @param {Object} This DataProxy object.
-         * @param {Object} params The params parameter to the load function.
-         */
-        beforeload : true,
-        /**
-         * @event load
-         * Fires before the load method's callback is called.
-         * @param {Object} This DataProxy object.
-         * @param {Object} o The data object.
-         * @param {Object} arg The callback argument object passed to the load function.
-         */
-        load : true,
-        /**
-         * @event loadexception
-         * Fires if an Exception occurs during data retrieval.
-         * @param {Object} This DataProxy object.
-         * @param {Object} o The data object.
-         * @param {Object} arg The callback argument object passed to the load function.
-         * @param {Object} e The Exception.
-         */
-        loadexception : true
-    });
-    Roo.data.DataProxy.superclass.constructor.call(this);
-};
+                },
 
-Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
+                {
+                    tag: 'input',
+                    type : 'hidden',
+                    name : this.name,
+                    value : this.value,
+                    cls : 'd-none  form-control'
+                },
+                
+                {
+                    tag: 'input',
+                    multiple : 'multiple',
+                    type : 'file',
+                    cls : 'd-none  roo-card-upload-selector'
+                },
+                
+                {
+                    cls : 'roo-card-uploader-button-container w-100 mb-2'
+                },
+                {
+                    cls : 'card-columns roo-card-uploader-container'
+                }
 
-    /**
-     * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
-     */
-/*
- * 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.data.MemoryProxy
- * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
- * to the Reader when its load method is called.
- * @constructor
- * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
- */
-Roo.data.MemoryProxy = function(data){
-    if (data.data) {
-        data = data.data;
-    }
-    Roo.data.MemoryProxy.superclass.constructor.call(this);
-    this.data = data;
-};
+            ]
+        };
+           
+         
+        return cfg;
+    },
+    
+    getChildContainer : function() /// what children are added to.
+    {
+        return this.containerEl;
+    },
+   
+    getButtonContainer : function() /// what children are added to.
+    {
+        return this.el.select(".roo-card-uploader-button-container").first();
+    },
+   
+    initEvents : function()
+    {
+        
+        Roo.bootstrap.Input.prototype.initEvents.call(this);
+        
+        var t = this;
+        this.addxtype({
+            xns: Roo.bootstrap,
 
-Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+            xtype : 'Button',
+            container_method : 'getButtonContainer' ,            
+            html :  this.html, // fix changable?
+            cls : 'w-100 ',
+            listeners : {
+                'click' : function(btn, e) {
+                    t.onClick(e);
+                }
+            }
+        });
+        
+        
+        
+        
+        this.urlAPI = (window.createObjectURL && window) || 
+                                (window.URL && URL.revokeObjectURL && URL) || 
+                                (window.webkitURL && webkitURL);
+                        
+         
+         
+         
+        this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
+        
+        this.selectorEl.on('change', this.onFileSelected, this);
+        if (this.images) {
+            var t = this;
+            this.images.forEach(function(img) {
+                t.addCard(img)
+            });
+            this.images = false;
+        }
+        this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
+         
+       
+    },
     
-    /**
-     * Load data from the requested source (in this case an in-memory
-     * data object passed to the constructor), read the data object into
-     * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
-     * process that block using the passed callback.
-     * @param {Object} params This parameter is not used by the MemoryProxy class.
-     * @param {Roo.data.DataReader} reader The Reader object which converts the data
-     * object into a block of Roo.data.Records.
-     * @param {Function} callback The function into which to pass the block of Roo.data.records.
-     * The function must be passed <ul>
-     * <li>The Record block object</li>
-     * <li>The "arg" argument from the load function</li>
-     * <li>A boolean success indicator</li>
-     * </ul>
-     * @param {Object} scope The scope in which to call the callback
-     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
-     */
-    load : function(params, reader, callback, scope, arg){
-        params = params || {};
-        var result;
-        try {
-            result = reader.readRecords(params.data ? params.data :this.data);
-        }catch(e){
-            this.fireEvent("loadexception", this, arg, null, e);
-            callback.call(scope, null, arg, false);
+   
+    onClick : function(e)
+    {
+        e.preventDefault();
+         
+        this.selectorEl.dom.click();
+         
+    },
+    
+    onFileSelected : function(e)
+    {
+        e.preventDefault();
+        
+        if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
             return;
         }
-        callback.call(scope, result, arg, true);
+        
+        Roo.each(this.selectorEl.dom.files, function(file){    
+            this.addFile(file);
+        }, this);
+         
     },
     
-    // private
-    update : function(params, records){
+      
+    
+      
+    
+    addFile : function(file)
+    {
+           
+        if(typeof(file) === 'string'){
+            throw "Add file by name?"; // should not happen
+            return;
+        }
+        
+        if(!file || !this.urlAPI){
+            return;
+        }
+        
+        // file;
+        // file.type;
+        
+        var _this = this;
+        
+        
+        var url = _this.urlAPI.createObjectURL( file);
+           
+        this.addCard({
+            id : Roo.bootstrap.CardUploader.ID--,
+            is_uploaded : false,
+            src : url,
+            srcfile : file,
+            title : file.name,
+            mimetype : file.type,
+            preview : false,
+            is_deleted : 0
+        });
+        
+    },
+    
+    /**
+     * addCard - add an Attachment to the uploader
+     * @param data - the data about the image to upload
+     *
+     * {
+          id : 123
+          title : "Title of file",
+          is_uploaded : false,
+          src : "http://.....",
+          srcfile : { the File upload object },
+          mimetype : file.type,
+          preview : false,
+          is_deleted : 0
+          .. any other data...
+        }
+     *
+     * 
+    */
+    
+    addCard : function (data)
+    {
+        // hidden input element?
+        // if the file is not an image...
+        //then we need to use something other that and header_image
+        var t = this;
+        //   remove.....
+        var footer = [
+            {
+                xns : Roo.bootstrap,
+                xtype : 'CardFooter',
+                 items: [
+                    {
+                        xns : Roo.bootstrap,
+                        xtype : 'Element',
+                        cls : 'd-flex',
+                        items : [
+                            
+                            {
+                                xns : Roo.bootstrap,
+                                xtype : 'Button',
+                                html : String.format("<small>{0}</small>", data.title),
+                                cls : 'col-10 text-left',
+                                size: 'sm',
+                                weight: 'link',
+                                fa : 'download',
+                                listeners : {
+                                    click : function() {
+                                     
+                                        t.fireEvent( "download", t, data );
+                                    }
+                                }
+                            },
+                          
+                            {
+                                xns : Roo.bootstrap,
+                                xtype : 'Button',
+                                style: 'max-height: 28px; ',
+                                size : 'sm',
+                                weight: 'danger',
+                                cls : 'col-2',
+                                fa : 'times',
+                                listeners : {
+                                    click : function() {
+                                        t.removeCard(data.id)
+                                    }
+                                }
+                            }
+                        ]
+                    }
+                    
+                ] 
+            }
+            
+        ];
+        
+        var cn = this.addxtype(
+            {
+                 
+                xns : Roo.bootstrap,
+                xtype : 'Card',
+                closeable : true,
+                header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
+                header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
+                header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
+                data : data,
+                html : false,
+                 
+                items : footer,
+                initEvents : function() {
+                    Roo.bootstrap.Card.prototype.initEvents.call(this);
+                    var card = this;
+                    this.imgEl = this.el.select('.card-img-top').first();
+                    if (this.imgEl) {
+                        this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
+                        this.imgEl.set({ 'pointer' : 'cursor' });
+                                  
+                    }
+                    this.getCardFooter().addClass('p-1');
+                    
+                  
+                }
+                
+            }
+        );
+        // dont' really need ot update items.
+        // this.items.push(cn);
+        this.fileCollection.add(cn);
+        
+        if (!data.srcfile) {
+            this.updateInput();
+            return;
+        }
+            
+        var _t = this;
+        var reader = new FileReader();
+        reader.addEventListener("load", function() {  
+            data.srcdata =  reader.result;
+            _t.updateInput();
+        });
+        reader.readAsDataURL(data.srcfile);
+        
+        
+        
+    },
+    removeCard : function(id)
+    {
+        
+        var card  = this.fileCollection.get(id);
+        card.data.is_deleted = 1;
+        card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
+        //this.fileCollection.remove(card);
+        //this.items = this.items.filter(function(e) { return e != card });
+        // dont' really need ot update items.
+        card.el.dom.parentNode.removeChild(card.el.dom);
+        this.updateInput();
+
+        
+    },
+    reset: function()
+    {
+        this.fileCollection.each(function(card) {
+            if (card.el.dom && card.el.dom.parentNode) {
+                card.el.dom.parentNode.removeChild(card.el.dom);
+            }
+        });
+        this.fileCollection.clear();
+        this.updateInput();
+    },
+    
+    updateInput : function()
+    {
+         var data = [];
+        this.fileCollection.each(function(e) {
+            data.push(e.data);
+            
+        });
+        this.inputEl().dom.value = JSON.stringify(data);
+        
+        
         
     }
-});/*
+    
+    
+});
+
+
+Roo.bootstrap.CardUploader.ID = -1;/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -14655,146 +14251,98 @@ Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
  * Fork - LGPL
  * <script type="text/javascript">
  */
+
+
 /**
- * @class Roo.data.HttpProxy
- * @extends Roo.data.DataProxy
- * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
- * configured to reference a certain URL.<br><br>
- * <p>
- * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
- * from which the running page was served.<br><br>
- * <p>
- * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
- * <p>
- * Be aware that to enable the browser to parse an XML document, the server must set
- * the Content-Type header in the HTTP response to "text/xml".
- * @constructor
- * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
- * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
- * will be used to make the request.
+ * @class Roo.data.SortTypes
+ * @singleton
+ * Defines the default sorting (casting?) comparison functions used when sorting data.
  */
-Roo.data.HttpProxy = function(conn){
-    Roo.data.HttpProxy.superclass.constructor.call(this);
-    // is conn a conn config or a real conn?
-    this.conn = conn;
-    this.useAjax = !conn || !conn.events;
-  
-};
-
-Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
-    // thse are take from connection...
-    
-    /**
-     * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
-     */
+Roo.data.SortTypes = {
     /**
-     * @cfg {Object} extraParams (Optional) An object containing properties which are used as
-     * extra parameters to each request made by this object. (defaults to undefined)
+     * Default sort that does nothing
+     * @param {Mixed} s The value being converted
+     * @return {Mixed} The comparison value
      */
+    none : function(s){
+        return s;
+    },
+    
     /**
-     * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
-     *  to each request made by this object. (defaults to undefined)
+     * The regular expression used to strip tags
+     * @type {RegExp}
+     * @property
      */
+    stripTagsRE : /<\/?[^>]+>/gi,
+    
     /**
-     * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+     * Strips all HTML tags to sort on text only
+     * @param {Mixed} s The value being converted
+     * @return {String} The comparison value
      */
+    asText : function(s){
+        return String(s).replace(this.stripTagsRE, "");
+    },
+    
     /**
-     * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+     * Strips all HTML tags to sort on text only - Case insensitive
+     * @param {Mixed} s The value being converted
+     * @return {String} The comparison value
      */
-     /**
-     * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
-     * @type Boolean
-     */
-  
-
+    asUCText : function(s){
+        return String(s).toUpperCase().replace(this.stripTagsRE, "");
+    },
+    
     /**
-     * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
-     * @type Boolean
+     * Case insensitive string
+     * @param {Mixed} s The value being converted
+     * @return {String} The comparison value
      */
+    asUCString : function(s) {
+       return String(s).toUpperCase();
+    },
+    
     /**
-     * Return the {@link Roo.data.Connection} object being used by this Proxy.
-     * @return {Connection} The Connection object. This object may be used to subscribe to events on
-     * a finer-grained basis than the DataProxy events.
+     * Date sorting
+     * @param {Mixed} s The value being converted
+     * @return {Number} The comparison value
      */
-    getConnection : function(){
-        return this.useAjax ? Roo.Ajax : this.conn;
+    asDate : function(s) {
+        if(!s){
+            return 0;
+        }
+        if(s instanceof Date){
+            return s.getTime();
+        }
+       return Date.parse(String(s));
     },
-
+    
     /**
-     * Load data from the configured {@link Roo.data.Connection}, read the data object into
-     * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
-     * process that block using the passed callback.
-     * @param {Object} params An object containing properties which are to be used as HTTP parameters
-     * for the request to the remote server.
-     * @param {Roo.data.DataReader} reader The Reader object which converts the data
-     * object into a block of Roo.data.Records.
-     * @param {Function} callback The function into which to pass the block of Roo.data.Records.
-     * The function must be passed <ul>
-     * <li>The Record block object</li>
-     * <li>The "arg" argument from the load function</li>
-     * <li>A boolean success indicator</li>
-     * </ul>
-     * @param {Object} scope The scope in which to call the callback
-     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+     * Float sorting
+     * @param {Mixed} s The value being converted
+     * @return {Float} The comparison value
      */
-    load : function(params, reader, callback, scope, arg){
-        if(this.fireEvent("beforeload", this, params) !== false){
-            var  o = {
-                params : params || {},
-                request: {
-                    callback : callback,
-                    scope : scope,
-                    arg : arg
-                },
-                reader: reader,
-                callback : this.loadResponse,
-                scope: this
-            };
-            if(this.useAjax){
-                Roo.applyIf(o, this.conn);
-                if(this.activeRequest){
-                    Roo.Ajax.abort(this.activeRequest);
-                }
-                this.activeRequest = Roo.Ajax.request(o);
-            }else{
-                this.conn.request(o);
-            }
-        }else{
-            callback.call(scope||this, null, arg, false);
+    asFloat : function(s) {
+       var val = parseFloat(String(s).replace(/,/g, ""));
+        if(isNaN(val)) {
+            val = 0;
         }
+       return val;
     },
-
-    // private
-    loadResponse : function(o, success, response){
-        delete this.activeRequest;
-        if(!success){
-            this.fireEvent("loadexception", this, o, response);
-            o.request.callback.call(o.request.scope, null, o.request.arg, false);
-            return;
-        }
-        var result;
-        try {
-            result = o.reader.read(response);
-        }catch(e){
-            this.fireEvent("loadexception", this, o, response, e);
-            o.request.callback.call(o.request.scope, null, o.request.arg, false);
-            return;
+    
+    /**
+     * Integer sorting
+     * @param {Mixed} s The value being converted
+     * @return {Number} The comparison value
+     */
+    asInt : function(s) {
+        var val = parseInt(String(s).replace(/,/g, ""));
+        if(isNaN(val)) {
+            val = 0;
         }
-        
-        this.fireEvent("load", this, o, o.request.arg);
-        o.request.callback.call(o.request.scope, result, o.request.arg, true);
-    },
-
-    // private
-    update : function(dataSet){
-
-    },
-
-    // private
-    updateResponse : function(dataSet){
-
+       return val;
     }
-});/*
+};/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -14806,195 +14354,228 @@ Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
  */
 
 /**
- * @class Roo.data.ScriptTagProxy
- * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
- * other than the originating domain of the running page.<br><br>
- * <p>
- * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
- * of the running page, you must use this class, rather than DataProxy.</em><br><br>
- * <p>
- * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
- * source code that is used as the source inside a &lt;script> tag.<br><br>
+* @class Roo.data.Record
+ * Instances of this class encapsulate both record <em>definition</em> information, and record
+ * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
+ * to access Records cached in an {@link Roo.data.Store} object.<br>
  * <p>
- * In order for the browser to process the returned data, the server must wrap the data object
- * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
- * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
- * depending on whether the callback name was passed:
+ * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
+ * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
+ * objects.<br>
  * <p>
- * <pre><code>
-boolean scriptTag = false;
-String cb = request.getParameter("callback");
-if (cb != null) {
-    scriptTag = true;
-    response.setContentType("text/javascript");
-} else {
-    response.setContentType("application/x-json");
-}
-Writer out = response.getWriter();
-if (scriptTag) {
-    out.write(cb + "(");
-}
-out.print(dataBlock.toJsonString());
-if (scriptTag) {
-    out.write(");");
-}
-</pre></code>
- *
+ * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
  * @constructor
- * @param {Object} config A configuration object.
+ * This constructor should not be used to create Record objects. Instead, use the constructor generated by
+ * {@link #create}. The parameters are the same.
+ * @param {Array} data An associative Array of data values keyed by the field name.
+ * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
+ * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
+ * not specified an integer id is generated.
  */
-Roo.data.ScriptTagProxy = function(config){
-    Roo.data.ScriptTagProxy.superclass.constructor.call(this);
-    Roo.apply(this, config);
-    this.head = document.getElementsByTagName("head")[0];
+Roo.data.Record = function(data, id){
+    this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
+    this.data = data;
 };
 
-Roo.data.ScriptTagProxy.TRANS_ID = 1000;
-
-Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
-    /**
-     * @cfg {String} url The URL from which to request the data object.
-     */
-    /**
-     * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
-     */
-    timeout : 30000,
-    /**
-     * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
-     * the server the name of the callback function set up by the load call to process the returned data object.
-     * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
-     * javascript output which calls this named function passing the data object as its only parameter.
-     */
-    callbackParam : "callback",
-    /**
-     *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
-     * name to the request.
-     */
-    nocache : true,
+/**
+ * Generate a constructor for a specific record layout.
+ * @param {Array} o An Array of field definition objects which specify field names, and optionally,
+ * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
+ * Each field definition object may contain the following properties: <ul>
+ * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
+ * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
+ * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
+ * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
+ * is being used, then this is a string containing the javascript expression to reference the data relative to 
+ * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
+ * to the data item relative to the record element. If the mapping expression is the same as the field name,
+ * this may be omitted.</p></li>
+ * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
+ * <ul><li>auto (Default, implies no conversion)</li>
+ * <li>string</li>
+ * <li>int</li>
+ * <li>float</li>
+ * <li>boolean</li>
+ * <li>date</li></ul></p></li>
+ * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
+ * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
+ * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
+ * by the Reader into an object that will be stored in the Record. It is passed the
+ * following parameters:<ul>
+ * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
+ * </ul></p></li>
+ * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
+ * </ul>
+ * <br>usage:<br><pre><code>
+var TopicRecord = Roo.data.Record.create(
+    {name: 'title', mapping: 'topic_title'},
+    {name: 'author', mapping: 'username'},
+    {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
+    {name: 'lastPost', mapping: 'post_time', type: 'date'},
+    {name: 'lastPoster', mapping: 'user2'},
+    {name: 'excerpt', mapping: 'post_text'}
+);
 
-    /**
-     * Load data from the configured URL, read the data object into
-     * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
-     * process that block using the passed callback.
-     * @param {Object} params An object containing properties which are to be used as HTTP parameters
-     * for the request to the remote server.
-     * @param {Roo.data.DataReader} reader The Reader object which converts the data
-     * object into a block of Roo.data.Records.
-     * @param {Function} callback The function into which to pass the block of Roo.data.Records.
-     * The function must be passed <ul>
-     * <li>The Record block object</li>
-     * <li>The "arg" argument from the load function</li>
-     * <li>A boolean success indicator</li>
-     * </ul>
-     * @param {Object} scope The scope in which to call the callback
-     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
-     */
-    load : function(params, reader, callback, scope, arg){
-        if(this.fireEvent("beforeload", this, params) !== false){
-
-            var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
+var myNewRecord = new TopicRecord({
+    title: 'Do my job please',
+    author: 'noobie',
+    totalPosts: 1,
+    lastPost: new Date(),
+    lastPoster: 'Animal',
+    excerpt: 'No way dude!'
+});
+myStore.add(myNewRecord);
+</code></pre>
+ * @method create
+ * @static
+ */
+Roo.data.Record.create = function(o){
+    var f = function(){
+        f.superclass.constructor.apply(this, arguments);
+    };
+    Roo.extend(f, Roo.data.Record);
+    var p = f.prototype;
+    p.fields = new Roo.util.MixedCollection(false, function(field){
+        return field.name;
+    });
+    for(var i = 0, len = o.length; i < len; i++){
+        p.fields.add(new Roo.data.Field(o[i]));
+    }
+    f.getField = function(name){
+        return p.fields.get(name);  
+    };
+    return f;
+};
 
-            var url = this.url;
-            url += (url.indexOf("?") != -1 ? "&" : "?") + p;
-            if(this.nocache){
-                url += "&_dc=" + (new Date().getTime());
-            }
-            var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
-            var trans = {
-                id : transId,
-                cb : "stcCallback"+transId,
-                scriptId : "stcScript"+transId,
-                params : params,
-                arg : arg,
-                url : url,
-                callback : callback,
-                scope : scope,
-                reader : reader
-            };
-            var conn = this;
+Roo.data.Record.AUTO_ID = 1000;
+Roo.data.Record.EDIT = 'edit';
+Roo.data.Record.REJECT = 'reject';
+Roo.data.Record.COMMIT = 'commit';
 
-            window[trans.cb] = function(o){
-                conn.handleResponse(o, trans);
-            };
+Roo.data.Record.prototype = {
+    /**
+     * Readonly flag - true if this record has been modified.
+     * @type Boolean
+     */
+    dirty : false,
+    editing : false,
+    error: null,
+    modified: null,
 
-            url += String.format("&{0}={1}", this.callbackParam, trans.cb);
+    // private
+    join : function(store){
+        this.store = store;
+    },
 
-            if(this.autoAbort !== false){
-                this.abort();
-            }
+    /**
+     * Set the named field to the specified value.
+     * @param {String} name The name of the field to set.
+     * @param {Object} value The value to set the field to.
+     */
+    set : function(name, value){
+        if(this.data[name] == value){
+            return;
+        }
+        this.dirty = true;
+        if(!this.modified){
+            this.modified = {};
+        }
+        if(typeof this.modified[name] == 'undefined'){
+            this.modified[name] = this.data[name];
+        }
+        this.data[name] = value;
+        if(!this.editing && this.store){
+            this.store.afterEdit(this);
+        }       
+    },
 
-            trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
+    /**
+     * Get the value of the named field.
+     * @param {String} name The name of the field to get the value of.
+     * @return {Object} The value of the field.
+     */
+    get : function(name){
+        return this.data[name]; 
+    },
 
-            var script = document.createElement("script");
-            script.setAttribute("src", url);
-            script.setAttribute("type", "text/javascript");
-            script.setAttribute("id", trans.scriptId);
-            this.head.appendChild(script);
+    // private
+    beginEdit : function(){
+        this.editing = true;
+        this.modified = {}; 
+    },
 
-            this.trans = trans;
-        }else{
-            callback.call(scope||this, null, arg, false);
-        }
+    // private
+    cancelEdit : function(){
+        this.editing = false;
+        delete this.modified;
     },
 
     // private
-    isLoading : function(){
-        return this.trans ? true : false;
+    endEdit : function(){
+        this.editing = false;
+        if(this.dirty && this.store){
+            this.store.afterEdit(this);
+        }
     },
 
     /**
-     * Abort the current server request.
+     * Usually called by the {@link Roo.data.Store} which owns the Record.
+     * Rejects all changes made to the Record since either creation, or the last commit operation.
+     * Modified fields are reverted to their original values.
+     * <p>
+     * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
+     * of reject operations.
      */
-    abort : function(){
-        if(this.isLoading()){
-            this.destroyTrans(this.trans);
+    reject : function(){
+        var m = this.modified;
+        for(var n in m){
+            if(typeof m[n] != "function"){
+                this.data[n] = m[n];
+            }
+        }
+        this.dirty = false;
+        delete this.modified;
+        this.editing = false;
+        if(this.store){
+            this.store.afterReject(this);
         }
     },
 
-    // private
-    destroyTrans : function(trans, isLoaded){
-        this.head.removeChild(document.getElementById(trans.scriptId));
-        clearTimeout(trans.timeoutId);
-        if(isLoaded){
-            window[trans.cb] = undefined;
-            try{
-                delete window[trans.cb];
-            }catch(e){}
-        }else{
-            // if hasn't been loaded, wait for load to remove it to prevent script error
-            window[trans.cb] = function(){
-                window[trans.cb] = undefined;
-                try{
-                    delete window[trans.cb];
-                }catch(e){}
-            };
+    /**
+     * Usually called by the {@link Roo.data.Store} which owns the Record.
+     * Commits all changes made to the Record since either creation, or the last commit operation.
+     * <p>
+     * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
+     * of commit operations.
+     */
+    commit : function(){
+        this.dirty = false;
+        delete this.modified;
+        this.editing = false;
+        if(this.store){
+            this.store.afterCommit(this);
         }
     },
 
     // private
-    handleResponse : function(o, trans){
-        this.trans = false;
-        this.destroyTrans(trans, true);
-        var result;
-        try {
-            result = trans.reader.readRecords(o);
-        }catch(e){
-            this.fireEvent("loadexception", this, o, trans.arg, e);
-            trans.callback.call(trans.scope||window, null, trans.arg, false);
-            return;
-        }
-        this.fireEvent("load", this, o, trans.arg);
-        trans.callback.call(trans.scope||window, result, trans.arg, true);
+    hasError : function(){
+        return this.error != null;
     },
 
     // private
-    handleFailure : function(trans){
-        this.trans = false;
-        this.destroyTrans(trans, false);
-        this.fireEvent("loadexception", this, null, trans.arg);
-        trans.callback.call(trans.scope||window, null, trans.arg, false);
+    clearError : function(){
+        this.error = null;
+    },
+
+    /**
+     * Creates a copy of this record.
+     * @param {String} id (optional) A new record id if you don't want to use this record's id
+     * @return {Record}
+     */
+    copy : function(newId) {
+        return new this.constructor(Roo.apply({}, this.data), newId || this.id);
     }
-});/*
+};/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -15005,11594 +14586,12535 @@ Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
  * <script type="text/javascript">
  */
 
+
+
 /**
- * @class Roo.data.JsonReader
- * @extends Roo.data.DataReader
- * Data reader class to create an Array of Roo.data.Record objects from a JSON response
- * based on mappings in a provided Roo.data.Record constructor.
- * 
- * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
- * in the reply previously. 
- * 
+ * @class Roo.data.Store
+ * @extends Roo.util.Observable
+ * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
+ * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
  * <p>
- * Example code:
- * <pre><code>
-var RecordDef = Roo.data.Record.create([
-    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
-    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
-]);
-var myReader = new Roo.data.JsonReader({
-    totalProperty: "results",    // The property which contains the total dataset size (optional)
-    root: "rows",                // The property which contains an Array of row objects
-    id: "id"                     // The property within each row object that provides an ID for the record (optional)
-}, RecordDef);
-</code></pre>
+ * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
+ * has no knowledge of the format of the data returned by the Proxy.<br>
  * <p>
- * This would consume a JSON file like this:
- * <pre><code>
-{ 'results': 2, 'rows': [
-    { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
-    { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
-}
-</code></pre>
- * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
- * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
- * paged from the remote server.
- * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
- * @cfg {String} root name of the property which contains the Array of row objects.
- * @cfg {String} id Name of the property within a row object that contains a record identifier value.
- * @cfg {Array} fields Array of field definition objects
+ * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
+ * instances from the data object. These records are cached and made available through accessor functions.
  * @constructor
- * Create a new JsonReader
- * @param {Object} meta Metadata configuration options
- * @param {Object} recordType Either an Array of field definition objects,
- * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
+ * Creates a new Store.
+ * @param {Object} config A config object containing the objects needed for the Store to access data,
+ * and read the data into Records.
  */
-Roo.data.JsonReader = function(meta, recordType){
+Roo.data.Store = function(config){
+    this.data = new Roo.util.MixedCollection(false);
+    this.data.getKey = function(o){
+        return o.id;
+    };
+    this.baseParams = {};
+    // private
+    this.paramNames = {
+        "start" : "start",
+        "limit" : "limit",
+        "sort" : "sort",
+        "dir" : "dir",
+        "multisort" : "_multisort"
+    };
+
+    if(config && config.data){
+        this.inlineData = config.data;
+        delete config.data;
+    }
+
+    Roo.apply(this, config);
     
-    meta = meta || {};
-    // set some defaults:
-    Roo.applyIf(meta, {
-        totalProperty: 'total',
-        successProperty : 'success',
-        root : 'data',
-        id : 'id'
+    if(this.reader){ // reader passed
+        this.reader = Roo.factory(this.reader, Roo.data);
+        this.reader.xmodule = this.xmodule || false;
+        if(!this.recordType){
+            this.recordType = this.reader.recordType;
+        }
+        if(this.reader.onMetaChange){
+            this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
+        }
+    }
+
+    if(this.recordType){
+        this.fields = this.recordType.prototype.fields;
+    }
+    this.modified = [];
+
+    this.addEvents({
+        /**
+         * @event datachanged
+         * Fires when the data cache has changed, and a widget which is using this Store
+         * as a Record cache should refresh its view.
+         * @param {Store} this
+         */
+        datachanged : true,
+        /**
+         * @event metachange
+         * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
+         * @param {Store} this
+         * @param {Object} meta The JSON metadata
+         */
+        metachange : true,
+        /**
+         * @event add
+         * Fires when Records have been added to the Store
+         * @param {Store} this
+         * @param {Roo.data.Record[]} records The array of Records added
+         * @param {Number} index The index at which the record(s) were added
+         */
+        add : true,
+        /**
+         * @event remove
+         * Fires when a Record has been removed from the Store
+         * @param {Store} this
+         * @param {Roo.data.Record} record The Record that was removed
+         * @param {Number} index The index at which the record was removed
+         */
+        remove : true,
+        /**
+         * @event update
+         * Fires when a Record has been updated
+         * @param {Store} this
+         * @param {Roo.data.Record} record The Record that was updated
+         * @param {String} operation The update operation being performed.  Value may be one of:
+         * <pre><code>
+ Roo.data.Record.EDIT
+ Roo.data.Record.REJECT
+ Roo.data.Record.COMMIT
+         * </code></pre>
+         */
+        update : true,
+        /**
+         * @event clear
+         * Fires when the data cache has been cleared.
+         * @param {Store} this
+         */
+        clear : true,
+        /**
+         * @event beforeload
+         * Fires before a request is made for a new data object.  If the beforeload handler returns false
+         * the load action will be canceled.
+         * @param {Store} this
+         * @param {Object} options The loading options that were specified (see {@link #load} for details)
+         */
+        beforeload : true,
+        /**
+         * @event beforeloadadd
+         * Fires after a new set of Records has been loaded.
+         * @param {Store} this
+         * @param {Roo.data.Record[]} records The Records that were loaded
+         * @param {Object} options The loading options that were specified (see {@link #load} for details)
+         */
+        beforeloadadd : true,
+        /**
+         * @event load
+         * Fires after a new set of Records has been loaded, before they are added to the store.
+         * @param {Store} this
+         * @param {Roo.data.Record[]} records The Records that were loaded
+         * @param {Object} options The loading options that were specified (see {@link #load} for details)
+         * @params {Object} return from reader
+         */
+        load : true,
+        /**
+         * @event loadexception
+         * Fires if an exception occurs in the Proxy during loading.
+         * Called with the signature of the Proxy's "loadexception" event.
+         * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
+         * 
+         * @param {Proxy} 
+         * @param {Object} return from JsonData.reader() - success, totalRecords, records
+         * @param {Object} load options 
+         * @param {Object} jsonData from your request (normally this contains the Exception)
+         */
+        loadexception : true
     });
     
-    Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+    if(this.proxy){
+        this.proxy = Roo.factory(this.proxy, Roo.data);
+        this.proxy.xmodule = this.xmodule || false;
+        this.relayEvents(this.proxy,  ["loadexception"]);
+    }
+    this.sortToggle = {};
+    this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
+
+    Roo.data.Store.superclass.constructor.call(this);
+
+    if(this.inlineData){
+        this.loadData(this.inlineData);
+        delete this.inlineData;
+    }
 };
-Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
-    
-    readerType : 'Json',
+
+Roo.extend(Roo.data.Store, Roo.util.Observable, {
+     /**
+    * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
+    * without a remote query - used by combo/forms at present.
+    */
     
     /**
-     * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
-     * Used by Store query builder to append _requestMeta to params.
-     * 
-     */
-    metaFromRemote : false,
+    * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
+    */
     /**
-     * This method is only used by a DataProxy which has retrieved data from a remote server.
-     * @param {Object} response The XHR object which contains the JSON data in its responseText.
-     * @return {Object} data A data block which is used by an Roo.data.Store object as
-     * a cache of Roo.data.Records.
+    * @cfg {Array} data Inline data to be loaded when the store is initialized.
+    */
+    /**
+    * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
+    * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
+    */
+    /**
+    * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
+    * on any HTTP request
+    */
+    /**
+    * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
+    */
+    /**
+    * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
+    */
+    multiSort: false,
+    /**
+    * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
+    * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
+    */
+    remoteSort : false,
+
+    /**
+    * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
+     * loaded or when a record is removed. (defaults to false).
+    */
+    pruneModifiedRecords : false,
+
+    // private
+    lastOptions : null,
+
+    /**
+     * Add Records to the Store and fires the add event.
+     * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
      */
-    read : function(response){
-        var json = response.responseText;
-       
-        var o = /* eval:var:o */ eval("("+json+")");
-        if(!o) {
-            throw {message: "JsonReader.read: Json object not found"};
-        }
-        
-        if(o.metaData){
-            
-            delete this.ef;
-            this.metaFromRemote = true;
-            this.meta = o.metaData;
-            this.recordType = Roo.data.Record.create(o.metaData.fields);
-            this.onMetaChange(this.meta, this.recordType, o);
+    add : function(records){
+        records = [].concat(records);
+        for(var i = 0, len = records.length; i < len; i++){
+            records[i].join(this);
         }
-        return this.readRecords(o);
+        var index = this.data.length;
+        this.data.addAll(records);
+        this.fireEvent("add", this, records, index);
     },
 
-    // private function a store will implement
-    onMetaChange : function(meta, recordType, o){
-
+    /**
+     * Remove a Record from the Store and fires the remove event.
+     * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
+     */
+    remove : function(record){
+        var index = this.data.indexOf(record);
+        this.data.removeAt(index);
+        if(this.pruneModifiedRecords){
+            this.modified.remove(record);
+        }
+        this.fireEvent("remove", this, record, index);
     },
 
     /**
-        * @ignore
-        */
-    simpleAccess: function(obj, subsc) {
-       return obj[subsc];
+     * Remove all Records from the Store and fires the clear event.
+     */
+    removeAll : function(){
+        this.data.clear();
+        if(this.pruneModifiedRecords){
+            this.modified = [];
+        }
+        this.fireEvent("clear", this);
     },
 
-       /**
-        * @ignore
-        */
-    getJsonAccessor: function(){
-        var re = /[\[\.]/;
-        return function(expr) {
-            try {
-                return(re.test(expr))
-                    ? new Function("obj", "return obj." + expr)
-                    : function(obj){
-                        return obj[expr];
-                    };
-            } catch(e){}
-            return Roo.emptyFn;
-        };
-    }(),
+    /**
+     * Inserts Records to the Store at the given index and fires the add event.
+     * @param {Number} index The start index at which to insert the passed Records.
+     * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+     */
+    insert : function(index, records){
+        records = [].concat(records);
+        for(var i = 0, len = records.length; i < len; i++){
+            this.data.insert(index, records[i]);
+            records[i].join(this);
+        }
+        this.fireEvent("add", this, records, index);
+    },
 
     /**
-     * Create a data block containing Roo.data.Records from an XML document.
-     * @param {Object} o An object which contains an Array of row objects in the property specified
-     * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
-     * which contains the total size of the dataset.
-     * @return {Object} data A data block which is used by an Roo.data.Store object as
-     * a cache of Roo.data.Records.
+     * Get the index within the cache of the passed Record.
+     * @param {Roo.data.Record} record The Roo.data.Record object to to find.
+     * @return {Number} The index of the passed Record. Returns -1 if not found.
      */
-    readRecords : function(o){
-        /**
-         * After any data loads, the raw JSON data is available for further custom processing.
-         * @type Object
-         */
-        this.o = o;
-        var s = this.meta, Record = this.recordType,
-            f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
+    indexOf : function(record){
+        return this.data.indexOf(record);
+    },
 
-//      Generate extraction functions for the totalProperty, the root, the id, and for each field
-        if (!this.ef) {
-            if(s.totalProperty) {
-                   this.getTotal = this.getJsonAccessor(s.totalProperty);
-               }
-               if(s.successProperty) {
-                   this.getSuccess = this.getJsonAccessor(s.successProperty);
-               }
-               this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
-               if (s.id) {
-                       var g = this.getJsonAccessor(s.id);
-                       this.getId = function(rec) {
-                               var r = g(rec);  
-                               return (r === undefined || r === "") ? null : r;
-                       };
-               } else {
-                       this.getId = function(){return null;};
-               }
-            this.ef = [];
-            for(var jj = 0; jj < fl; jj++){
-                f = fi[jj];
-                var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
-                this.ef[jj] = this.getJsonAccessor(map);
+    /**
+     * Get the index within the cache of the Record with the passed id.
+     * @param {String} id The id of the Record to find.
+     * @return {Number} The index of the Record. Returns -1 if not found.
+     */
+    indexOfId : function(id){
+        return this.data.indexOfKey(id);
+    },
+
+    /**
+     * Get the Record with the specified id.
+     * @param {String} id The id of the Record to find.
+     * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
+     */
+    getById : function(id){
+        return this.data.key(id);
+    },
+
+    /**
+     * Get the Record at the specified index.
+     * @param {Number} index The index of the Record to find.
+     * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
+     */
+    getAt : function(index){
+        return this.data.itemAt(index);
+    },
+
+    /**
+     * Returns a range of Records between specified indices.
+     * @param {Number} startIndex (optional) The starting index (defaults to 0)
+     * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
+     * @return {Roo.data.Record[]} An array of Records
+     */
+    getRange : function(start, end){
+        return this.data.getRange(start, end);
+    },
+
+    // private
+    storeOptions : function(o){
+        o = Roo.apply({}, o);
+        delete o.callback;
+        delete o.scope;
+        this.lastOptions = o;
+    },
+
+    /**
+     * Loads the Record cache from the configured Proxy using the configured Reader.
+     * <p>
+     * If using remote paging, then the first load call must specify the <em>start</em>
+     * and <em>limit</em> properties in the options.params property to establish the initial
+     * position within the dataset, and the number of Records to cache on each read from the Proxy.
+     * <p>
+     * <strong>It is important to note that for remote data sources, loading is asynchronous,
+     * and this call will return before the new data has been loaded. Perform any post-processing
+     * in a callback function, or in a "load" event handler.</strong>
+     * <p>
+     * @param {Object} options An object containing properties which control loading options:<ul>
+     * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
+     * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
+     * passed the following arguments:<ul>
+     * <li>r : Roo.data.Record[]</li>
+     * <li>options: Options object from the load call</li>
+     * <li>success: Boolean success indicator</li></ul></li>
+     * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
+     * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
+     * </ul>
+     */
+    load : function(options){
+        options = options || {};
+        if(this.fireEvent("beforeload", this, options) !== false){
+            this.storeOptions(options);
+            var p = Roo.apply(options.params || {}, this.baseParams);
+            // if meta was not loaded from remote source.. try requesting it.
+            if (!this.reader.metaFromRemote) {
+                p._requestMeta = 1;
+            }
+            if(this.sortInfo && this.remoteSort){
+                var pn = this.paramNames;
+                p[pn["sort"]] = this.sortInfo.field;
+                p[pn["dir"]] = this.sortInfo.direction;
             }
+            if (this.multiSort) {
+                var pn = this.paramNames;
+                p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
+            }
+            
+            this.proxy.load(p, this.reader, this.loadRecords, this, options);
         }
+    },
 
-       var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
-       if(s.totalProperty){
-            var vt = parseInt(this.getTotal(o), 10);
-            if(!isNaN(vt)){
-                totalRecords = vt;
+    /**
+     * Reloads the Record cache from the configured Proxy using the configured Reader and
+     * the options from the last load operation performed.
+     * @param {Object} options (optional) An object containing properties which may override the options
+     * used in the last load operation. See {@link #load} for details (defaults to null, in which case
+     * the most recently used options are reused).
+     */
+    reload : function(options){
+        this.load(Roo.applyIf(options||{}, this.lastOptions));
+    },
+
+    // private
+    // Called as a callback by the Reader during a load operation.
+    loadRecords : function(o, options, success){
+        if(!o || success === false){
+            if(success !== false){
+                this.fireEvent("load", this, [], options, o);
+            }
+            if(options.callback){
+                options.callback.call(options.scope || this, [], options, false);
             }
+            return;
         }
-        if(s.successProperty){
-            var vs = this.getSuccess(o);
-            if(vs === false || vs === 'false'){
-                success = false;
+        // if data returned failure - throw an exception.
+        if (o.success === false) {
+            // show a message if no listener is registered.
+            if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
+                    Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
             }
+            // loadmask wil be hooked into this..
+            this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
+            return;
         }
-        var records = [];
-        for(var i = 0; i < c; i++){
-                var n = root[i];
-            var values = {};
-            var id = this.getId(n);
-            for(var j = 0; j < fl; j++){
-                f = fi[j];
-            var v = this.ef[j](n);
-            if (!f.convert) {
-                Roo.log('missing convert for ' + f.name);
-                Roo.log(f);
-                continue;
+        var r = o.records, t = o.totalRecords || r.length;
+        
+        this.fireEvent("beforeloadadd", this, r, options, o);
+        
+        if(!options || options.add !== true){
+            if(this.pruneModifiedRecords){
+                this.modified = [];
             }
-            values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+            for(var i = 0, len = r.length; i < len; i++){
+                r[i].join(this);
             }
-            var record = new Record(values, id);
-            record.json = n;
-            records[i] = record;
+            if(this.snapshot){
+                this.data = this.snapshot;
+                delete this.snapshot;
+            }
+            this.data.clear();
+            this.data.addAll(r);
+            this.totalLength = t;
+            this.applySort();
+            this.fireEvent("datachanged", this);
+        }else{
+            this.totalLength = Math.max(t, this.data.length+r.length);
+            this.add(r);
         }
-        return {
-            raw : o,
-            success : success,
-            records : records,
-            totalRecords : totalRecords
-        };
-    },
-    // used when loading children.. @see loadDataFromChildren
-    toLoadData: function(rec)
-    {
-       // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
-       var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
-       return { data : data, total : data.length };
-       
-    }
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
+        
+        if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
+                
+            var e = new Roo.data.Record({});
 
-/**
- * @class Roo.data.ArrayReader
- * @extends Roo.data.DataReader
- * Data reader class to create an Array of Roo.data.Record objects from an Array.
- * Each element of that Array represents a row of data fields. The
- * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
- * of the field definition if it exists, or the field's ordinal position in the definition.<br>
- * <p>
- * Example code:.
- * <pre><code>
-var RecordDef = Roo.data.Record.create([
-    {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
-    {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
-]);
-var myReader = new Roo.data.ArrayReader({
-    id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
-}, RecordDef);
-</code></pre>
- * <p>
- * This would consume an Array like this:
- * <pre><code>
-[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
-  </code></pre>
- * @constructor
- * Create a new JsonReader
- * @param {Object} meta Metadata configuration options.
- * @param {Object|Array} recordType Either an Array of field definition objects
- * 
- * @cfg {Array} fields Array of field definition objects
- * @cfg {String} id Name of the property within a row object that contains a record identifier value.
- * as specified to {@link Roo.data.Record#create},
- * or an {@link Roo.data.Record} object
- *
- * 
- * created using {@link Roo.data.Record#create}.
- */
-Roo.data.ArrayReader = function(meta, recordType)
-{    
-    Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
-};
+            e.set(this.parent.displayField, this.parent.emptyTitle);
+            e.set(this.parent.valueField, '');
 
-Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
-    
-      /**
-     * Create a data block containing Roo.data.Records from an XML document.
-     * @param {Object} o An Array of row objects which represents the dataset.
-     * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
-     * a cache of Roo.data.Records.
-     */
-    readRecords : function(o)
-    {
-        var sid = this.meta ? this.meta.id : null;
-        var recordType = this.recordType, fields = recordType.prototype.fields;
-        var records = [];
-        var root = o;
-        for(var i = 0; i < root.length; i++){
-            var n = root[i];
-            var values = {};
-            var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
-            for(var j = 0, jlen = fields.length; j < jlen; j++){
-                var f = fields.items[j];
-                var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
-                var v = n[k] !== undefined ? n[k] : f.defaultValue;
-                v = f.convert(v);
-                values[f.name] = v;
-            }
-            var record = new recordType(values, id);
-            record.json = n;
-            records[records.length] = record;
+            this.insert(0, e);
+        }
+            
+        this.fireEvent("load", this, r, options, o);
+        if(options.callback){
+            options.callback.call(options.scope || this, r, options, true);
         }
-        return {
-            records : records,
-            totalRecords : records.length
-        };
     },
-    // used when loading children.. @see loadDataFromChildren
-    toLoadData: function(rec)
-    {
-        // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
-        return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
-        
-    }
-    
-    
-});/*
- * - LGPL
- * * 
- */
 
-/**
- * @class Roo.bootstrap.ComboBox
- * @extends Roo.bootstrap.TriggerField
- * A combobox control with support for autocomplete, remote-loading, paging and many other features.
- * @cfg {Boolean} append (true|false) default false
- * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
- * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
- * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
- * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
- * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
- * @cfg {Boolean} animate default true
- * @cfg {Boolean} emptyResultText only for touch device
- * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
- * @cfg {String} emptyTitle default ''
- * @cfg {Number} width fixed with? experimental
- * @constructor
- * Create a new ComboBox.
- * @param {Object} config Configuration options
- */
-Roo.bootstrap.ComboBox = function(config){
-    Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
-    this.addEvents({
-        /**
-         * @event expand
-         * Fires when the dropdown list is expanded
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        */
-        'expand' : true,
-        /**
-         * @event collapse
-         * Fires when the dropdown list is collapsed
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        */
-        'collapse' : true,
-        /**
-         * @event beforeselect
-         * Fires before a list item is selected. Return false to cancel the selection.
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        * @param {Roo.data.Record} record The data record returned from the underlying store
-        * @param {Number} index The index of the selected item in the dropdown list
-        */
-        'beforeselect' : true,
-        /**
-         * @event select
-         * Fires when a list item is selected
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
-        * @param {Number} index The index of the selected item in the dropdown list
-        */
-        'select' : true,
-        /**
-         * @event beforequery
-         * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
-         * The event object passed has these properties:
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        * @param {String} query The query
-        * @param {Boolean} forceAll true to force "all" query
-        * @param {Boolean} cancel true to cancel the query
-        * @param {Object} e The query event object
-        */
-        'beforequery': true,
-         /**
-         * @event add
-         * Fires when the 'add' icon is pressed (add a listener to enable add button)
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        */
-        'add' : true,
-        /**
-         * @event edit
-         * Fires when the 'edit' icon is pressed (add a listener to enable add button)
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
-        */
-        'edit' : true,
-        /**
-         * @event remove
-         * Fires when the remove value from the combobox array
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        */
-        'remove' : true,
-        /**
-         * @event afterremove
-         * Fires when the remove value from the combobox array
-        * @param {Roo.bootstrap.ComboBox} combo This combo box
-        */
-        'afterremove' : true,
-        /**
-         * @event specialfilter
-         * Fires when specialfilter
-            * @param {Roo.bootstrap.ComboBox} combo This combo box
-            */
-        'specialfilter' : true,
-        /**
-         * @event tick
-         * Fires when tick the element
-            * @param {Roo.bootstrap.ComboBox} combo This combo box
-            */
-        'tick' : true,
-        /**
-         * @event touchviewdisplay
-         * Fires when touch view require special display (default is using displayField)
-            * @param {Roo.bootstrap.ComboBox} combo This combo box
-            * @param {Object} cfg set html .
-            */
-        'touchviewdisplay' : true
-        
-    });
-    
-    this.item = [];
-    this.tickItems = [];
-    
-    this.selectedIndex = -1;
-    if(this.mode == 'local'){
-        if(config.queryDelay === undefined){
-            this.queryDelay = 10;
-        }
-        if(config.minChars === undefined){
-            this.minChars = 0;
-        }
-    }
-};
 
-Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
-     
     /**
-     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
-     * rendering into an Roo.Editor, defaults to false)
+     * Loads data from a passed data block. A Reader which understands the format of the data
+     * must have been configured in the constructor.
+     * @param {Object} data The data block from which to read the Records.  The format of the data expected
+     * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
+     * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
      */
-    /**
-     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
-     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
+    loadData : function(o, append){
+        var r = this.reader.readRecords(o);
+        this.loadRecords(r, {add: append}, true);
+    },
+    
+     /**
+     * using 'cn' the nested child reader read the child array into it's child stores.
+     * @param {Object} rec The record with a 'children array
      */
+    loadDataFromChildren : function(rec)
+    {
+        this.loadData(this.reader.toLoadData(rec));
+    },
+    
+
     /**
-     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
-     */
-    /**
-     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
-     * the dropdown list (defaults to undefined, with no header element)
+     * Gets the number of cached records.
+     * <p>
+     * <em>If using paging, this may not be the total size of the dataset. If the data object
+     * used by the Reader contains the dataset size, then the getTotalCount() function returns
+     * the data set size</em>
      */
+    getCount : function(){
+        return this.data.length || 0;
+    },
 
-     /**
-     * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
-     */
-     
-     /**
-     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
-     */
-    listWidth: undefined,
-    /**
-     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
-     * mode = 'remote' or 'text' if mode = 'local')
-     */
-    displayField: undefined,
-    
-    /**
-     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
-     * mode = 'remote' or 'value' if mode = 'local'). 
-     * Note: use of a valueField requires the user make a selection
-     * in order for a value to be mapped.
-     */
-    valueField: undefined,
-    /**
-     * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
-     */
-    modalTitle : '',
-    
     /**
-     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
-     * field's data value (defaults to the underlying DOM element's name)
+     * Gets the total number of records in the dataset as returned by the server.
+     * <p>
+     * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
+     * the dataset size</em>
      */
-    hiddenName: undefined,
+    getTotalCount : function(){
+        return this.totalLength || 0;
+    },
+
     /**
-     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
+     * Returns the sort state of the Store as an object with two properties:
+     * <pre><code>
+ field {String} The name of the field by which the Records are sorted
+ direction {String} The sort order, "ASC" or "DESC"
+     * </code></pre>
      */
-    listClass: '',
+    getSortState : function(){
+        return this.sortInfo;
+    },
+
+    // private
+    applySort : function(){
+        if(this.sortInfo && !this.remoteSort){
+            var s = this.sortInfo, f = s.field;
+            var st = this.fields.get(f).sortType;
+            var fn = function(r1, r2){
+                var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
+                return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
+            };
+            this.data.sort(s.direction, fn);
+            if(this.snapshot && this.snapshot != this.data){
+                this.snapshot.sort(s.direction, fn);
+            }
+        }
+    },
+
     /**
-     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
+     * Sets the default sort column and order to be used by the next load operation.
+     * @param {String} fieldName The name of the field to sort by.
+     * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
      */
-    selectedClass: 'active',
-    
+    setDefaultSort : function(field, dir){
+        this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
+    },
+
     /**
-     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
+     * Sort the Records.
+     * If remote sorting is used, the sort is performed on the server, and the cache is
+     * reloaded. If local sorting is used, the cache is sorted internally.
+     * @param {String} fieldName The name of the field to sort by.
+     * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
      */
-    shadow:'sides',
+    sort : function(fieldName, dir){
+        var f = this.fields.get(fieldName);
+        if(!dir){
+            this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
+            
+            if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
+                dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
+            }else{
+                dir = f.sortDir;
+            }
+        }
+        this.sortToggle[f.name] = dir;
+        this.sortInfo = {field: f.name, direction: dir};
+        if(!this.remoteSort){
+            this.applySort();
+            this.fireEvent("datachanged", this);
+        }else{
+            this.load(this.lastOptions);
+        }
+    },
+
     /**
-     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
-     * anchor positions (defaults to 'tl-bl')
+     * Calls the specified function for each of the Records in the cache.
+     * @param {Function} fn The function to call. The Record is passed as the first parameter.
+     * Returning <em>false</em> aborts and exits the iteration.
+     * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
      */
-    listAlign: 'tl-bl?',
+    each : function(fn, scope){
+        this.data.each(fn, scope);
+    },
+
     /**
-     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
+     * Gets all records modified since the last commit.  Modified records are persisted across load operations
+     * (e.g., during paging).
+     * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
      */
-    maxHeight: 300,
+    getModifiedRecords : function(){
+        return this.modified;
+    },
+
+    // private
+    createFilterFn : function(property, value, anyMatch){
+        if(!value.exec){ // not a regex
+            value = String(value);
+            if(value.length == 0){
+                return false;
+            }
+            value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
+        }
+        return function(r){
+            return value.test(r.data[property]);
+        };
+    },
+
     /**
-     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
-     * query specified by the allQuery config option (defaults to 'query')
+     * Sums the value of <i>property</i> for each record between start and end and returns the result.
+     * @param {String} property A field on your records
+     * @param {Number} start The record index to start at (defaults to 0)
+     * @param {Number} end The last record index to include (defaults to length - 1)
+     * @return {Number} The sum
      */
-    triggerAction: 'query',
+    sum : function(property, start, end){
+        var rs = this.data.items, v = 0;
+        start = start || 0;
+        end = (end || end === 0) ? end : rs.length-1;
+
+        for(var i = start; i <= end; i++){
+            v += (rs[i].data[property] || 0);
+        }
+        return v;
+    },
+
     /**
-     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
-     * (defaults to 4, does not apply if editable = false)
+     * Filter the records by a specified property.
+     * @param {String} field A field on your records
+     * @param {String/RegExp} value Either a string that the field
+     * should start with or a RegExp to test against the field
+     * @param {Boolean} anyMatch True to match any part not just the beginning
      */
-    minChars : 4,
+    filter : function(property, value, anyMatch){
+        var fn = this.createFilterFn(property, value, anyMatch);
+        return fn ? this.filterBy(fn) : this.clearFilter();
+    },
+
     /**
-     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
-     * delay (typeAheadDelay) if it matches a known value (defaults to false)
+     * Filter by a function. The specified function will be called with each
+     * record in this data source. If the function returns true the record is included,
+     * otherwise it is filtered.
+     * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+     * @param {Object} scope (optional) The scope of the function (defaults to this)
      */
-    typeAhead: false,
+    filterBy : function(fn, scope){
+        this.snapshot = this.snapshot || this.data;
+        this.data = this.queryBy(fn, scope||this);
+        this.fireEvent("datachanged", this);
+    },
+
     /**
-     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
-     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
+     * Query the records by a specified property.
+     * @param {String} field A field on your records
+     * @param {String/RegExp} value Either a string that the field
+     * should start with or a RegExp to test against the field
+     * @param {Boolean} anyMatch True to match any part not just the beginning
+     * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
      */
-    queryDelay: 500,
+    query : function(property, value, anyMatch){
+        var fn = this.createFilterFn(property, value, anyMatch);
+        return fn ? this.queryBy(fn) : this.data.clone();
+    },
+
     /**
-     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
-     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
-     */
-    pageSize: 0,
+     * Query by a function. The specified function will be called with each
+     * record in this data source. If the function returns true the record is included
+     * in the results.
+     * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+     * @param {Object} scope (optional) The scope of the function (defaults to this)
+      @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
+     **/
+    queryBy : function(fn, scope){
+        var data = this.snapshot || this.data;
+        return data.filterBy(fn, scope||this);
+    },
+
     /**
-     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
-     * when editable = true (defaults to false)
-     */
-    selectOnFocus:false,
+     * Collects unique values for a particular dataIndex from this store.
+     * @param {String} dataIndex The property to collect
+     * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
+     * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
+     * @return {Array} An array of the unique values
+     **/
+    collect : function(dataIndex, allowNull, bypassFilter){
+        var d = (bypassFilter === true && this.snapshot) ?
+                this.snapshot.items : this.data.items;
+        var v, sv, r = [], l = {};
+        for(var i = 0, len = d.length; i < len; i++){
+            v = d[i].data[dataIndex];
+            sv = String(v);
+            if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
+                l[sv] = true;
+                r[r.length] = v;
+            }
+        }
+        return r;
+    },
+
     /**
-     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
+     * Revert to a view of the Record cache with no filtering applied.
+     * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
      */
-    queryParam: 'query',
-    /**
-     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
-     * when mode = 'remote' (defaults to 'Loading...')
-     */
-    loadingText: 'Loading...',
-    /**
-     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
-     */
-    resizable: false,
-    /**
-     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
-     */
-    handleHeight : 8,
-    /**
-     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
-     * traditional select (defaults to true)
-     */
-    editable: true,
-    /**
-     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
-     */
-    allQuery: '',
-    /**
-     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
-     */
-    mode: 'remote',
-    /**
-     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
-     * listWidth has a higher value)
-     */
-    minListWidth : 70,
-    /**
-     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
-     * allow the user to set arbitrary text into the field (defaults to false)
-     */
-    forceSelection:false,
-    /**
-     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
-     * if typeAhead = true (defaults to 250)
-     */
-    typeAheadDelay : 250,
-    /**
-     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
-     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
-     */
-    valueNotFoundText : undefined,
-    /**
-     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
-     */
-    blockFocus : false,
-    
-    /**
-     * @cfg {Boolean} disableClear Disable showing of clear button.
-     */
-    disableClear : false,
-    /**
-     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
-     */
-    alwaysQuery : false,
-    
-    /**
-     * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
-     */
-    multiple : false,
-    
-    /**
-     * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
-     */
-    invalidClass : "has-warning",
-    
-    /**
-     * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
-     */
-    validClass : "has-success",
-    
-    /**
-     * @cfg {Boolean} specialFilter (true|false) special filter default false
-     */
-    specialFilter : false,
-    
-    /**
-     * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
-     */
-    mobileTouchView : true,
+    clearFilter : function(suppressEvent){
+        if(this.snapshot && this.snapshot != this.data){
+            this.data = this.snapshot;
+            delete this.snapshot;
+            if(suppressEvent !== true){
+                this.fireEvent("datachanged", this);
+            }
+        }
+    },
+
+    // private
+    afterEdit : function(record){
+        if(this.modified.indexOf(record) == -1){
+            this.modified.push(record);
+        }
+        this.fireEvent("update", this, record, Roo.data.Record.EDIT);
+    },
     
+    // private
+    afterReject : function(record){
+        this.modified.remove(record);
+        this.fireEvent("update", this, record, Roo.data.Record.REJECT);
+    },
+
+    // private
+    afterCommit : function(record){
+        this.modified.remove(record);
+        this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
+    },
+
     /**
-     * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
+     * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
+     * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
      */
-    useNativeIOS : false,
-    
+    commitChanges : function(){
+        var m = this.modified.slice(0);
+        this.modified = [];
+        for(var i = 0, len = m.length; i < len; i++){
+            m[i].commit();
+        }
+    },
+
     /**
-     * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
+     * Cancel outstanding changes on all changed records.
      */
-    mobile_restrict_height : false,
-    
-    ios_options : false,
-    
-    //private
-    addicon : false,
-    editicon: false,
-    
-    page: 0,
-    hasQuery: false,
-    append: false,
-    loadNext: false,
-    autoFocus : true,
-    tickable : false,
-    btnPosition : 'right',
-    triggerList : true,
-    showToggleBtn : true,
-    animate : true,
-    emptyResultText: 'Empty',
-    triggerText : 'Select',
-    emptyTitle : '',
-    width : false,
-    
-    // element that contains real text value.. (when hidden is used..)
-    
-    getAutoCreate : function()
-    {   
-        var cfg = false;
-        //render
-        /*
-         * Render classic select for iso
-         */
-        
-        if(Roo.isIOS && this.useNativeIOS){
-            cfg = this.getAutoCreateNativeIOS();
-            return cfg;
-        }
-        
-        /*
-         * Touch Devices
-         */
-        
-        if(Roo.isTouch && this.mobileTouchView){
-            cfg = this.getAutoCreateTouchView();
-            return cfg;;
-        }
-        
-        /*
-         *  Normal ComboBox
-         */
-        if(!this.tickable){
-            cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
-            return cfg;
+    rejectChanges : function(){
+        var m = this.modified.slice(0);
+        this.modified = [];
+        for(var i = 0, len = m.length; i < len; i++){
+            m[i].reject();
         }
+    },
+
+    onMetaChange : function(meta, rtype, o){
+        this.recordType = rtype;
+        this.fields = rtype.prototype.fields;
+        delete this.snapshot;
+        this.sortInfo = meta.sortInfo || this.sortInfo;
+        this.modified = [];
+        this.fireEvent('metachange', this, this.reader.meta);
+    },
+    
+    moveIndex : function(data, type)
+    {
+        var index = this.indexOf(data);
         
-        /*
-         *  ComboBox with tickable selections
-         */
-             
-        var align = this.labelAlign || this.parentLabelAlign();
-        
-        cfg = {
-            cls : 'form-group roo-combobox-tickable' //input-group
-        };
-        
-        var btn_text_select = '';
-        var btn_text_done = '';
-        var btn_text_cancel = '';
-        
-        if (this.btn_text_show) {
-            btn_text_select = 'Select';
-            btn_text_done = 'Done';
-            btn_text_cancel = 'Cancel'; 
-        }
+        var newIndex = index + type;
         
-        var buttons = {
-            tag : 'div',
-            cls : 'tickable-buttons',
-            cn : [
-                {
-                    tag : 'button',
-                    type : 'button',
-                    cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
-                    //html : this.triggerText
-                    html: btn_text_select
-                },
-                {
-                    tag : 'button',
-                    type : 'button',
-                    name : 'ok',
-                    cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
-                    //html : 'Done'
-                    html: btn_text_done
-                },
-                {
-                    tag : 'button',
-                    type : 'button',
-                    name : 'cancel',
-                    cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
-                    //html : 'Cancel'
-                    html: btn_text_cancel
-                }
-            ]
-        };
+        this.remove(data);
         
-        if(this.editable){
-            buttons.cn.unshift({
-                tag: 'input',
-                cls: 'roo-select2-search-field-input'
-            });
-        }
+        this.insert(newIndex, data);
         
-        var _this = this;
-        
-        Roo.each(buttons.cn, function(c){
-            if (_this.size) {
-                c.cls += ' btn-' + _this.size;
-            }
+    }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
 
-            if (_this.disabled) {
-                c.disabled = true;
-            }
-        });
-        
-        var box = {
-            tag: 'div',
-            style : 'display: contents',
-            cn: [
-                {
-                    tag: 'input',
-                    type : 'hidden',
-                    cls: 'form-hidden-field'
-                },
-                {
-                    tag: 'ul',
-                    cls: 'roo-select2-choices',
-                    cn:[
-                        {
-                            tag: 'li',
-                            cls: 'roo-select2-search-field',
-                            cn: [
-                                buttons
-                            ]
-                        }
-                    ]
-                }
-            ]
-        };
-        
-        var combobox = {
-            cls: 'roo-select2-container input-group roo-select2-container-multi',
-            cn: [
-                
-                box
-//                {
-//                    tag: 'ul',
-//                    cls: 'typeahead typeahead-long dropdown-menu',
-//                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
-//                }
-            ]
-        };
-        
-        if(this.hasFeedback && !this.allowBlank){
-            
-            var feedback = {
-                tag: 'span',
-                cls: 'glyphicon form-control-feedback'
-            };
+/**
+ * @class Roo.data.SimpleStore
+ * @extends Roo.data.Store
+ * Small helper class to make creating Stores from Array data easier.
+ * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @cfg {Object} an existing reader (eg. copied from another store)
+ * @cfg {Array} data The multi-dimensional array of data
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.SimpleStore = function(config)
+{
+    Roo.data.SimpleStore.superclass.constructor.call(this, {
+        isLocal : true,
+        reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
+                id: config.id
+            },
+            Roo.data.Record.create(config.fields)
+        ),
+        proxy : new Roo.data.MemoryProxy(config.data)
+    });
+    this.load();
+};
+Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
+ * 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">
+ */
 
-            combobox.cn.push(feedback);
-        }
-        
-        
-        
-        var indicator = {
-            tag : 'i',
-            cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
-            tooltip : 'This field is required'
-        };
-        if (Roo.bootstrap.version == 4) {
-            indicator = {
-                tag : 'i',
-                style : 'display:none'
-            };
+/**
+/**
+ * @extends Roo.data.Store
+ * @class Roo.data.JsonStore
+ * Small helper class to make creating Stores for JSON data easier. <br/>
+<pre><code>
+var store = new Roo.data.JsonStore({
+    url: 'get-images.php',
+    root: 'images',
+    fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
+});
+</code></pre>
+ * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
+ * JsonReader and HttpProxy (unless inline data is provided).</b>
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.JsonStore = function(c){
+    Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
+        proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
+        reader: new Roo.data.JsonReader(c, c.fields)
+    }));
+};
+Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
+ * 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.data.Field = function(config){
+    if(typeof config == "string"){
+        config = {name: config};
+    }
+    Roo.apply(this, config);
+    
+    if(!this.type){
+        this.type = "auto";
+    }
+    
+    var st = Roo.data.SortTypes;
+    // named sortTypes are supported, here we look them up
+    if(typeof this.sortType == "string"){
+        this.sortType = st[this.sortType];
+    }
+    
+    // set default sortType for strings and dates
+    if(!this.sortType){
+        switch(this.type){
+            case "string":
+                this.sortType = st.asUCString;
+                break;
+            case "date":
+                this.sortType = st.asDate;
+                break;
+            default:
+                this.sortType = st.none;
         }
-        if (align ==='left' && this.fieldLabel.length) {
-            
-            cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
-            
-            cfg.cn = [
-                indicator,
-                {
-                    tag: 'label',
-                    'for' :  id,
-                    cls : 'control-label col-form-label',
-                    html : this.fieldLabel
+    }
 
-                },
-                {
-                    cls : "", 
-                    cn: [
-                        combobox
-                    ]
-                }
+    // define once
+    var stripRe = /[\$,%]/g;
 
-            ];
-            
-            var labelCfg = cfg.cn[1];
-            var contentCfg = cfg.cn[2];
+    // prebuilt conversion function for this field, instead of
+    // switching every time we're reading a value
+    if(!this.convert){
+        var cv, dateFormat = this.dateFormat;
+        switch(this.type){
+            case "":
+            case "auto":
+            case undefined:
+                cv = function(v){ return v; };
+                break;
+            case "string":
+                cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
+                break;
+            case "int":
+                cv = function(v){
+                    return v !== undefined && v !== null && v !== '' ?
+                           parseInt(String(v).replace(stripRe, ""), 10) : '';
+                    };
+                break;
+            case "float":
+                cv = function(v){
+                    return v !== undefined && v !== null && v !== '' ?
+                           parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
+                    };
+                break;
+            case "bool":
+            case "boolean":
+                cv = function(v){ return v === true || v === "true" || v == 1; };
+                break;
+            case "date":
+                cv = function(v){
+                    if(!v){
+                        return '';
+                    }
+                    if(v instanceof Date){
+                        return v;
+                    }
+                    if(dateFormat){
+                        if(dateFormat == "timestamp"){
+                            return new Date(v*1000);
+                        }
+                        return Date.parseDate(v, dateFormat);
+                    }
+                    var parsed = Date.parse(v);
+                    return parsed ? new Date(parsed) : null;
+                };
+             break;
             
+        }
+        this.convert = cv;
+    }
+};
 
-            if(this.indicatorpos == 'right'){
-                
-                cfg.cn = [
-                    {
-                        tag: 'label',
-                        'for' :  id,
-                        cls : 'control-label col-form-label',
-                        cn : [
-                            {
-                                tag : 'span',
-                                html : this.fieldLabel
-                            },
-                            indicator
-                        ]
-                    },
-                    {
-                        cls : "",
-                        cn: [
-                            combobox
-                        ]
-                    }
+Roo.data.Field.prototype = {
+    dateFormat: null,
+    defaultValue: "",
+    mapping: null,
+    sortType : null,
+    sortDir : "ASC"
+};/*
+ * 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">
+ */
+// Base class for reading structured data from a data source.  This class is intended to be
+// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
 
-                ];
-                
-                
-                
-                labelCfg = cfg.cn[0];
-                contentCfg = cfg.cn[1];
-            
-            }
-            
-            if(this.labelWidth > 12){
-                labelCfg.style = "width: " + this.labelWidth + 'px';
-            }
-            if(this.width * 1 > 0){
-                contentCfg.style = "width: " + this.width + 'px';
-            }
-            if(this.labelWidth < 13 && this.labelmd == 0){
-                this.labelmd = this.labelWidth;
-            }
-            
-            if(this.labellg > 0){
-                labelCfg.cls += ' col-lg-' + this.labellg;
-                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
-            }
-            
-            if(this.labelmd > 0){
-                labelCfg.cls += ' col-md-' + this.labelmd;
-                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
-            }
-            
-            if(this.labelsm > 0){
-                labelCfg.cls += ' col-sm-' + this.labelsm;
-                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
-            }
-            
-            if(this.labelxs > 0){
-                labelCfg.cls += ' col-xs-' + this.labelxs;
-                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
-            }
-                
-                
-        } else if ( this.fieldLabel.length) {
-//                Roo.log(" label");
-                 cfg.cn = [
-                   indicator,
-                    {
-                        tag: 'label',
-                        //cls : 'input-group-addon',
-                        html : this.fieldLabel
-                    },
-                    combobox
-                ];
-                
-                if(this.indicatorpos == 'right'){
-                    cfg.cn = [
-                        {
-                            tag: 'label',
-                            //cls : 'input-group-addon',
-                            html : this.fieldLabel
-                        },
-                        indicator,
-                        combobox
-                    ];
-                    
-                }
+/**
+ * @class Roo.data.DataReader
+ * Base class for reading structured data from a data source.  This class is intended to be
+ * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
+ */
 
-        } else {
-            
-//                Roo.log(" no label && no align");
-                cfg = combobox
-                     
-                
-        }
-         
-        var settings=this;
-        ['xs','sm','md','lg'].map(function(size){
-            if (settings[size]) {
-                cfg.cls += ' col-' + size + '-' + settings[size];
+Roo.data.DataReader = function(meta, recordType){
+    
+    this.meta = meta;
+    
+    this.recordType = recordType instanceof Array ? 
+        Roo.data.Record.create(recordType) : recordType;
+};
+
+Roo.data.DataReader.prototype = {
+    
+    
+    readerType : 'Data',
+     /**
+     * Create an empty record
+     * @param {Object} data (optional) - overlay some values
+     * @return {Roo.data.Record} record created.
+     */
+    newRow :  function(d) {
+        var da =  {};
+        this.recordType.prototype.fields.each(function(c) {
+            switch( c.type) {
+                case 'int' : da[c.name] = 0; break;
+                case 'date' : da[c.name] = new Date(); break;
+                case 'float' : da[c.name] = 0.0; break;
+                case 'boolean' : da[c.name] = false; break;
+                default : da[c.name] = ""; break;
             }
+            
         });
-        
-        return cfg;
-        
-    },
+        return new this.recordType(Roo.apply(da, d));
+    }
     
-    _initEventsCalled : false,
     
-    // private
-    initEvents: function()
-    {   
-        if (this._initEventsCalled) { // as we call render... prevent looping...
-            return;
-        }
-        this._initEventsCalled = true;
-        
-        if (!this.store) {
-            throw "can not find store for combo";
-        }
-        
-        this.indicator = this.indicatorEl();
-        
-        this.store = Roo.factory(this.store, Roo.data);
-        this.store.parent = this;
-        
-        // if we are building from html. then this element is so complex, that we can not really
-        // use the rendered HTML.
-        // so we have to trash and replace the previous code.
-        if (Roo.XComponent.build_from_html) {
-            // remove this element....
-            var e = this.el.dom, k=0;
-            while (e ) { e = e.previousSibling;  ++k;}
+};/*
+ * 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">
+ */
 
-            this.el.remove();
-            
-            this.el=false;
-            this.rendered = false;
-            
-            this.render(this.parent().getChildContainer(true), k);
-        }
-        
-        if(Roo.isIOS && this.useNativeIOS){
-            this.initIOSView();
-            return;
-        }
-        
-        /*
-         * Touch Devices
+/**
+ * @class Roo.data.DataProxy
+ * @extends Roo.data.Observable
+ * This class is an abstract base class for implementations which provide retrieval of
+ * unformatted data objects.<br>
+ * <p>
+ * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
+ * (of the appropriate type which knows how to parse the data object) to provide a block of
+ * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
+ * <p>
+ * Custom implementations must implement the load method as described in
+ * {@link Roo.data.HttpProxy#load}.
+ */
+Roo.data.DataProxy = function(){
+    this.addEvents({
+        /**
+         * @event beforeload
+         * Fires before a network request is made to retrieve a data object.
+         * @param {Object} This DataProxy object.
+         * @param {Object} params The params parameter to the load function.
          */
-        
-        if(Roo.isTouch && this.mobileTouchView){
-            this.initTouchView();
-            return;
-        }
-        
-        if(this.tickable){
-            this.initTickableEvents();
-            return;
-        }
-        
-        Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
-        
-        if(this.hiddenName){
-            
-            this.hiddenField = this.el.select('input.form-hidden-field',true).first();
-            
-            this.hiddenField.dom.value =
-                this.hiddenValue !== undefined ? this.hiddenValue :
-                this.value !== undefined ? this.value : '';
-
-            // prevent input submission
-            this.el.dom.removeAttribute('name');
-            this.hiddenField.dom.setAttribute('name', this.hiddenName);
-             
-             
-        }
-        //if(Roo.isGecko){
-        //    this.el.dom.setAttribute('autocomplete', 'off');
-        //}
-        
-        var cls = 'x-combo-list';
-        
-        //this.list = new Roo.Layer({
-        //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
-        //});
-        
-        var _this = this;
-        
-        (function(){
-            var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
-            _this.list.setWidth(lw);
-        }).defer(100);
-        
-        this.list.on('mouseover', this.onViewOver, this);
-        this.list.on('mousemove', this.onViewMove, this);
-        this.list.on('scroll', this.onViewScroll, this);
-        
-        /*
-        this.list.swallowEvent('mousewheel');
-        this.assetHeight = 0;
+        beforeload : true,
+        /**
+         * @event load
+         * Fires before the load method's callback is called.
+         * @param {Object} This DataProxy object.
+         * @param {Object} o The data object.
+         * @param {Object} arg The callback argument object passed to the load function.
+         */
+        load : true,
+        /**
+         * @event loadexception
+         * Fires if an Exception occurs during data retrieval.
+         * @param {Object} This DataProxy object.
+         * @param {Object} o The data object.
+         * @param {Object} arg The callback argument object passed to the load function.
+         * @param {Object} e The Exception.
+         */
+        loadexception : true
+    });
+    Roo.data.DataProxy.superclass.constructor.call(this);
+};
 
-        if(this.title){
-            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
-            this.assetHeight += this.header.getHeight();
-        }
+Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
 
-        this.innerList = this.list.createChild({cls:cls+'-inner'});
-        this.innerList.on('mouseover', this.onViewOver, this);
-        this.innerList.on('mousemove', this.onViewMove, this);
-        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
-        
-        if(this.allowBlank && !this.pageSize && !this.disableClear){
-            this.footer = this.list.createChild({cls:cls+'-ft'});
-            this.pageTb = new Roo.Toolbar(this.footer);
-           
-        }
-        if(this.pageSize){
-            this.footer = this.list.createChild({cls:cls+'-ft'});
-            this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
-                    {pageSize: this.pageSize});
-            
+    /**
+     * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
+     */
+/*
+ * 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.data.MemoryProxy
+ * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
+ * to the Reader when its load method is called.
+ * @constructor
+ * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
+ */
+Roo.data.MemoryProxy = function(data){
+    if (data.data) {
+        data = data.data;
+    }
+    Roo.data.MemoryProxy.superclass.constructor.call(this);
+    this.data = data;
+};
+
+Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+    
+    /**
+     * Load data from the requested source (in this case an in-memory
+     * data object passed to the constructor), read the data object into
+     * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+     * process that block using the passed callback.
+     * @param {Object} params This parameter is not used by the MemoryProxy class.
+     * @param {Roo.data.DataReader} reader The Reader object which converts the data
+     * object into a block of Roo.data.Records.
+     * @param {Function} callback The function into which to pass the block of Roo.data.records.
+     * The function must be passed <ul>
+     * <li>The Record block object</li>
+     * <li>The "arg" argument from the load function</li>
+     * <li>A boolean success indicator</li>
+     * </ul>
+     * @param {Object} scope The scope in which to call the callback
+     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+     */
+    load : function(params, reader, callback, scope, arg){
+        params = params || {};
+        var result;
+        try {
+            result = reader.readRecords(params.data ? params.data :this.data);
+        }catch(e){
+            this.fireEvent("loadexception", this, arg, null, e);
+            callback.call(scope, null, arg, false);
+            return;
         }
+        callback.call(scope, result, arg, true);
+    },
+    
+    // private
+    update : function(params, records){
         
-        if (this.pageTb && this.allowBlank && !this.disableClear) {
-            var _this = this;
-            this.pageTb.add(new Roo.Toolbar.Fill(), {
-                cls: 'x-btn-icon x-btn-clear',
-                text: '&#160;',
-                handler: function()
-                {
-                    _this.collapse();
-                    _this.clearValue();
-                    _this.onSelect(false, -1);
+    }
+});/*
+ * 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.data.HttpProxy
+ * @extends Roo.data.DataProxy
+ * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
+ * configured to reference a certain URL.<br><br>
+ * <p>
+ * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
+ * from which the running page was served.<br><br>
+ * <p>
+ * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
+ * <p>
+ * Be aware that to enable the browser to parse an XML document, the server must set
+ * the Content-Type header in the HTTP response to "text/xml".
+ * @constructor
+ * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
+ * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
+ * will be used to make the request.
+ */
+Roo.data.HttpProxy = function(conn){
+    Roo.data.HttpProxy.superclass.constructor.call(this);
+    // is conn a conn config or a real conn?
+    this.conn = conn;
+    this.useAjax = !conn || !conn.events;
+  
+};
+
+Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
+    // thse are take from connection...
+    
+    /**
+     * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
+     */
+    /**
+     * @cfg {Object} extraParams (Optional) An object containing properties which are used as
+     * extra parameters to each request made by this object. (defaults to undefined)
+     */
+    /**
+     * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
+     *  to each request made by this object. (defaults to undefined)
+     */
+    /**
+     * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+     */
+    /**
+     * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+     */
+     /**
+     * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
+     * @type Boolean
+     */
+  
+
+    /**
+     * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
+     * @type Boolean
+     */
+    /**
+     * Return the {@link Roo.data.Connection} object being used by this Proxy.
+     * @return {Connection} The Connection object. This object may be used to subscribe to events on
+     * a finer-grained basis than the DataProxy events.
+     */
+    getConnection : function(){
+        return this.useAjax ? Roo.Ajax : this.conn;
+    },
+
+    /**
+     * Load data from the configured {@link Roo.data.Connection}, read the data object into
+     * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
+     * process that block using the passed callback.
+     * @param {Object} params An object containing properties which are to be used as HTTP parameters
+     * for the request to the remote server.
+     * @param {Roo.data.DataReader} reader The Reader object which converts the data
+     * object into a block of Roo.data.Records.
+     * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+     * The function must be passed <ul>
+     * <li>The Record block object</li>
+     * <li>The "arg" argument from the load function</li>
+     * <li>A boolean success indicator</li>
+     * </ul>
+     * @param {Object} scope The scope in which to call the callback
+     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+     */
+    load : function(params, reader, callback, scope, arg){
+        if(this.fireEvent("beforeload", this, params) !== false){
+            var  o = {
+                params : params || {},
+                request: {
+                    callback : callback,
+                    scope : scope,
+                    arg : arg
+                },
+                reader: reader,
+                callback : this.loadResponse,
+                scope: this
+            };
+            if(this.useAjax){
+                Roo.applyIf(o, this.conn);
+                if(this.activeRequest){
+                    Roo.Ajax.abort(this.activeRequest);
                 }
-            });
-        }
-        if (this.footer) {
-            this.assetHeight += this.footer.getHeight();
-        }
-        */
-            
-        if(!this.tpl){
-            this.tpl = Roo.bootstrap.version == 4 ?
-                '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
-                '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
+                this.activeRequest = Roo.Ajax.request(o);
+            }else{
+                this.conn.request(o);
+            }
+        }else{
+            callback.call(scope||this, null, arg, false);
         }
+    },
 
-        this.view = new Roo.View(this.list, this.tpl, {
-            singleSelect:true, store: this.store, selectedClass: this.selectedClass
-        });
-        //this.view.wrapEl.setDisplayed(false);
-        this.view.on('click', this.onViewClick, this);
-        
-        
-        this.store.on('beforeload', this.onBeforeLoad, this);
-        this.store.on('load', this.onLoad, this);
-        this.store.on('loadexception', this.onLoadException, this);
-        /*
-        if(this.resizable){
-            this.resizer = new Roo.Resizable(this.list,  {
-               pinned:true, handles:'se'
-            });
-            this.resizer.on('resize', function(r, w, h){
-                this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
-                this.listWidth = w;
-                this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
-                this.restrictHeight();
-            }, this);
-            this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
-        }
-        */
-        if(!this.editable){
-            this.editable = true;
-            this.setEditable(false);
-        }
-        
-        /*
-        
-        if (typeof(this.events.add.listeners) != 'undefined') {
-            
-            this.addicon = this.wrap.createChild(
-                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
-       
-            this.addicon.on('click', function(e) {
-                this.fireEvent('add', this);
-            }, this);
+    // private
+    loadResponse : function(o, success, response){
+        delete this.activeRequest;
+        if(!success){
+            this.fireEvent("loadexception", this, o, response);
+            o.request.callback.call(o.request.scope, null, o.request.arg, false);
+            return;
         }
-        if (typeof(this.events.edit.listeners) != 'undefined') {
-            
-            this.editicon = this.wrap.createChild(
-                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
-            if (this.addicon) {
-                this.editicon.setStyle('margin-left', '40px');
-            }
-            this.editicon.on('click', function(e) {
-                
-                // we fire even  if inothing is selected..
-                this.fireEvent('edit', this, this.lastData );
-                
-            }, this);
+        var result;
+        try {
+            result = o.reader.read(response);
+        }catch(e){
+            this.fireEvent("loadexception", this, o, response, e);
+            o.request.callback.call(o.request.scope, null, o.request.arg, false);
+            return;
         }
-        */
         
-        this.keyNav = new Roo.KeyNav(this.inputEl(), {
-            "up" : function(e){
-                this.inKeyMode = true;
-                this.selectPrev();
-            },
+        this.fireEvent("load", this, o, o.request.arg);
+        o.request.callback.call(o.request.scope, result, o.request.arg, true);
+    },
 
-            "down" : function(e){
-                if(!this.isExpanded()){
-                    this.onTriggerClick();
-                }else{
-                    this.inKeyMode = true;
-                    this.selectNext();
-                }
-            },
+    // private
+    update : function(dataSet){
 
-            "enter" : function(e){
-//                this.onViewClick();
-                //return true;
-                this.collapse();
-                
-                if(this.fireEvent("specialkey", this, e)){
-                    this.onViewClick(false);
-                }
-                
-                return true;
-            },
+    },
 
-            "esc" : function(e){
-                this.collapse();
-            },
+    // private
+    updateResponse : function(dataSet){
 
-            "tab" : function(e){
-                this.collapse();
-                
-                if(this.fireEvent("specialkey", this, e)){
-                    this.onViewClick(false);
-                }
-                
-                return true;
-            },
+    }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
 
-            scope : this,
+/**
+ * @class Roo.data.ScriptTagProxy
+ * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
+ * other than the originating domain of the running page.<br><br>
+ * <p>
+ * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
+ * of the running page, you must use this class, rather than DataProxy.</em><br><br>
+ * <p>
+ * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
+ * source code that is used as the source inside a &lt;script> tag.<br><br>
+ * <p>
+ * In order for the browser to process the returned data, the server must wrap the data object
+ * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
+ * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
+ * depending on whether the callback name was passed:
+ * <p>
+ * <pre><code>
+boolean scriptTag = false;
+String cb = request.getParameter("callback");
+if (cb != null) {
+    scriptTag = true;
+    response.setContentType("text/javascript");
+} else {
+    response.setContentType("application/x-json");
+}
+Writer out = response.getWriter();
+if (scriptTag) {
+    out.write(cb + "(");
+}
+out.print(dataBlock.toJsonString());
+if (scriptTag) {
+    out.write(");");
+}
+</pre></code>
+ *
+ * @constructor
+ * @param {Object} config A configuration object.
+ */
+Roo.data.ScriptTagProxy = function(config){
+    Roo.data.ScriptTagProxy.superclass.constructor.call(this);
+    Roo.apply(this, config);
+    this.head = document.getElementsByTagName("head")[0];
+};
 
-            doRelay : function(foo, bar, hname){
-                if(hname == 'down' || this.scope.isExpanded()){
-                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
-                }
-                return true;
-            },
+Roo.data.ScriptTagProxy.TRANS_ID = 1000;
 
-            forceKeyDown: true
-        });
-        
-        
-        this.queryDelay = Math.max(this.queryDelay || 10,
-                this.mode == 'local' ? 10 : 250);
-        
-        
-        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
-        
-        if(this.typeAhead){
-            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
-        }
-        if(this.editable !== false){
-            this.inputEl().on("keyup", this.onKeyUp, this);
-        }
-        if(this.forceSelection){
-            this.inputEl().on('blur', this.doForce, this);
-        }
-        
-        if(this.multiple){
-            this.choices = this.el.select('ul.roo-select2-choices', true).first();
-            this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
-        }
-    },
-    
-    initTickableEvents: function()
-    {   
-        this.createList();
-        
-        if(this.hiddenName){
-            
-            this.hiddenField = this.el.select('input.form-hidden-field',true).first();
-            
-            this.hiddenField.dom.value =
-                this.hiddenValue !== undefined ? this.hiddenValue :
-                this.value !== undefined ? this.value : '';
+Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
+    /**
+     * @cfg {String} url The URL from which to request the data object.
+     */
+    /**
+     * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
+     */
+    timeout : 30000,
+    /**
+     * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
+     * the server the name of the callback function set up by the load call to process the returned data object.
+     * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
+     * javascript output which calls this named function passing the data object as its only parameter.
+     */
+    callbackParam : "callback",
+    /**
+     *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
+     * name to the request.
+     */
+    nocache : true,
 
-            // prevent input submission
-            this.el.dom.removeAttribute('name');
-            this.hiddenField.dom.setAttribute('name', this.hiddenName);
-             
-             
-        }
-        
-//        this.list = this.el.select('ul.dropdown-menu',true).first();
-        
-        this.choices = this.el.select('ul.roo-select2-choices', true).first();
-        this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
-        if(this.triggerList){
-            this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
-        }
-         
-        this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
-        this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
-        
-        this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
-        this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
-        
-        this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
-        this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
-        
-        this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
-        this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
-        this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
-        
-        this.okBtn.hide();
-        this.cancelBtn.hide();
-        
-        var _this = this;
-        
-        (function(){
-            var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
-            _this.list.setWidth(lw);
-        }).defer(100);
-        
-        this.list.on('mouseover', this.onViewOver, this);
-        this.list.on('mousemove', this.onViewMove, this);
-        
-        this.list.on('scroll', this.onViewScroll, this);
-        
-        if(!this.tpl){
-            this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
-                'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
-        }
-
-        this.view = new Roo.View(this.list, this.tpl, {
-            singleSelect:true,
-            tickable:true,
-            parent:this,
-            store: this.store,
-            selectedClass: this.selectedClass
-        });
-        
-        //this.view.wrapEl.setDisplayed(false);
-        this.view.on('click', this.onViewClick, this);
-        
-        
-        
-        this.store.on('beforeload', this.onBeforeLoad, this);
-        this.store.on('load', this.onLoad, this);
-        this.store.on('loadexception', this.onLoadException, this);
-        
-        if(this.editable){
-            this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
-                "up" : function(e){
-                    this.inKeyMode = true;
-                    this.selectPrev();
-                },
-
-                "down" : function(e){
-                    this.inKeyMode = true;
-                    this.selectNext();
-                },
+    /**
+     * Load data from the configured URL, read the data object into
+     * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+     * process that block using the passed callback.
+     * @param {Object} params An object containing properties which are to be used as HTTP parameters
+     * for the request to the remote server.
+     * @param {Roo.data.DataReader} reader The Reader object which converts the data
+     * object into a block of Roo.data.Records.
+     * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+     * The function must be passed <ul>
+     * <li>The Record block object</li>
+     * <li>The "arg" argument from the load function</li>
+     * <li>A boolean success indicator</li>
+     * </ul>
+     * @param {Object} scope The scope in which to call the callback
+     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+     */
+    load : function(params, reader, callback, scope, arg){
+        if(this.fireEvent("beforeload", this, params) !== false){
 
-                "enter" : function(e){
-                    if(this.fireEvent("specialkey", this, e)){
-                        this.onViewClick(false);
-                    }
-                    
-                    return true;
-                },
+            var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
 
-                "esc" : function(e){
-                    this.onTickableFooterButtonClick(e, false, false);
-                },
+            var url = this.url;
+            url += (url.indexOf("?") != -1 ? "&" : "?") + p;
+            if(this.nocache){
+                url += "&_dc=" + (new Date().getTime());
+            }
+            var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
+            var trans = {
+                id : transId,
+                cb : "stcCallback"+transId,
+                scriptId : "stcScript"+transId,
+                params : params,
+                arg : arg,
+                url : url,
+                callback : callback,
+                scope : scope,
+                reader : reader
+            };
+            var conn = this;
 
-                "tab" : function(e){
-                    this.fireEvent("specialkey", this, e);
-                    
-                    this.onTickableFooterButtonClick(e, false, false);
-                    
-                    return true;
-                },
+            window[trans.cb] = function(o){
+                conn.handleResponse(o, trans);
+            };
 
-                scope : this,
+            url += String.format("&{0}={1}", this.callbackParam, trans.cb);
 
-                doRelay : function(e, fn, key){
-                    if(this.scope.isExpanded()){
-                       return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
-                    }
-                    return true;
-                },
+            if(this.autoAbort !== false){
+                this.abort();
+            }
 
-                forceKeyDown: true
-            });
-        }
-        
-        this.queryDelay = Math.max(this.queryDelay || 10,
-                this.mode == 'local' ? 10 : 250);
-        
-        
-        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
-        
-        if(this.typeAhead){
-            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
-        }
-        
-        if(this.editable !== false){
-            this.tickableInputEl().on("keyup", this.onKeyUp, this);
-        }
-        
-        this.indicator = this.indicatorEl();
-        
-        if(this.indicator){
-            this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
-            this.indicator.hide();
-        }
-        
-    },
+            trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
 
-    onDestroy : function(){
-        if(this.view){
-            this.view.setStore(null);
-            this.view.el.removeAllListeners();
-            this.view.el.remove();
-            this.view.purgeListeners();
-        }
-        if(this.list){
-            this.list.dom.innerHTML  = '';
-        }
-        
-        if(this.store){
-            this.store.un('beforeload', this.onBeforeLoad, this);
-            this.store.un('load', this.onLoad, this);
-            this.store.un('loadexception', this.onLoadException, this);
-        }
-        Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
-    },
+            var script = document.createElement("script");
+            script.setAttribute("src", url);
+            script.setAttribute("type", "text/javascript");
+            script.setAttribute("id", trans.scriptId);
+            this.head.appendChild(script);
 
-    // private
-    fireKey : function(e){
-        if(e.isNavKeyPress() && !this.list.isVisible()){
-            this.fireEvent("specialkey", this, e);
+            this.trans = trans;
+        }else{
+            callback.call(scope||this, null, arg, false);
         }
     },
 
     // private
-    onResize: function(w, h)
-    {
-        
-        
-//        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
-//        
-//        if(typeof w != 'number'){
-//            // we do not handle it!?!?
-//            return;
-//        }
-//        var tw = this.trigger.getWidth();
-//       // tw += this.addicon ? this.addicon.getWidth() : 0;
-//       // tw += this.editicon ? this.editicon.getWidth() : 0;
-//        var x = w - tw;
-//        this.inputEl().setWidth( this.adjustWidth('input', x));
-//            
-//        //this.trigger.setStyle('left', x+'px');
-//        
-//        if(this.list && this.listWidth === undefined){
-//            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
-//            this.list.setWidth(lw);
-//            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
-//        }
-        
-    
-        
+    isLoading : function(){
+        return this.trans ? true : false;
     },
 
     /**
-     * Allow or prevent the user from directly editing the field text.  If false is passed,
-     * the user will only be able to select from the items defined in the dropdown list.  This method
-     * is the runtime equivalent of setting the 'editable' config option at config time.
-     * @param {Boolean} value True to allow the user to directly edit the field text
+     * Abort the current server request.
      */
-    setEditable : function(value){
-        if(value == this.editable){
-            return;
-        }
-        this.editable = value;
-        if(!value){
-            this.inputEl().dom.setAttribute('readOnly', true);
-            this.inputEl().on('mousedown', this.onTriggerClick,  this);
-            this.inputEl().addClass('x-combo-noedit');
-        }else{
-            this.inputEl().dom.removeAttribute('readOnly');
-            this.inputEl().un('mousedown', this.onTriggerClick,  this);
-            this.inputEl().removeClass('x-combo-noedit');
+    abort : function(){
+        if(this.isLoading()){
+            this.destroyTrans(this.trans);
         }
     },
 
     // private
-    
-    onBeforeLoad : function(combo,opts){
-        if(!this.hasFocus){
-            return;
+    destroyTrans : function(trans, isLoaded){
+        this.head.removeChild(document.getElementById(trans.scriptId));
+        clearTimeout(trans.timeoutId);
+        if(isLoaded){
+            window[trans.cb] = undefined;
+            try{
+                delete window[trans.cb];
+            }catch(e){}
+        }else{
+            // if hasn't been loaded, wait for load to remove it to prevent script error
+            window[trans.cb] = function(){
+                window[trans.cb] = undefined;
+                try{
+                    delete window[trans.cb];
+                }catch(e){}
+            };
         }
-         if (!opts.add) {
-            this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
-         }
-        this.restrictHeight();
-        this.selectedIndex = -1;
     },
 
     // private
-    onLoad : function(){
-        
-        this.hasQuery = false;
-        
-        if(!this.hasFocus){
-            return;
-        }
-        
-        if(typeof(this.loading) !== 'undefined' && this.loading !== null){
-            this.loading.hide();
-        }
-        
-        if(this.store.getCount() > 0){
-            
-            this.expand();
-            this.restrictHeight();
-            if(this.lastQuery == this.allQuery){
-                if(this.editable && !this.tickable){
-                    this.inputEl().dom.select();
-                }
-                
-                if(
-                    !this.selectByValue(this.value, true) &&
-                    this.autoFocus && 
-                    (
-                        !this.store.lastOptions ||
-                        typeof(this.store.lastOptions.add) == 'undefined' || 
-                        this.store.lastOptions.add != true
-                    )
-                ){
-                    this.select(0, true);
-                }
-            }else{
-                if(this.autoFocus){
-                    this.selectNext();
-                }
-                if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
-                    this.taTask.delay(this.typeAheadDelay);
-                }
-            }
-        }else{
-            this.onEmptyResults();
-        }
-        
-        //this.el.focus();
-    },
-    // private
-    onLoadException : function()
-    {
-        this.hasQuery = false;
-        
-        if(typeof(this.loading) !== 'undefined' && this.loading !== null){
-            this.loading.hide();
-        }
-        
-        if(this.tickable && this.editable){
+    handleResponse : function(o, trans){
+        this.trans = false;
+        this.destroyTrans(trans, true);
+        var result;
+        try {
+            result = trans.reader.readRecords(o);
+        }catch(e){
+            this.fireEvent("loadexception", this, o, trans.arg, e);
+            trans.callback.call(trans.scope||window, null, trans.arg, false);
             return;
         }
-        
-        this.collapse();
-        // only causes errors at present
-        //Roo.log(this.store.reader.jsonData);
-        //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
-            // fixme
-            //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
-        //}
-        
-        
-    },
-    // private
-    onTypeAhead : function(){
-        if(this.store.getCount() > 0){
-            var r = this.store.getAt(0);
-            var newValue = r.data[this.displayField];
-            var len = newValue.length;
-            var selStart = this.getRawValue().length;
-            
-            if(selStart != len){
-                this.setRawValue(newValue);
-                this.selectText(selStart, newValue.length);
-            }
-        }
+        this.fireEvent("load", this, o, trans.arg);
+        trans.callback.call(trans.scope||window, result, trans.arg, true);
     },
 
     // private
-    onSelect : function(record, index){
-        
-        if(this.fireEvent('beforeselect', this, record, index) !== false){
-        
-            this.setFromData(index > -1 ? record.data : false);
-            
-            this.collapse();
-            this.fireEvent('select', this, record, index);
-        }
-    },
+    handleFailure : function(trans){
+        this.trans = false;
+        this.destroyTrans(trans, false);
+        this.fireEvent("loadexception", this, null, trans.arg);
+        trans.callback.call(trans.scope||window, null, trans.arg, false);
+    }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
 
+/**
+ * @class Roo.data.JsonReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from a JSON response
+ * based on mappings in a provided Roo.data.Record constructor.
+ * 
+ * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
+ * in the reply previously. 
+ * 
+ * <p>
+ * Example code:
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
+    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
+]);
+var myReader = new Roo.data.JsonReader({
+    totalProperty: "results",    // The property which contains the total dataset size (optional)
+    root: "rows",                // The property which contains an Array of row objects
+    id: "id"                     // The property within each row object that provides an ID for the record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume a JSON file like this:
+ * <pre><code>
+{ 'results': 2, 'rows': [
+    { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
+    { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
+}
+</code></pre>
+ * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
+ * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
+ * paged from the remote server.
+ * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
+ * @cfg {String} root name of the property which contains the Array of row objects.
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * @cfg {Array} fields Array of field definition objects
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options
+ * @param {Object} recordType Either an Array of field definition objects,
+ * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
+ */
+Roo.data.JsonReader = function(meta, recordType){
+    
+    meta = meta || {};
+    // set some defaults:
+    Roo.applyIf(meta, {
+        totalProperty: 'total',
+        successProperty : 'success',
+        root : 'data',
+        id : 'id'
+    });
+    
+    Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
+    
+    readerType : 'Json',
+    
     /**
-     * Returns the currently selected field value or empty string if no value is set.
-     * @return {String} value The selected value
+     * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
+     * Used by Store query builder to append _requestMeta to params.
+     * 
      */
-    getValue : function()
-    {
-        if(Roo.isIOS && this.useNativeIOS){
-            return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
-        }
-        
-        if(this.multiple){
-            return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
+    metaFromRemote : false,
+    /**
+     * This method is only used by a DataProxy which has retrieved data from a remote server.
+     * @param {Object} response The XHR object which contains the JSON data in its responseText.
+     * @return {Object} data A data block which is used by an Roo.data.Store object as
+     * a cache of Roo.data.Records.
+     */
+    read : function(response){
+        var json = response.responseText;
+       
+        var o = /* eval:var:o */ eval("("+json+")");
+        if(!o) {
+            throw {message: "JsonReader.read: Json object not found"};
         }
         
-        if(this.valueField){
-            return typeof this.value != 'undefined' ? this.value : '';
-        }else{
-            return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
+        if(o.metaData){
+            
+            delete this.ef;
+            this.metaFromRemote = true;
+            this.meta = o.metaData;
+            this.recordType = Roo.data.Record.create(o.metaData.fields);
+            this.onMetaChange(this.meta, this.recordType, o);
         }
+        return this.readRecords(o);
     },
-    
-    getRawValue : function()
-    {
-        if(Roo.isIOS && this.useNativeIOS){
-            return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
-        }
-        
-        var v = this.inputEl().getValue();
-        
-        return v;
+
+    // private function a store will implement
+    onMetaChange : function(meta, recordType, o){
+
     },
 
     /**
-     * Clears any text/value currently set in the field
-     */
-    clearValue : function(){
-        
-        if(this.hiddenField){
-            this.hiddenField.dom.value = '';
-        }
-        this.value = '';
-        this.setRawValue('');
-        this.lastSelectionText = '';
-        this.lastData = false;
-        
-        var close = this.closeTriggerEl();
-        
-        if(close){
-            close.hide();
-        }
-        
-        this.validate();
-        
+        * @ignore
+        */
+    simpleAccess: function(obj, subsc) {
+       return obj[subsc];
     },
 
-    /**
-     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
-     * will be displayed in the field.  If the value does not match the data value of an existing item,
-     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
-     * Otherwise the field will be blank (although the value will still be set).
-     * @param {String} value The value to match
-     */
-    setValue : function(v)
-    {
-        if(Roo.isIOS && this.useNativeIOS){
-            this.setIOSValue(v);
-            return;
-        }
-        
-        if(this.multiple){
-            this.syncValue();
-            return;
+       /**
+        * @ignore
+        */
+    getJsonAccessor: function(){
+        var re = /[\[\.]/;
+        return function(expr) {
+            try {
+                return(re.test(expr))
+                    ? new Function("obj", "return obj." + expr)
+                    : function(obj){
+                        return obj[expr];
+                    };
+            } catch(e){}
+            return Roo.emptyFn;
+        };
+    }(),
+
+    /**
+     * Create a data block containing Roo.data.Records from an XML document.
+     * @param {Object} o An object which contains an Array of row objects in the property specified
+     * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
+     * which contains the total size of the dataset.
+     * @return {Object} data A data block which is used by an Roo.data.Store object as
+     * a cache of Roo.data.Records.
+     */
+    readRecords : function(o){
+        /**
+         * After any data loads, the raw JSON data is available for further custom processing.
+         * @type Object
+         */
+        this.o = o;
+        var s = this.meta, Record = this.recordType,
+            f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
+
+//      Generate extraction functions for the totalProperty, the root, the id, and for each field
+        if (!this.ef) {
+            if(s.totalProperty) {
+                   this.getTotal = this.getJsonAccessor(s.totalProperty);
+               }
+               if(s.successProperty) {
+                   this.getSuccess = this.getJsonAccessor(s.successProperty);
+               }
+               this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
+               if (s.id) {
+                       var g = this.getJsonAccessor(s.id);
+                       this.getId = function(rec) {
+                               var r = g(rec);  
+                               return (r === undefined || r === "") ? null : r;
+                       };
+               } else {
+                       this.getId = function(){return null;};
+               }
+            this.ef = [];
+            for(var jj = 0; jj < fl; jj++){
+                f = fi[jj];
+                var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
+                this.ef[jj] = this.getJsonAccessor(map);
+            }
         }
-        
-        var text = v;
-        if(this.valueField){
-            var r = this.findRecord(this.valueField, v);
-            if(r){
-                text = r.data[this.displayField];
-            }else if(this.valueNotFoundText !== undefined){
-                text = this.valueNotFoundText;
+
+       var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
+       if(s.totalProperty){
+            var vt = parseInt(this.getTotal(o), 10);
+            if(!isNaN(vt)){
+                totalRecords = vt;
             }
         }
-        this.lastSelectionText = text;
-        if(this.hiddenField){
-            this.hiddenField.dom.value = v;
+        if(s.successProperty){
+            var vs = this.getSuccess(o);
+            if(vs === false || vs === 'false'){
+                success = false;
+            }
         }
-        Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
-        this.value = v;
-        
-        var close = this.closeTriggerEl();
-        
-        if(close){
-            (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
+        var records = [];
+        for(var i = 0; i < c; i++){
+                var n = root[i];
+            var values = {};
+            var id = this.getId(n);
+            for(var j = 0; j < fl; j++){
+                f = fi[j];
+            var v = this.ef[j](n);
+            if (!f.convert) {
+                Roo.log('missing convert for ' + f.name);
+                Roo.log(f);
+                continue;
+            }
+            values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+            }
+            var record = new Record(values, id);
+            record.json = n;
+            records[i] = record;
         }
-        
-        this.validate();
+        return {
+            raw : o,
+            success : success,
+            records : records,
+            totalRecords : totalRecords
+        };
     },
-    /**
-     * @property {Object} the last set data for the element
-     */
+    // used when loading children.. @see loadDataFromChildren
+    toLoadData: function(rec)
+    {
+       // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+       var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
+       return { data : data, total : data.length };
+       
+    }
+});/*
+ * 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.data.ArrayReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from an Array.
+ * Each element of that Array represents a row of data fields. The
+ * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
+ * of the field definition if it exists, or the field's ordinal position in the definition.<br>
+ * <p>
+ * Example code:.
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+    {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
+    {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
+]);
+var myReader = new Roo.data.ArrayReader({
+    id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume an Array like this:
+ * <pre><code>
+[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
+  </code></pre>
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options.
+ * @param {Object|Array} recordType Either an Array of field definition objects
+ * 
+ * @cfg {Array} fields Array of field definition objects
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * as specified to {@link Roo.data.Record#create},
+ * or an {@link Roo.data.Record} object
+ *
+ * 
+ * created using {@link Roo.data.Record#create}.
+ */
+Roo.data.ArrayReader = function(meta, recordType)
+{    
+    Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+
+Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
     
-    lastData : false,
-    /**
-     * Sets the value of the field based on a object which is related to the record format for the store.
-     * @param {Object} value the value to set as. or false on reset?
+      /**
+     * Create a data block containing Roo.data.Records from an XML document.
+     * @param {Object} o An Array of row objects which represents the dataset.
+     * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
+     * a cache of Roo.data.Records.
      */
-    setFromData : function(o){
-        
-        if(this.multiple){
-            this.addItem(o);
-            return;
-        }
-            
-        var dv = ''; // display value
-        var vv = ''; // value value..
-        this.lastData = o;
-        if (this.displayField) {
-            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
-        } else {
-            // this is an error condition!!!
-            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
-        }
-        
-        if(this.valueField){
-            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
-        }
-        
-        var close = this.closeTriggerEl();
-        
-        if(close){
-            if(dv.length || vv * 1 > 0){
-                close.show() ;
-                this.blockFocus=true;
-            } else {
-                close.hide();
-            }             
+    readRecords : function(o)
+    {
+        var sid = this.meta ? this.meta.id : null;
+        var recordType = this.recordType, fields = recordType.prototype.fields;
+        var records = [];
+        var root = o;
+        for(var i = 0; i < root.length; i++){
+            var n = root[i];
+            var values = {};
+            var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
+            for(var j = 0, jlen = fields.length; j < jlen; j++){
+                var f = fields.items[j];
+                var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
+                var v = n[k] !== undefined ? n[k] : f.defaultValue;
+                v = f.convert(v);
+                values[f.name] = v;
+            }
+            var record = new recordType(values, id);
+            record.json = n;
+            records[records.length] = record;
         }
+        return {
+            records : records,
+            totalRecords : records.length
+        };
+    },
+    // used when loading children.. @see loadDataFromChildren
+    toLoadData: function(rec)
+    {
+        // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+        return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
         
-        if(this.hiddenField){
-            this.hiddenField.dom.value = vv;
-            
-            this.lastSelectionText = dv;
-            Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
-            this.value = vv;
-            return;
-        }
-        // no hidden field.. - we store the value in 'value', but still display
-        // display field!!!!
-        this.lastSelectionText = dv;
-        Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
-        this.value = vv;
-        
-        
-        
-    },
-    // private
-    reset : function(){
-        // overridden so that last data is reset..
-        
-        if(this.multiple){
-            this.clearItem();
-            return;
-        }
-        
-        this.setValue(this.originalValue);
-        //this.clearInvalid();
-        this.lastData = false;
-        if (this.view) {
-            this.view.clearSelections();
-        }
-        
-        this.validate();
-    },
-    // private
-    findRecord : function(prop, value){
-        var record;
-        if(this.store.getCount() > 0){
-            this.store.each(function(r){
-                if(r.data[prop] == value){
-                    record = r;
-                    return false;
-                }
-                return true;
-            });
-        }
-        return record;
-    },
+    }
     
-    getName: function()
-    {
-        // returns hidden if it's set..
-        if (!this.rendered) {return ''};
-        return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
-        
-    },
-    // private
-    onViewMove : function(e, t){
-        this.inKeyMode = false;
-    },
-
-    // private
-    onViewOver : function(e, t){
-        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
-            return;
-        }
-        var item = this.view.findItemFromChild(t);
-        
-        if(item){
-            var index = this.view.indexOf(item);
-            this.select(index, false);
-        }
-    },
+    
+});/*
+ * - LGPL
+ * * 
+ */
 
-    // private
-    onViewClick : function(view, doFocus, el, e)
-    {
-        var index = this.view.getSelectedIndexes()[0];
-        
-        var r = this.store.getAt(index);
-        
-        if(this.tickable){
-            
-            if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
-                return;
-            }
-            
-            var rm = false;
-            var _this = this;
-            
-            Roo.each(this.tickItems, function(v,k){
-                
-                if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
-                    Roo.log(v);
-                    _this.tickItems.splice(k, 1);
-                    
-                    if(typeof(e) == 'undefined' && view == false){
-                        Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
-                    }
-                    
-                    rm = true;
-                    return;
-                }
-            });
-            
-            if(rm){
-                return;
-            }
-            
-            if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
-                this.tickItems.push(r.data);
-            }
-            
-            if(typeof(e) == 'undefined' && view == false){
-                Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
-            }
-                    
-            return;
-        }
+/**
+ * @class Roo.bootstrap.ComboBox
+ * @extends Roo.bootstrap.TriggerField
+ * A combobox control with support for autocomplete, remote-loading, paging and many other features.
+ * @cfg {Boolean} append (true|false) default false
+ * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
+ * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
+ * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
+ * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
+ * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
+ * @cfg {Boolean} animate default true
+ * @cfg {Boolean} emptyResultText only for touch device
+ * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
+ * @cfg {String} emptyTitle default ''
+ * @cfg {Number} width fixed with? experimental
+ * @constructor
+ * Create a new ComboBox.
+ * @param {Object} config Configuration options
+ */
+Roo.bootstrap.ComboBox = function(config){
+    Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
+    this.addEvents({
+        /**
+         * @event expand
+         * Fires when the dropdown list is expanded
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        */
+        'expand' : true,
+        /**
+         * @event collapse
+         * Fires when the dropdown list is collapsed
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        */
+        'collapse' : true,
+        /**
+         * @event beforeselect
+         * Fires before a list item is selected. Return false to cancel the selection.
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        * @param {Roo.data.Record} record The data record returned from the underlying store
+        * @param {Number} index The index of the selected item in the dropdown list
+        */
+        'beforeselect' : true,
+        /**
+         * @event select
+         * Fires when a list item is selected
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
+        * @param {Number} index The index of the selected item in the dropdown list
+        */
+        'select' : true,
+        /**
+         * @event beforequery
+         * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
+         * The event object passed has these properties:
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        * @param {String} query The query
+        * @param {Boolean} forceAll true to force "all" query
+        * @param {Boolean} cancel true to cancel the query
+        * @param {Object} e The query event object
+        */
+        'beforequery': true,
+         /**
+         * @event add
+         * Fires when the 'add' icon is pressed (add a listener to enable add button)
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        */
+        'add' : true,
+        /**
+         * @event edit
+         * Fires when the 'edit' icon is pressed (add a listener to enable add button)
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
+        */
+        'edit' : true,
+        /**
+         * @event remove
+         * Fires when the remove value from the combobox array
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        */
+        'remove' : true,
+        /**
+         * @event afterremove
+         * Fires when the remove value from the combobox array
+        * @param {Roo.bootstrap.ComboBox} combo This combo box
+        */
+        'afterremove' : true,
+        /**
+         * @event specialfilter
+         * Fires when specialfilter
+            * @param {Roo.bootstrap.ComboBox} combo This combo box
+            */
+        'specialfilter' : true,
+        /**
+         * @event tick
+         * Fires when tick the element
+            * @param {Roo.bootstrap.ComboBox} combo This combo box
+            */
+        'tick' : true,
+        /**
+         * @event touchviewdisplay
+         * Fires when touch view require special display (default is using displayField)
+            * @param {Roo.bootstrap.ComboBox} combo This combo box
+            * @param {Object} cfg set html .
+            */
+        'touchviewdisplay' : true
         
-        if(r){
-            this.onSelect(r, index);
+    });
+    
+    this.item = [];
+    this.tickItems = [];
+    
+    this.selectedIndex = -1;
+    if(this.mode == 'local'){
+        if(config.queryDelay === undefined){
+            this.queryDelay = 10;
         }
-        if(doFocus !== false && !this.blockFocus){
-            this.inputEl().focus();
+        if(config.minChars === undefined){
+            this.minChars = 0;
         }
-    },
-
-    // private
-    restrictHeight : function(){
-        //this.innerList.dom.style.height = '';
-        //var inner = this.innerList.dom;
-        //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
-        //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
-        //this.list.beginUpdate();
-        //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
-        this.list.alignTo(this.inputEl(), this.listAlign);
-        this.list.alignTo(this.inputEl(), this.listAlign);
-        //this.list.endUpdate();
-    },
-
-    // private
-    onEmptyResults : function(){
-        
-        if(this.tickable && this.editable){
-            this.hasFocus = false;
-            this.restrictHeight();
-            return;
-        }
-        
-        this.collapse();
-    },
+    }
+};
 
+Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
+     
     /**
-     * Returns true if the dropdown list is expanded, else false.
+     * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
+     * rendering into an Roo.Editor, defaults to false)
      */
-    isExpanded : function(){
-        return this.list.isVisible();
-    },
-
     /**
-     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
-     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
-     * @param {String} value The data value of the item to select
-     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
-     * selected item if it is not currently in view (defaults to true)
-     * @return {Boolean} True if the value matched an item in the list, else false
+     * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
+     * {tag: "input", type: "text", size: "24", autocomplete: "off"})
      */
-    selectByValue : function(v, scrollIntoView){
-        if(v !== undefined && v !== null){
-            var r = this.findRecord(this.valueField || this.displayField, v);
-            if(r){
-                this.select(this.store.indexOf(r), scrollIntoView);
-                return true;
-            }
-        }
-        return false;
-    },
-
     /**
-     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
-     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
-     * @param {Number} index The zero-based index of the list item to select
-     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
-     * selected item if it is not currently in view (defaults to true)
+     * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
+     */
+    /**
+     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
+     * the dropdown list (defaults to undefined, with no header element)
      */
-    select : function(index, scrollIntoView){
-        this.selectedIndex = index;
-        this.view.select(index);
-        if(scrollIntoView !== false){
-            var el = this.view.getNode(index);
-            /*
-             * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
-             */
-            if(el){
-                this.list.scrollChildIntoView(el, false);
-            }
-        }
-    },
-
-    // private
-    selectNext : function(){
-        var ct = this.store.getCount();
-        if(ct > 0){
-            if(this.selectedIndex == -1){
-                this.select(0);
-            }else if(this.selectedIndex < ct-1){
-                this.select(this.selectedIndex+1);
-            }
-        }
-    },
-
-    // private
-    selectPrev : function(){
-        var ct = this.store.getCount();
-        if(ct > 0){
-            if(this.selectedIndex == -1){
-                this.select(0);
-            }else if(this.selectedIndex != 0){
-                this.select(this.selectedIndex-1);
-            }
-        }
-    },
-
-    // private
-    onKeyUp : function(e){
-        if(this.editable !== false && !e.isSpecialKey()){
-            this.lastKey = e.getKey();
-            this.dqTask.delay(this.queryDelay);
-        }
-    },
-
-    // private
-    validateBlur : function(){
-        return !this.list || !this.list.isVisible();   
-    },
-
-    // private
-    initQuery : function(){
-        
-        var v = this.getRawValue();
-        
-        if(this.tickable && this.editable){
-            v = this.tickableInputEl().getValue();
-        }
-        
-        this.doQuery(v);
-    },
-
-    // private
-    doForce : function(){
-        if(this.inputEl().dom.value.length > 0){
-            this.inputEl().dom.value =
-                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
-             
-        }
-    },
 
+     /**
+     * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
+     */
+     
+     /**
+     * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
+     */
+    listWidth: undefined,
     /**
-     * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
-     * query allowing the query action to be canceled if needed.
-     * @param {String} query The SQL query to execute
-     * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
-     * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
-     * saved in the current store (defaults to false)
+     * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
+     * mode = 'remote' or 'text' if mode = 'local')
      */
-    doQuery : function(q, forceAll){
-        
-        if(q === undefined || q === null){
-            q = '';
-        }
-        var qe = {
-            query: q,
-            forceAll: forceAll,
-            combo: this,
-            cancel:false
-        };
-        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
-            return false;
-        }
-        q = qe.query;
-        
-        forceAll = qe.forceAll;
-        if(forceAll === true || (q.length >= this.minChars)){
-            
-            this.hasQuery = true;
-            
-            if(this.lastQuery != q || this.alwaysQuery){
-                this.lastQuery = q;
-                if(this.mode == 'local'){
-                    this.selectedIndex = -1;
-                    if(forceAll){
-                        this.store.clearFilter();
-                    }else{
-                        
-                        if(this.specialFilter){
-                            this.fireEvent('specialfilter', this);
-                            this.onLoad();
-                            return;
-                        }
-                        
-                        this.store.filter(this.displayField, q);
-                    }
-                    
-                    this.store.fireEvent("datachanged", this.store);
-                    
-                    this.onLoad();
-                    
-                    
-                }else{
-                    
-                    this.store.baseParams[this.queryParam] = q;
-                    
-                    var options = {params : this.getParams(q)};
-                    
-                    if(this.loadNext){
-                        options.add = true;
-                        options.params.start = this.page * this.pageSize;
-                    }
-                    
-                    this.store.load(options);
-                    
-                    /*
-                     *  this code will make the page width larger, at the beginning, the list not align correctly, 
-                     *  we should expand the list on onLoad
-                     *  so command out it
-                     */
-//                    this.expand();
-                }
-            }else{
-                this.selectedIndex = -1;
-                this.onLoad();   
-            }
-        }
-        
-        this.loadNext = false;
-    },
+    displayField: undefined,
     
-    // private
-    getParams : function(q){
-        var p = {};
-        //p[this.queryParam] = q;
-        
-        if(this.pageSize){
-            p.start = 0;
-            p.limit = this.pageSize;
-        }
-        return p;
-    },
-
     /**
-     * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+     * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
+     * mode = 'remote' or 'value' if mode = 'local'). 
+     * Note: use of a valueField requires the user make a selection
+     * in order for a value to be mapped.
      */
-    collapse : function(){
-        if(!this.isExpanded()){
-            return;
-        }
-        
-        this.list.hide();
-        
-        this.hasFocus = false;
-        
-        if(this.tickable){
-            this.okBtn.hide();
-            this.cancelBtn.hide();
-            this.trigger.show();
-            
-            if(this.editable){
-                this.tickableInputEl().dom.value = '';
-                this.tickableInputEl().blur();
-            }
-            
-        }
-        
-        Roo.get(document).un('mousedown', this.collapseIf, this);
-        Roo.get(document).un('mousewheel', this.collapseIf, this);
-        if (!this.editable) {
-            Roo.get(document).un('keydown', this.listKeyPress, this);
-        }
-        this.fireEvent('collapse', this);
-        
-        this.validate();
-    },
-
-    // private
-    collapseIf : function(e){
-        var in_combo  = e.within(this.el);
-        var in_list =  e.within(this.list);
-        var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
+    valueField: undefined,
+    /**
+     * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
+     */
+    modalTitle : '',
+    
+    /**
+     * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
+     * field's data value (defaults to the underlying DOM element's name)
+     */
+    hiddenName: undefined,
+    /**
+     * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
+     */
+    listClass: '',
+    /**
+     * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
+     */
+    selectedClass: 'active',
+    
+    /**
+     * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
+     */
+    shadow:'sides',
+    /**
+     * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
+     * anchor positions (defaults to 'tl-bl')
+     */
+    listAlign: 'tl-bl?',
+    /**
+     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
+     */
+    maxHeight: 300,
+    /**
+     * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
+     * query specified by the allQuery config option (defaults to 'query')
+     */
+    triggerAction: 'query',
+    /**
+     * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
+     * (defaults to 4, does not apply if editable = false)
+     */
+    minChars : 4,
+    /**
+     * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
+     * delay (typeAheadDelay) if it matches a known value (defaults to false)
+     */
+    typeAhead: false,
+    /**
+     * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
+     * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
+     */
+    queryDelay: 500,
+    /**
+     * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
+     * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
+     */
+    pageSize: 0,
+    /**
+     * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
+     * when editable = true (defaults to false)
+     */
+    selectOnFocus:false,
+    /**
+     * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
+     */
+    queryParam: 'query',
+    /**
+     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
+     * when mode = 'remote' (defaults to 'Loading...')
+     */
+    loadingText: 'Loading...',
+    /**
+     * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
+     */
+    resizable: false,
+    /**
+     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
+     */
+    handleHeight : 8,
+    /**
+     * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
+     * traditional select (defaults to true)
+     */
+    editable: true,
+    /**
+     * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
+     */
+    allQuery: '',
+    /**
+     * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
+     */
+    mode: 'remote',
+    /**
+     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
+     * listWidth has a higher value)
+     */
+    minListWidth : 70,
+    /**
+     * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
+     * allow the user to set arbitrary text into the field (defaults to false)
+     */
+    forceSelection:false,
+    /**
+     * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+     * if typeAhead = true (defaults to 250)
+     */
+    typeAheadDelay : 250,
+    /**
+     * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+     * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
+     */
+    valueNotFoundText : undefined,
+    /**
+     * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
+     */
+    blockFocus : false,
+    
+    /**
+     * @cfg {Boolean} disableClear Disable showing of clear button.
+     */
+    disableClear : false,
+    /**
+     * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
+     */
+    alwaysQuery : false,
+    
+    /**
+     * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
+     */
+    multiple : false,
+    
+    /**
+     * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
+     */
+    invalidClass : "has-warning",
+    
+    /**
+     * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
+     */
+    validClass : "has-success",
+    
+    /**
+     * @cfg {Boolean} specialFilter (true|false) special filter default false
+     */
+    specialFilter : false,
+    
+    /**
+     * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
+     */
+    mobileTouchView : true,
+    
+    /**
+     * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
+     */
+    useNativeIOS : false,
+    
+    /**
+     * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
+     */
+    mobile_restrict_height : false,
+    
+    ios_options : false,
+    
+    //private
+    addicon : false,
+    editicon: false,
+    
+    page: 0,
+    hasQuery: false,
+    append: false,
+    loadNext: false,
+    autoFocus : true,
+    tickable : false,
+    btnPosition : 'right',
+    triggerList : true,
+    showToggleBtn : true,
+    animate : true,
+    emptyResultText: 'Empty',
+    triggerText : 'Select',
+    emptyTitle : '',
+    width : false,
+    
+    // element that contains real text value.. (when hidden is used..)
+    
+    getAutoCreate : function()
+    {   
+        var cfg = false;
+        //render
+        /*
+         * Render classic select for iso
+         */
         
-        if (in_combo || in_list || is_list) {
-            //e.stopPropagation();
-            return;
+        if(Roo.isIOS && this.useNativeIOS){
+            cfg = this.getAutoCreateNativeIOS();
+            return cfg;
         }
         
-        if(this.tickable){
-            this.onTickableFooterButtonClick(e, false, false);
+        /*
+         * Touch Devices
+         */
+        
+        if(Roo.isTouch && this.mobileTouchView){
+            cfg = this.getAutoCreateTouchView();
+            return cfg;;
         }
-
-        this.collapse();
         
-    },
-
-    /**
-     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
-     */
-    expand : function(){
-       
-        if(this.isExpanded() || !this.hasFocus){
-            return;
+        /*
+         *  Normal ComboBox
+         */
+        if(!this.tickable){
+            cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
+            return cfg;
         }
         
-        var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
-        this.list.setWidth(lw);
+        /*
+         *  ComboBox with tickable selections
+         */
+             
+        var align = this.labelAlign || this.parentLabelAlign();
         
-        Roo.log('expand');
+        cfg = {
+            cls : 'form-group roo-combobox-tickable' //input-group
+        };
         
-        this.list.show();
+        var btn_text_select = '';
+        var btn_text_done = '';
+        var btn_text_cancel = '';
         
-        this.restrictHeight();
+        if (this.btn_text_show) {
+            btn_text_select = 'Select';
+            btn_text_done = 'Done';
+            btn_text_cancel = 'Cancel'; 
+        }
         
-        if(this.tickable){
-            
-            this.tickItems = Roo.apply([], this.item);
-            
-            this.okBtn.show();
-            this.cancelBtn.show();
-            this.trigger.hide();
-            
-            if(this.editable){
-                this.tickableInputEl().focus();
-            }
-            
-        }
-        
-        Roo.get(document).on('mousedown', this.collapseIf, this);
-        Roo.get(document).on('mousewheel', this.collapseIf, this);
-        if (!this.editable) {
-            Roo.get(document).on('keydown', this.listKeyPress, this);
-        }
-        
-        this.fireEvent('expand', this);
-    },
-
-    // private
-    // Implements the default empty TriggerField.onTriggerClick function
-    onTriggerClick : function(e)
-    {
-        Roo.log('trigger click');
+        var buttons = {
+            tag : 'div',
+            cls : 'tickable-buttons',
+            cn : [
+                {
+                    tag : 'button',
+                    type : 'button',
+                    cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
+                    //html : this.triggerText
+                    html: btn_text_select
+                },
+                {
+                    tag : 'button',
+                    type : 'button',
+                    name : 'ok',
+                    cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
+                    //html : 'Done'
+                    html: btn_text_done
+                },
+                {
+                    tag : 'button',
+                    type : 'button',
+                    name : 'cancel',
+                    cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
+                    //html : 'Cancel'
+                    html: btn_text_cancel
+                }
+            ]
+        };
         
-        if(this.disabled || !this.triggerList){
-            return;
+        if(this.editable){
+            buttons.cn.unshift({
+                tag: 'input',
+                cls: 'roo-select2-search-field-input'
+            });
         }
         
-        this.page = 0;
-        this.loadNext = false;
+        var _this = this;
         
-        if(this.isExpanded()){
-            this.collapse();
-            if (!this.blockFocus) {
-                this.inputEl().focus();
-            }
-            
-        }else {
-            this.hasFocus = true;
-            if(this.triggerAction == 'all') {
-                this.doQuery(this.allQuery, true);
-            } else {
-                this.doQuery(this.getRawValue());
+        Roo.each(buttons.cn, function(c){
+            if (_this.size) {
+                c.cls += ' btn-' + _this.size;
             }
-            if (!this.blockFocus) {
-                this.inputEl().focus();
+
+            if (_this.disabled) {
+                c.disabled = true;
             }
-        }
-    },
-    
-    onTickableTriggerClick : function(e)
-    {
-        if(this.disabled){
-            return;
-        }
-        
-        this.page = 0;
-        this.loadNext = false;
-        this.hasFocus = true;
-        
-        if(this.triggerAction == 'all') {
-            this.doQuery(this.allQuery, true);
-        } else {
-            this.doQuery(this.getRawValue());
-        }
-    },
-    
-    onSearchFieldClick : function(e)
-    {
-        if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
-            this.onTickableFooterButtonClick(e, false, false);
-            return;
-        }
+        });
         
-        if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
-            return;
-        }
+        var box = {
+            tag: 'div',
+            style : 'display: contents',
+            cn: [
+                {
+                    tag: 'input',
+                    type : 'hidden',
+                    cls: 'form-hidden-field'
+                },
+                {
+                    tag: 'ul',
+                    cls: 'roo-select2-choices',
+                    cn:[
+                        {
+                            tag: 'li',
+                            cls: 'roo-select2-search-field',
+                            cn: [
+                                buttons
+                            ]
+                        }
+                    ]
+                }
+            ]
+        };
         
-        this.page = 0;
-        this.loadNext = false;
-        this.hasFocus = true;
+        var combobox = {
+            cls: 'roo-select2-container input-group roo-select2-container-multi',
+            cn: [
+                
+                box
+//                {
+//                    tag: 'ul',
+//                    cls: 'typeahead typeahead-long dropdown-menu',
+//                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
+//                }
+            ]
+        };
         
-        if(this.triggerAction == 'all') {
-            this.doQuery(this.allQuery, true);
-        } else {
-            this.doQuery(this.getRawValue());
-        }
-    },
-    
-    listKeyPress : function(e)
-    {
-        //Roo.log('listkeypress');
-        // scroll to first matching element based on key pres..
-        if (e.isSpecialKey()) {
-            return false;
-        }
-        var k = String.fromCharCode(e.getKey()).toUpperCase();
-        //Roo.log(k);
-        var match  = false;
-        var csel = this.view.getSelectedNodes();
-        var cselitem = false;
-        if (csel.length) {
-            var ix = this.view.indexOf(csel[0]);
-            cselitem  = this.store.getAt(ix);
-            if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
-                cselitem = false;
-            }
+        if(this.hasFeedback && !this.allowBlank){
             
+            var feedback = {
+                tag: 'span',
+                cls: 'glyphicon form-control-feedback'
+            };
+
+            combobox.cn.push(feedback);
         }
         
-        this.store.each(function(v) { 
-            if (cselitem) {
-                // start at existing selection.
-                if (cselitem.id == v.id) {
-                    cselitem = false;
-                }
-                return true;
-            }
-                
-            if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
-                match = this.store.indexOf(v);
-                return false;
-            }
-            return true;
-        }, this);
         
-        if (match === false) {
-            return true; // no more action?
-        }
-        // scroll to?
-        this.view.select(match);
-        var sn = Roo.get(this.view.getSelectedNodes()[0]);
-        sn.scrollIntoView(sn.dom.parentNode, false);
-    },
-    
-    onViewScroll : function(e, t){
         
-        if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
-            return;
+        var indicator = {
+            tag : 'i',
+            cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+            tooltip : 'This field is required'
+        };
+        if (Roo.bootstrap.version == 4) {
+            indicator = {
+                tag : 'i',
+                style : 'display:none'
+            };
         }
-        
-        this.hasQuery = true;
-        
-        this.loading = this.list.select('.loading', true).first();
-        
-        if(this.loading === null){
-            this.list.createChild({
-                tag: 'div',
-                cls: 'loading roo-select2-more-results roo-select2-active',
-                html: 'Loading more results...'
-            });
+        if (align ==='left' && this.fieldLabel.length) {
             
-            this.loading = this.list.select('.loading', true).first();
+            cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
             
-            this.loading.setVisibilityMode(Roo.Element.DISPLAY);
+            cfg.cn = [
+                indicator,
+                {
+                    tag: 'label',
+                    'for' :  id,
+                    cls : 'control-label col-form-label',
+                    html : this.fieldLabel
+
+                },
+                {
+                    cls : "", 
+                    cn: [
+                        combobox
+                    ]
+                }
+
+            ];
             
-            this.loading.hide();
+            var labelCfg = cfg.cn[1];
+            var contentCfg = cfg.cn[2];
+            
+
+            if(this.indicatorpos == 'right'){
+                
+                cfg.cn = [
+                    {
+                        tag: 'label',
+                        'for' :  id,
+                        cls : 'control-label col-form-label',
+                        cn : [
+                            {
+                                tag : 'span',
+                                html : this.fieldLabel
+                            },
+                            indicator
+                        ]
+                    },
+                    {
+                        cls : "",
+                        cn: [
+                            combobox
+                        ]
+                    }
+
+                ];
+                
+                
+                
+                labelCfg = cfg.cn[0];
+                contentCfg = cfg.cn[1];
+            
+            }
+            
+            if(this.labelWidth > 12){
+                labelCfg.style = "width: " + this.labelWidth + 'px';
+            }
+            if(this.width * 1 > 0){
+                contentCfg.style = "width: " + this.width + 'px';
+            }
+            if(this.labelWidth < 13 && this.labelmd == 0){
+                this.labelmd = this.labelWidth;
+            }
+            
+            if(this.labellg > 0){
+                labelCfg.cls += ' col-lg-' + this.labellg;
+                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+            }
+            
+            if(this.labelmd > 0){
+                labelCfg.cls += ' col-md-' + this.labelmd;
+                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+            }
+            
+            if(this.labelsm > 0){
+                labelCfg.cls += ' col-sm-' + this.labelsm;
+                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+            }
+            
+            if(this.labelxs > 0){
+                labelCfg.cls += ' col-xs-' + this.labelxs;
+                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+            }
+                
+                
+        } else if ( this.fieldLabel.length) {
+//                Roo.log(" label");
+                 cfg.cn = [
+                   indicator,
+                    {
+                        tag: 'label',
+                        //cls : 'input-group-addon',
+                        html : this.fieldLabel
+                    },
+                    combobox
+                ];
+                
+                if(this.indicatorpos == 'right'){
+                    cfg.cn = [
+                        {
+                            tag: 'label',
+                            //cls : 'input-group-addon',
+                            html : this.fieldLabel
+                        },
+                        indicator,
+                        combobox
+                    ];
+                    
+                }
+
+        } else {
+            
+//                Roo.log(" no label && no align");
+                cfg = combobox
+                     
+                
         }
+         
+        var settings=this;
+        ['xs','sm','md','lg'].map(function(size){
+            if (settings[size]) {
+                cfg.cls += ' col-' + size + '-' + settings[size];
+            }
+        });
         
-        this.loading.show();
-        
-        var _combo = this;
-        
-        this.page++;
-        this.loadNext = true;
-        
-        (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+        return cfg;
         
-        return;
     },
     
-    addItem : function(o)
+    _initEventsCalled : false,
+    
+    // private
+    initEvents: function()
     {   
-        var dv = ''; // display value
-        
-        if (this.displayField) {
-            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
-        } else {
-            // this is an error condition!!!
-            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
-        }
-        
-        if(!dv.length){
+        if (this._initEventsCalled) { // as we call render... prevent looping...
             return;
         }
+        this._initEventsCalled = true;
         
-        var choice = this.choices.createChild({
-            tag: 'li',
-            cls: 'roo-select2-search-choice',
-            cn: [
-                {
-                    tag: 'div',
-                    html: dv
-                },
-                {
-                    tag: 'a',
-                    href: '#',
-                    cls: 'roo-select2-search-choice-close fa fa-times',
-                    tabindex: '-1'
-                }
-            ]
-            
-        }, this.searchField);
-        
-        var close = choice.select('a.roo-select2-search-choice-close', true).first();
-        
-        close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
-        
-        this.item.push(o);
-        
-        this.lastData = o;
-        
-        this.syncValue();
-        
-        this.inputEl().dom.value = '';
+        if (!this.store) {
+            throw "can not find store for combo";
+        }
         
-        this.validate();
-    },
-    
-    onRemoveItem : function(e, _self, o)
-    {
-        e.preventDefault();
+        this.indicator = this.indicatorEl();
         
-        this.lastItem = Roo.apply([], this.item);
+        this.store = Roo.factory(this.store, Roo.data);
+        this.store.parent = this;
         
-        var index = this.item.indexOf(o.data) * 1;
+        // if we are building from html. then this element is so complex, that we can not really
+        // use the rendered HTML.
+        // so we have to trash and replace the previous code.
+        if (Roo.XComponent.build_from_html) {
+            // remove this element....
+            var e = this.el.dom, k=0;
+            while (e ) { e = e.previousSibling;  ++k;}
+
+            this.el.remove();
+            
+            this.el=false;
+            this.rendered = false;
+            
+            this.render(this.parent().getChildContainer(true), k);
+        }
         
-        if( index < 0){
-            Roo.log('not this item?!');
+        if(Roo.isIOS && this.useNativeIOS){
+            this.initIOSView();
             return;
         }
         
-        this.item.splice(index, 1);
-        o.item.remove();
-        
-        this.syncValue();
-        
-        this.fireEvent('remove', this, e);
+        /*
+         * Touch Devices
+         */
         
-        this.validate();
+        if(Roo.isTouch && this.mobileTouchView){
+            this.initTouchView();
+            return;
+        }
         
-    },
-    
-    syncValue : function()
-    {
-        if(!this.item.length){
-            this.clearValue();
+        if(this.tickable){
+            this.initTickableEvents();
             return;
         }
+        
+        Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
+        
+        if(this.hiddenName){
             
-        var value = [];
-        var _this = this;
-        Roo.each(this.item, function(i){
-            if(_this.valueField){
-                value.push(i[_this.valueField]);
-                return;
-            }
-
-            value.push(i);
-        });
-
-        this.value = value.join(',');
+            this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+            
+            this.hiddenField.dom.value =
+                this.hiddenValue !== undefined ? this.hiddenValue :
+                this.value !== undefined ? this.value : '';
 
-        if(this.hiddenField){
-            this.hiddenField.dom.value = this.value;
+            // prevent input submission
+            this.el.dom.removeAttribute('name');
+            this.hiddenField.dom.setAttribute('name', this.hiddenName);
+             
+             
         }
+        //if(Roo.isGecko){
+        //    this.el.dom.setAttribute('autocomplete', 'off');
+        //}
         
-        this.store.fireEvent("datachanged", this.store);
+        var cls = 'x-combo-list';
         
-        this.validate();
-    },
-    
-    clearItem : function()
-    {
-        if(!this.multiple){
-            return;
-        }
+        //this.list = new Roo.Layer({
+        //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+        //});
         
-        this.item = [];
+        var _this = this;
         
-        Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
-           c.remove();
-        });
+        (function(){
+            var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
+            _this.list.setWidth(lw);
+        }).defer(100);
         
-        this.syncValue();
+        this.list.on('mouseover', this.onViewOver, this);
+        this.list.on('mousemove', this.onViewMove, this);
+        this.list.on('scroll', this.onViewScroll, this);
         
-        this.validate();
+        /*
+        this.list.swallowEvent('mousewheel');
+        this.assetHeight = 0;
+
+        if(this.title){
+            this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
+            this.assetHeight += this.header.getHeight();
+        }
+
+        this.innerList = this.list.createChild({cls:cls+'-inner'});
+        this.innerList.on('mouseover', this.onViewOver, this);
+        this.innerList.on('mousemove', this.onViewMove, this);
+        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
         
-        if(this.tickable && !Roo.isTouch){
-            this.view.refresh();
+        if(this.allowBlank && !this.pageSize && !this.disableClear){
+            this.footer = this.list.createChild({cls:cls+'-ft'});
+            this.pageTb = new Roo.Toolbar(this.footer);
+           
         }
-    },
-    
-    inputEl: function ()
-    {
-        if(Roo.isIOS && this.useNativeIOS){
-            return this.el.select('select.roo-ios-select', true).first();
+        if(this.pageSize){
+            this.footer = this.list.createChild({cls:cls+'-ft'});
+            this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
+                    {pageSize: this.pageSize});
+            
         }
         
-        if(Roo.isTouch && this.mobileTouchView){
-            return this.el.select('input.form-control',true).first();
+        if (this.pageTb && this.allowBlank && !this.disableClear) {
+            var _this = this;
+            this.pageTb.add(new Roo.Toolbar.Fill(), {
+                cls: 'x-btn-icon x-btn-clear',
+                text: '&#160;',
+                handler: function()
+                {
+                    _this.collapse();
+                    _this.clearValue();
+                    _this.onSelect(false, -1);
+                }
+            });
         }
-        
-        if(this.tickable){
-            return this.searchField;
+        if (this.footer) {
+            this.assetHeight += this.footer.getHeight();
         }
+        */
+            
+        if(!this.tpl){
+            this.tpl = Roo.bootstrap.version == 4 ?
+                '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
+                '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
+        }
+
+        this.view = new Roo.View(this.list, this.tpl, {
+            singleSelect:true, store: this.store, selectedClass: this.selectedClass
+        });
+        //this.view.wrapEl.setDisplayed(false);
+        this.view.on('click', this.onViewClick, this);
         
-        return this.el.select('input.form-control',true).first();
-    },
-    
-    onTickableFooterButtonClick : function(e, btn, el)
-    {
-        e.preventDefault();
-        
-        this.lastItem = Roo.apply([], this.item);
         
-        if(btn && btn.name == 'cancel'){
-            this.tickItems = Roo.apply([], this.item);
-            this.collapse();
-            return;
+        this.store.on('beforeload', this.onBeforeLoad, this);
+        this.store.on('load', this.onLoad, this);
+        this.store.on('loadexception', this.onLoadException, this);
+        /*
+        if(this.resizable){
+            this.resizer = new Roo.Resizable(this.list,  {
+               pinned:true, handles:'se'
+            });
+            this.resizer.on('resize', function(r, w, h){
+                this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
+                this.listWidth = w;
+                this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
+                this.restrictHeight();
+            }, this);
+            this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+        }
+        */
+        if(!this.editable){
+            this.editable = true;
+            this.setEditable(false);
         }
         
-        this.clearItem();
+        /*
         
-        var _this = this;
+        if (typeof(this.events.add.listeners) != 'undefined') {
+            
+            this.addicon = this.wrap.createChild(
+                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
+       
+            this.addicon.on('click', function(e) {
+                this.fireEvent('add', this);
+            }, this);
+        }
+        if (typeof(this.events.edit.listeners) != 'undefined') {
+            
+            this.editicon = this.wrap.createChild(
+                {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
+            if (this.addicon) {
+                this.editicon.setStyle('margin-left', '40px');
+            }
+            this.editicon.on('click', function(e) {
+                
+                // we fire even  if inothing is selected..
+                this.fireEvent('edit', this, this.lastData );
+                
+            }, this);
+        }
+        */
         
-        Roo.each(this.tickItems, function(o){
-            _this.addItem(o);
+        this.keyNav = new Roo.KeyNav(this.inputEl(), {
+            "up" : function(e){
+                this.inKeyMode = true;
+                this.selectPrev();
+            },
+
+            "down" : function(e){
+                if(!this.isExpanded()){
+                    this.onTriggerClick();
+                }else{
+                    this.inKeyMode = true;
+                    this.selectNext();
+                }
+            },
+
+            "enter" : function(e){
+//                this.onViewClick();
+                //return true;
+                this.collapse();
+                
+                if(this.fireEvent("specialkey", this, e)){
+                    this.onViewClick(false);
+                }
+                
+                return true;
+            },
+
+            "esc" : function(e){
+                this.collapse();
+            },
+
+            "tab" : function(e){
+                this.collapse();
+                
+                if(this.fireEvent("specialkey", this, e)){
+                    this.onViewClick(false);
+                }
+                
+                return true;
+            },
+
+            scope : this,
+
+            doRelay : function(foo, bar, hname){
+                if(hname == 'down' || this.scope.isExpanded()){
+                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+                }
+                return true;
+            },
+
+            forceKeyDown: true
         });
         
-        this.collapse();
         
-    },
-    
-    validate : function()
-    {
-        if(this.getVisibilityEl().hasClass('hidden')){
-            return true;
-        }
+        this.queryDelay = Math.max(this.queryDelay || 10,
+                this.mode == 'local' ? 10 : 250);
         
-        var v = this.getRawValue();
         
-        if(this.multiple){
-            v = this.getValue();
-        }
+        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
         
-        if(this.disabled || this.allowBlank || v.length){
-            this.markValid();
-            return true;
+        if(this.typeAhead){
+            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
         }
-        
-        this.markInvalid();
-        return false;
-    },
-    
-    tickableInputEl : function()
-    {
-        if(!this.tickable || !this.editable){
-            return this.inputEl();
+        if(this.editable !== false){
+            this.inputEl().on("keyup", this.onKeyUp, this);
+        }
+        if(this.forceSelection){
+            this.inputEl().on('blur', this.doForce, this);
         }
         
-        return this.inputEl().select('.roo-select2-search-field-input', true).first();
+        if(this.multiple){
+            this.choices = this.el.select('ul.roo-select2-choices', true).first();
+            this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+        }
     },
     
-    
-    getAutoCreateTouchView : function()
-    {
-        var id = Roo.id();
+    initTickableEvents: function()
+    {   
+        this.createList();
         
-        var cfg = {
-            cls: 'form-group' //input-group
-        };
+        if(this.hiddenName){
+            
+            this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+            
+            this.hiddenField.dom.value =
+                this.hiddenValue !== undefined ? this.hiddenValue :
+                this.value !== undefined ? this.value : '';
+
+            // prevent input submission
+            this.el.dom.removeAttribute('name');
+            this.hiddenField.dom.setAttribute('name', this.hiddenName);
+             
+             
+        }
         
-        var input =  {
-            tag: 'input',
-            id : id,
-            type : this.inputType,
-            cls : 'form-control x-combo-noedit',
-            autocomplete: 'new-password',
-            placeholder : this.placeholder || '',
-            readonly : true
-        };
+//        this.list = this.el.select('ul.dropdown-menu',true).first();
         
-        if (this.name) {
-            input.name = this.name;
+        this.choices = this.el.select('ul.roo-select2-choices', true).first();
+        this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+        if(this.triggerList){
+            this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
         }
+         
+        this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
+        this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
         
-        if (this.size) {
-            input.cls += ' input-' + this.size;
-        }
+        this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
+        this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
         
-        if (this.disabled) {
-            input.disabled = true;
-        }
+        this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
+        this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
         
-        var inputblock = {
-            cls : 'roo-combobox-wrap',
-            cn : [
-                input
-            ]
-        };
+        this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
+        this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
+        this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
         
-        if(this.before){
-            inputblock.cls += ' input-group';
-            
-            inputblock.cn.unshift({
-                tag :'span',
-                cls : 'input-group-addon input-group-prepend input-group-text',
-                html : this.before
-            });
-        }
+        this.okBtn.hide();
+        this.cancelBtn.hide();
         
-        if(this.removable && !this.multiple){
-            inputblock.cls += ' roo-removable';
-            
-            inputblock.cn.push({
-                tag: 'button',
-                html : 'x',
-                cls : 'roo-combo-removable-btn close'
-            });
-        }
-
-        if(this.hasFeedback && !this.allowBlank){
-            
-            inputblock.cls += ' has-feedback';
-            
-            inputblock.cn.push({
-                tag: 'span',
-                cls: 'glyphicon form-control-feedback'
-            });
-            
-        }
+        var _this = this;
         
-        if (this.after) {
-            
-            inputblock.cls += (this.before) ? '' : ' input-group';
-            
-            inputblock.cn.push({
-                tag :'span',
-                cls : 'input-group-addon input-group-append input-group-text',
-                html : this.after
-            });
+        (function(){
+            var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
+            _this.list.setWidth(lw);
+        }).defer(100);
+        
+        this.list.on('mouseover', this.onViewOver, this);
+        this.list.on('mousemove', this.onViewMove, this);
+        
+        this.list.on('scroll', this.onViewScroll, this);
+        
+        if(!this.tpl){
+            this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
+                'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
         }
 
+        this.view = new Roo.View(this.list, this.tpl, {
+            singleSelect:true,
+            tickable:true,
+            parent:this,
+            store: this.store,
+            selectedClass: this.selectedClass
+        });
         
-        var ibwrap = inputblock;
+        //this.view.wrapEl.setDisplayed(false);
+        this.view.on('click', this.onViewClick, this);
         
-        if(this.multiple){
-            ibwrap = {
-                tag: 'ul',
-                cls: 'roo-select2-choices',
-                cn:[
-                    {
-                        tag: 'li',
-                        cls: 'roo-select2-search-field',
-                        cn: [
-
-                            inputblock
-                        ]
-                    }
-                ]
-            };
         
-            
-        }
         
-        var combobox = {
-            cls: 'roo-select2-container input-group roo-touchview-combobox ',
-            cn: [
-                {
-                    tag: 'input',
-                    type : 'hidden',
-                    cls: 'form-hidden-field'
-                },
-                ibwrap
-            ]
-        };
+        this.store.on('beforeload', this.onBeforeLoad, this);
+        this.store.on('load', this.onLoad, this);
+        this.store.on('loadexception', this.onLoadException, this);
         
-        if(!this.multiple && this.showToggleBtn){
-            
-            var caret = {
-                cls: 'caret'
-            };
-            
-            if (this.caret != false) {
-                caret = {
-                     tag: 'i',
-                     cls: 'fa fa-' + this.caret
-                };
-                
-            }
-            
-            combobox.cn.push({
-                tag :'span',
-                cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
-                cn : [
-                    Roo.bootstrap.version == 3 ? caret : '',
-                    {
-                        tag: 'span',
-                        cls: 'combobox-clear',
-                        cn  : [
-                            {
-                                tag : 'i',
-                                cls: 'icon-remove'
-                            }
-                        ]
+        if(this.editable){
+            this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
+                "up" : function(e){
+                    this.inKeyMode = true;
+                    this.selectPrev();
+                },
+
+                "down" : function(e){
+                    this.inKeyMode = true;
+                    this.selectNext();
+                },
+
+                "enter" : function(e){
+                    if(this.fireEvent("specialkey", this, e)){
+                        this.onViewClick(false);
                     }
-                ]
+                    
+                    return true;
+                },
 
-            })
+                "esc" : function(e){
+                    this.onTickableFooterButtonClick(e, false, false);
+                },
+
+                "tab" : function(e){
+                    this.fireEvent("specialkey", this, e);
+                    
+                    this.onTickableFooterButtonClick(e, false, false);
+                    
+                    return true;
+                },
+
+                scope : this,
+
+                doRelay : function(e, fn, key){
+                    if(this.scope.isExpanded()){
+                       return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+                    }
+                    return true;
+                },
+
+                forceKeyDown: true
+            });
         }
         
-        if(this.multiple){
-            combobox.cls += ' roo-select2-container-multi';
+        this.queryDelay = Math.max(this.queryDelay || 10,
+                this.mode == 'local' ? 10 : 250);
+        
+        
+        this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
+        
+        if(this.typeAhead){
+            this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
         }
         
-        var required =  this.allowBlank ?  {
-                    tag : 'i',
-                    style: 'display: none'
-                } : {
-                   tag : 'i',
-                   cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
-                   tooltip : 'This field is required'
-                };
+        if(this.editable !== false){
+            this.tickableInputEl().on("keyup", this.onKeyUp, this);
+        }
         
-        var align = this.labelAlign || this.parentLabelAlign();
+        this.indicator = this.indicatorEl();
         
-        if (align ==='left' && this.fieldLabel.length) {
+        if(this.indicator){
+            this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
+            this.indicator.hide();
+        }
+        
+    },
 
-            cfg.cn = [
-                required,
-                {
-                    tag: 'label',
-                    cls : 'control-label col-form-label',
-                    html : this.fieldLabel
+    onDestroy : function(){
+        if(this.view){
+            this.view.setStore(null);
+            this.view.el.removeAllListeners();
+            this.view.el.remove();
+            this.view.purgeListeners();
+        }
+        if(this.list){
+            this.list.dom.innerHTML  = '';
+        }
+        
+        if(this.store){
+            this.store.un('beforeload', this.onBeforeLoad, this);
+            this.store.un('load', this.onLoad, this);
+            this.store.un('loadexception', this.onLoadException, this);
+        }
+        Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
+    },
 
-                },
-                {
-                    cls : 'roo-combobox-wrap ', 
-                    cn: [
-                        combobox
-                    ]
-                }
-            ];
-            
-            var labelCfg = cfg.cn[1];
-            var contentCfg = cfg.cn[2];
-            
-
-            if(this.indicatorpos == 'right'){
-                cfg.cn = [
-                    {
-                        tag: 'label',
-                        'for' :  id,
-                        cls : 'control-label col-form-label',
-                        cn : [
-                            {
-                                tag : 'span',
-                                html : this.fieldLabel
-                            },
-                            required
-                        ]
-                    },
-                    {
-                        cls : "roo-combobox-wrap ",
-                        cn: [
-                            combobox
-                        ]
-                    }
-
-                ];
-                
-                labelCfg = cfg.cn[0];
-                contentCfg = cfg.cn[1];
-            }
-            
-           
-            
-            if(this.labelWidth > 12){
-                labelCfg.style = "width: " + this.labelWidth + 'px';
-            }
-           
-            if(this.labelWidth < 13 && this.labelmd == 0){
-                this.labelmd = this.labelWidth;
-            }
-            
-            if(this.labellg > 0){
-                labelCfg.cls += ' col-lg-' + this.labellg;
-                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
-            }
-            
-            if(this.labelmd > 0){
-                labelCfg.cls += ' col-md-' + this.labelmd;
-                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
-            }
-            
-            if(this.labelsm > 0){
-                labelCfg.cls += ' col-sm-' + this.labelsm;
-                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
-            }
-            
-            if(this.labelxs > 0){
-                labelCfg.cls += ' col-xs-' + this.labelxs;
-                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
-            }
-                
-                
-        } else if ( this.fieldLabel.length) {
-            cfg.cn = [
-               required,
-                {
-                    tag: 'label',
-                    cls : 'control-label',
-                    html : this.fieldLabel
-
-                },
-                {
-                    cls : '', 
-                    cn: [
-                        combobox
-                    ]
-                }
-            ];
-            
-            if(this.indicatorpos == 'right'){
-                cfg.cn = [
-                    {
-                        tag: 'label',
-                        cls : 'control-label',
-                        html : this.fieldLabel,
-                        cn : [
-                            required
-                        ]
-                    },
-                    {
-                        cls : '', 
-                        cn: [
-                            combobox
-                        ]
-                    }
-                ];
-            }
-        } else {
-            cfg.cn = combobox;    
+    // private
+    fireKey : function(e){
+        if(e.isNavKeyPress() && !this.list.isVisible()){
+            this.fireEvent("specialkey", this, e);
         }
+    },
+
+    // private
+    onResize: function(w, h)
+    {
         
         
-        var settings = this;
+//        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
+//        
+//        if(typeof w != 'number'){
+//            // we do not handle it!?!?
+//            return;
+//        }
+//        var tw = this.trigger.getWidth();
+//       // tw += this.addicon ? this.addicon.getWidth() : 0;
+//       // tw += this.editicon ? this.editicon.getWidth() : 0;
+//        var x = w - tw;
+//        this.inputEl().setWidth( this.adjustWidth('input', x));
+//            
+//        //this.trigger.setStyle('left', x+'px');
+//        
+//        if(this.list && this.listWidth === undefined){
+//            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
+//            this.list.setWidth(lw);
+//            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+//        }
         
-        ['xs','sm','md','lg'].map(function(size){
-            if (settings[size]) {
-                cfg.cls += ' col-' + size + '-' + settings[size];
-            }
-        });
+    
         
-        return cfg;
     },
+
+    /**
+     * Allow or prevent the user from directly editing the field text.  If false is passed,
+     * the user will only be able to select from the items defined in the dropdown list.  This method
+     * is the runtime equivalent of setting the 'editable' config option at config time.
+     * @param {Boolean} value True to allow the user to directly edit the field text
+     */
+    setEditable : function(value){
+        if(value == this.editable){
+            return;
+        }
+        this.editable = value;
+        if(!value){
+            this.inputEl().dom.setAttribute('readOnly', true);
+            this.inputEl().on('mousedown', this.onTriggerClick,  this);
+            this.inputEl().addClass('x-combo-noedit');
+        }else{
+            this.inputEl().dom.removeAttribute('readOnly');
+            this.inputEl().un('mousedown', this.onTriggerClick,  this);
+            this.inputEl().removeClass('x-combo-noedit');
+        }
+    },
+
+    // private
     
-    initTouchView : function()
-    {
-        this.renderTouchView();
+    onBeforeLoad : function(combo,opts){
+        if(!this.hasFocus){
+            return;
+        }
+         if (!opts.add) {
+            this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
+         }
+        this.restrictHeight();
+        this.selectedIndex = -1;
+    },
+
+    // private
+    onLoad : function(){
         
-        this.touchViewEl.on('scroll', function(){
-            this.el.dom.scrollTop = 0;
-        }, this);
+        this.hasQuery = false;
         
-        this.originalValue = this.getValue();
+        if(!this.hasFocus){
+            return;
+        }
         
-        this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
+        if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+            this.loading.hide();
+        }
         
-        this.inputEl().on("click", this.showTouchView, this);
-        if (this.triggerEl) {
-            this.triggerEl.on("click", this.showTouchView, this);
+        if(this.store.getCount() > 0){
+            
+            this.expand();
+            this.restrictHeight();
+            if(this.lastQuery == this.allQuery){
+                if(this.editable && !this.tickable){
+                    this.inputEl().dom.select();
+                }
+                
+                if(
+                    !this.selectByValue(this.value, true) &&
+                    this.autoFocus && 
+                    (
+                        !this.store.lastOptions ||
+                        typeof(this.store.lastOptions.add) == 'undefined' || 
+                        this.store.lastOptions.add != true
+                    )
+                ){
+                    this.select(0, true);
+                }
+            }else{
+                if(this.autoFocus){
+                    this.selectNext();
+                }
+                if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
+                    this.taTask.delay(this.typeAheadDelay);
+                }
+            }
+        }else{
+            this.onEmptyResults();
         }
         
+        //this.el.focus();
+    },
+    // private
+    onLoadException : function()
+    {
+        this.hasQuery = false;
         
-        this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
-        this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
+        if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+            this.loading.hide();
+        }
         
-        this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
+        if(this.tickable && this.editable){
+            return;
+        }
         
-        this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
-        this.store.on('load', this.onTouchViewLoad, this);
-        this.store.on('loadexception', this.onTouchViewLoadException, this);
+        this.collapse();
+        // only causes errors at present
+        //Roo.log(this.store.reader.jsonData);
+        //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+            // fixme
+            //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+        //}
         
-        if(this.hiddenName){
-            
-            this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+        
+    },
+    // private
+    onTypeAhead : function(){
+        if(this.store.getCount() > 0){
+            var r = this.store.getAt(0);
+            var newValue = r.data[this.displayField];
+            var len = newValue.length;
+            var selStart = this.getRawValue().length;
             
-            this.hiddenField.dom.value =
-                this.hiddenValue !== undefined ? this.hiddenValue :
-                this.value !== undefined ? this.value : '';
+            if(selStart != len){
+                this.setRawValue(newValue);
+                this.selectText(selStart, newValue.length);
+            }
+        }
+    },
+
+    // private
+    onSelect : function(record, index){
         
-            this.el.dom.removeAttribute('name');
-            this.hiddenField.dom.setAttribute('name', this.hiddenName);
+        if(this.fireEvent('beforeselect', this, record, index) !== false){
+        
+            this.setFromData(index > -1 ? record.data : false);
+            
+            this.collapse();
+            this.fireEvent('select', this, record, index);
+        }
+    },
+
+    /**
+     * Returns the currently selected field value or empty string if no value is set.
+     * @return {String} value The selected value
+     */
+    getValue : function()
+    {
+        if(Roo.isIOS && this.useNativeIOS){
+            return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
         }
         
         if(this.multiple){
-            this.choices = this.el.select('ul.roo-select2-choices', true).first();
-            this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+            return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
         }
         
-        if(this.removable && !this.multiple){
-            var close = this.closeTriggerEl();
-            if(close){
-                close.setVisibilityMode(Roo.Element.DISPLAY).hide();
-                close.on('click', this.removeBtnClick, this, close);
-            }
+        if(this.valueField){
+            return typeof this.value != 'undefined' ? this.value : '';
+        }else{
+            return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
         }
-        /*
-         * fix the bug in Safari iOS8
-         */
-        this.inputEl().on("focus", function(e){
-            document.activeElement.blur();
-        }, this);
-        
-        this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
-        
-        return;
-        
-        
     },
     
-    renderTouchView : function()
+    getRawValue : function()
     {
-        this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
-        this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        if(Roo.isIOS && this.useNativeIOS){
+            return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
+        }
         
-        this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
-        this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        var v = this.inputEl().getValue();
         
-        this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
-        this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-        this.touchViewBodyEl.setStyle('overflow', 'auto');
+        return v;
+    },
+
+    /**
+     * Clears any text/value currently set in the field
+     */
+    clearValue : function(){
         
-        this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
-        this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        if(this.hiddenField){
+            this.hiddenField.dom.value = '';
+        }
+        this.value = '';
+        this.setRawValue('');
+        this.lastSelectionText = '';
+        this.lastData = false;
         
-        this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
-        this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        var close = this.closeTriggerEl();
         
-    },
-    
-    showTouchView : function()
-    {
-        if(this.disabled){
-            return;
+        if(close){
+            close.hide();
         }
         
-        this.touchViewHeaderEl.hide();
-
-        if(this.modalTitle.length){
-            this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
-            this.touchViewHeaderEl.show();
-        }
-
-        this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
-        this.touchViewEl.show();
-
-        this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
+        this.validate();
         
-        //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
-        //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
-
-        var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+    },
 
-        if(this.modalTitle.length){
-            bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+    /**
+     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
+     * will be displayed in the field.  If the value does not match the data value of an existing item,
+     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
+     * Otherwise the field will be blank (although the value will still be set).
+     * @param {String} value The value to match
+     */
+    setValue : function(v)
+    {
+        if(Roo.isIOS && this.useNativeIOS){
+            this.setIOSValue(v);
+            return;
         }
         
-        this.touchViewBodyEl.setHeight(bodyHeight);
-
-        if(this.animate){
-            var _this = this;
-            (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
-        }else{
-            this.touchViewEl.addClass(['in','show']);
+        if(this.multiple){
+            this.syncValue();
+            return;
         }
         
-        if(this._touchViewMask){
-            Roo.get(document.body).addClass("x-body-masked");
-            this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
-            this._touchViewMask.setStyle('z-index', 10000);
-            this._touchViewMask.addClass('show');
+        var text = v;
+        if(this.valueField){
+            var r = this.findRecord(this.valueField, v);
+            if(r){
+                text = r.data[this.displayField];
+            }else if(this.valueNotFoundText !== undefined){
+                text = this.valueNotFoundText;
+            }
         }
+        this.lastSelectionText = text;
+        if(this.hiddenField){
+            this.hiddenField.dom.value = v;
+        }
+        Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
+        this.value = v;
         
-        this.doTouchViewQuery();
+        var close = this.closeTriggerEl();
         
-    },
-    
-    hideTouchView : function()
-    {
-        this.touchViewEl.removeClass(['in','show']);
-
-        if(this.animate){
-            var _this = this;
-            (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
-        }else{
-            this.touchViewEl.setStyle('display', 'none');
+        if(close){
+            (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
         }
         
-        if(this._touchViewMask){
-            this._touchViewMask.removeClass('show');
-            Roo.get(document.body).removeClass("x-body-masked");
-        }
+        this.validate();
     },
+    /**
+     * @property {Object} the last set data for the element
+     */
     
-    setTouchViewValue : function()
-    {
+    lastData : false,
+    /**
+     * Sets the value of the field based on a object which is related to the record format for the store.
+     * @param {Object} value the value to set as. or false on reset?
+     */
+    setFromData : function(o){
+        
         if(this.multiple){
-            this.clearItem();
+            this.addItem(o);
+            return;
+        }
+            
+        var dv = ''; // display value
+        var vv = ''; // value value..
+        this.lastData = o;
+        if (this.displayField) {
+            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+        } else {
+            // this is an error condition!!!
+            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
+        }
         
-            var _this = this;
-
-            Roo.each(this.tickItems, function(o){
-                this.addItem(o);
-            }, this);
+        if(this.valueField){
+            vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
         }
         
-        this.hideTouchView();
-    },
-    
-    doTouchViewQuery : function()
-    {
-        var qe = {
-            query: '',
-            forceAll: true,
-            combo: this,
-            cancel:false
-        };
+        var close = this.closeTriggerEl();
         
-        if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
-            return false;
+        if(close){
+            if(dv.length || vv * 1 > 0){
+                close.show() ;
+                this.blockFocus=true;
+            } else {
+                close.hide();
+            }             
         }
         
-        if(!this.alwaysQuery || this.mode == 'local'){
-            this.onTouchViewLoad();
+        if(this.hiddenField){
+            this.hiddenField.dom.value = vv;
+            
+            this.lastSelectionText = dv;
+            Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
+            this.value = vv;
             return;
         }
+        // no hidden field.. - we store the value in 'value', but still display
+        // display field!!!!
+        this.lastSelectionText = dv;
+        Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
+        this.value = vv;
+        
+        
         
-        this.store.load();
+    },
+    // private
+    reset : function(){
+        // overridden so that last data is reset..
+        
+        if(this.multiple){
+            this.clearItem();
+            return;
+        }
+        
+        this.setValue(this.originalValue);
+        //this.clearInvalid();
+        this.lastData = false;
+        if (this.view) {
+            this.view.clearSelections();
+        }
+        
+        this.validate();
+    },
+    // private
+    findRecord : function(prop, value){
+        var record;
+        if(this.store.getCount() > 0){
+            this.store.each(function(r){
+                if(r.data[prop] == value){
+                    record = r;
+                    return false;
+                }
+                return true;
+            });
+        }
+        return record;
     },
     
-    onTouchViewBeforeLoad : function(combo,opts)
+    getName: function()
     {
-        return;
+        // returns hidden if it's set..
+        if (!this.rendered) {return ''};
+        return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
+        
+    },
+    // private
+    onViewMove : function(e, t){
+        this.inKeyMode = false;
     },
 
     // private
-    onTouchViewLoad : function()
-    {
-        if(this.store.getCount() < 1){
-            this.onTouchViewEmptyResults();
+    onViewOver : function(e, t){
+        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
             return;
         }
+        var item = this.view.findItemFromChild(t);
         
-        this.clearTouchView();
-        
-        var rawValue = this.getRawValue();
-        
-        var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
+        if(item){
+            var index = this.view.indexOf(item);
+            this.select(index, false);
+        }
+    },
+
+    // private
+    onViewClick : function(view, doFocus, el, e)
+    {
+        var index = this.view.getSelectedIndexes()[0];
         
-        this.tickItems = [];
+        var r = this.store.getAt(index);
         
-        this.store.data.each(function(d, rowIndex){
-            var row = this.touchViewListGroup.createChild(template);
+        if(this.tickable){
             
-            if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
-                row.addClass(d.data.cls);
+            if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
+                return;
             }
             
-            if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
-                var cfg = {
-                    data : d.data,
-                    html : d.data[this.displayField]
-                };
+            var rm = false;
+            var _this = this;
+            
+            Roo.each(this.tickItems, function(v,k){
                 
-                if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
-                    row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
+                if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
+                    Roo.log(v);
+                    _this.tickItems.splice(k, 1);
+                    
+                    if(typeof(e) == 'undefined' && view == false){
+                        Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
+                    }
+                    
+                    rm = true;
+                    return;
                 }
-            }
-            row.removeClass('selected');
-            if(!this.multiple && this.valueField &&
-                    typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
-            {
-                // radio buttons..
-                row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
-                row.addClass('selected');
-            }
+            });
             
-            if(this.multiple && this.valueField &&
-                    typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
-            {
-                
-                // checkboxes...
-                row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
-                this.tickItems.push(d.data);
+            if(rm){
+                return;
             }
             
-            row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
+            if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
+                this.tickItems.push(r.data);
+            }
             
-        }, this);
-        
-        var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
-        
-        var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
-
-        if(this.modalTitle.length){
-            bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+            if(typeof(e) == 'undefined' && view == false){
+                Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
+            }
+                    
+            return;
         }
-
-        var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
         
-        if(this.mobile_restrict_height && listHeight < bodyHeight){
-            this.touchViewBodyEl.setHeight(listHeight);
+        if(r){
+            this.onSelect(r, index);
         }
-        
-        var _this = this;
-        
-        if(firstChecked && listHeight > bodyHeight){
-            (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
+        if(doFocus !== false && !this.blockFocus){
+            this.inputEl().focus();
         }
-        
     },
-    
-    onTouchViewLoadException : function()
-    {
-        this.hideTouchView();
+
+    // private
+    restrictHeight : function(){
+        //this.innerList.dom.style.height = '';
+        //var inner = this.innerList.dom;
+        //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
+        //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+        //this.list.beginUpdate();
+        //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
+        this.list.alignTo(this.inputEl(), this.listAlign);
+        this.list.alignTo(this.inputEl(), this.listAlign);
+        //this.list.endUpdate();
     },
-    
-    onTouchViewEmptyResults : function()
-    {
-        this.clearTouchView();
-        
-        this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
+
+    // private
+    onEmptyResults : function(){
         
-        this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
+        if(this.tickable && this.editable){
+            this.hasFocus = false;
+            this.restrictHeight();
+            return;
+        }
         
+        this.collapse();
     },
-    
-    clearTouchView : function()
-    {
-        this.touchViewListGroup.dom.innerHTML = '';
-    },
-    
-    onTouchViewClick : function(e, el, o)
-    {
-        e.preventDefault();
-        
-        var row = o.row;
-        var rowIndex = o.rowIndex;
-        
-        var r = this.store.getAt(rowIndex);
-        
-        if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
-            
-            if(!this.multiple){
-                Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
-                    c.dom.removeAttribute('checked');
-                }, this);
-
-                row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
-
-                this.setFromData(r.data);
-
-                var close = this.closeTriggerEl();
-
-                if(close){
-                    close.show();
-                }
 
-                this.hideTouchView();
-
-                this.fireEvent('select', this, r, rowIndex);
-
-                return;
-            }
+    /**
+     * Returns true if the dropdown list is expanded, else false.
+     */
+    isExpanded : function(){
+        return this.list.isVisible();
+    },
 
-            if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
-                row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
-                this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
-                return;
+    /**
+     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
+     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+     * @param {String} value The data value of the item to select
+     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+     * selected item if it is not currently in view (defaults to true)
+     * @return {Boolean} True if the value matched an item in the list, else false
+     */
+    selectByValue : function(v, scrollIntoView){
+        if(v !== undefined && v !== null){
+            var r = this.findRecord(this.valueField || this.displayField, v);
+            if(r){
+                this.select(this.store.indexOf(r), scrollIntoView);
+                return true;
             }
-
-            row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
-            this.addItem(r.data);
-            this.tickItems.push(r.data);
         }
+        return false;
     },
-    
-    getAutoCreateNativeIOS : function()
-    {
-        var cfg = {
-            cls: 'form-group' //input-group,
-        };
-        
-        var combobox =  {
-            tag: 'select',
-            cls : 'roo-ios-select'
-        };
-        
-        if (this.name) {
-            combobox.name = this.name;
+
+    /**
+     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
+     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+     * @param {Number} index The zero-based index of the list item to select
+     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+     * selected item if it is not currently in view (defaults to true)
+     */
+    select : function(index, scrollIntoView){
+        this.selectedIndex = index;
+        this.view.select(index);
+        if(scrollIntoView !== false){
+            var el = this.view.getNode(index);
+            /*
+             * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
+             */
+            if(el){
+                this.list.scrollChildIntoView(el, false);
+            }
         }
-        
-        if (this.disabled) {
-            combobox.disabled = true;
+    },
+
+    // private
+    selectNext : function(){
+        var ct = this.store.getCount();
+        if(ct > 0){
+            if(this.selectedIndex == -1){
+                this.select(0);
+            }else if(this.selectedIndex < ct-1){
+                this.select(this.selectedIndex+1);
+            }
         }
-        
-        var settings = this;
-        
-        ['xs','sm','md','lg'].map(function(size){
-            if (settings[size]) {
-                cfg.cls += ' col-' + size + '-' + settings[size];
+    },
+
+    // private
+    selectPrev : function(){
+        var ct = this.store.getCount();
+        if(ct > 0){
+            if(this.selectedIndex == -1){
+                this.select(0);
+            }else if(this.selectedIndex != 0){
+                this.select(this.selectedIndex-1);
             }
-        });
-        
-        cfg.cn = combobox;
+        }
+    },
+
+    // private
+    onKeyUp : function(e){
+        if(this.editable !== false && !e.isSpecialKey()){
+            this.lastKey = e.getKey();
+            this.dqTask.delay(this.queryDelay);
+        }
+    },
+
+    // private
+    validateBlur : function(){
+        return !this.list || !this.list.isVisible();   
+    },
+
+    // private
+    initQuery : function(){
         
-        return cfg;
+        var v = this.getRawValue();
         
-    },
-    
-    initIOSView : function()
-    {
-        this.store.on('load', this.onIOSViewLoad, this);
+        if(this.tickable && this.editable){
+            v = this.tickableInputEl().getValue();
+        }
         
-        return;
+        this.doQuery(v);
     },
-    
-    onIOSViewLoad : function()
-    {
-        if(this.store.getCount() < 1){
-            return;
+
+    // private
+    doForce : function(){
+        if(this.inputEl().dom.value.length > 0){
+            this.inputEl().dom.value =
+                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
+             
         }
+    },
+
+    /**
+     * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
+     * query allowing the query action to be canceled if needed.
+     * @param {String} query The SQL query to execute
+     * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
+     * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
+     * saved in the current store (defaults to false)
+     */
+    doQuery : function(q, forceAll){
         
-        this.clearIOSView();
-        
-        if(this.allowBlank) {
-            
-            var default_text = '-- SELECT --';
-            
-            if(this.placeholder.length){
-                default_text = this.placeholder;
-            }
-            
-            if(this.emptyTitle.length){
-                default_text += ' - ' + this.emptyTitle + ' -';
-            }
-            
-            var opt = this.inputEl().createChild({
-                tag: 'option',
-                value : 0,
-                html : default_text
-            });
-            
-            var o = {};
-            o[this.valueField] = 0;
-            o[this.displayField] = default_text;
-            
-            this.ios_options.push({
-                data : o,
-                el : opt
-            });
-            
+        if(q === undefined || q === null){
+            q = '';
+        }
+        var qe = {
+            query: q,
+            forceAll: forceAll,
+            combo: this,
+            cancel:false
+        };
+        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+            return false;
         }
+        q = qe.query;
         
-        this.store.data.each(function(d, rowIndex){
-            
-            var html = '';
-            
-            if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
-                html = d.data[this.displayField];
-            }
-            
-            var value = '';
-            
-            if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
-                value = d.data[this.valueField];
-            }
+        forceAll = qe.forceAll;
+        if(forceAll === true || (q.length >= this.minChars)){
             
-            var option = {
-                tag: 'option',
-                value : value,
-                html : html
-            };
+            this.hasQuery = true;
             
-            if(this.value == d.data[this.valueField]){
-                option['selected'] = true;
+            if(this.lastQuery != q || this.alwaysQuery){
+                this.lastQuery = q;
+                if(this.mode == 'local'){
+                    this.selectedIndex = -1;
+                    if(forceAll){
+                        this.store.clearFilter();
+                    }else{
+                        
+                        if(this.specialFilter){
+                            this.fireEvent('specialfilter', this);
+                            this.onLoad();
+                            return;
+                        }
+                        
+                        this.store.filter(this.displayField, q);
+                    }
+                    
+                    this.store.fireEvent("datachanged", this.store);
+                    
+                    this.onLoad();
+                    
+                    
+                }else{
+                    
+                    this.store.baseParams[this.queryParam] = q;
+                    
+                    var options = {params : this.getParams(q)};
+                    
+                    if(this.loadNext){
+                        options.add = true;
+                        options.params.start = this.page * this.pageSize;
+                    }
+                    
+                    this.store.load(options);
+                    
+                    /*
+                     *  this code will make the page width larger, at the beginning, the list not align correctly, 
+                     *  we should expand the list on onLoad
+                     *  so command out it
+                     */
+//                    this.expand();
+                }
+            }else{
+                this.selectedIndex = -1;
+                this.onLoad();   
             }
-            
-            var opt = this.inputEl().createChild(option);
-            
-            this.ios_options.push({
-                data : d.data,
-                el : opt
-            });
-            
-        }, this);
-        
-        this.inputEl().on('change', function(){
-           this.fireEvent('select', this);
-        }, this);
+        }
         
+        this.loadNext = false;
     },
     
-    clearIOSView: function()
-    {
-        this.inputEl().dom.innerHTML = '';
+    // private
+    getParams : function(q){
+        var p = {};
+        //p[this.queryParam] = q;
         
-        this.ios_options = [];
+        if(this.pageSize){
+            p.start = 0;
+            p.limit = this.pageSize;
+        }
+        return p;
     },
-    
-    setIOSValue: function(v)
-    {
-        this.value = v;
-        
-        if(!this.ios_options){
+
+    /**
+     * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+     */
+    collapse : function(){
+        if(!this.isExpanded()){
             return;
         }
         
-        Roo.each(this.ios_options, function(opts){
-           
-           opts.el.dom.removeAttribute('selected');
-           
-           if(opts.data[this.valueField] != v){
-               return;
-           }
-           
-           opts.el.dom.setAttribute('selected', true);
-           
-        }, this);
-    }
+        this.list.hide();
+        
+        this.hasFocus = false;
+        
+        if(this.tickable){
+            this.okBtn.hide();
+            this.cancelBtn.hide();
+            this.trigger.show();
+            
+            if(this.editable){
+                this.tickableInputEl().dom.value = '';
+                this.tickableInputEl().blur();
+            }
+            
+        }
+        
+        Roo.get(document).un('mousedown', this.collapseIf, this);
+        Roo.get(document).un('mousewheel', this.collapseIf, this);
+        if (!this.editable) {
+            Roo.get(document).un('keydown', this.listKeyPress, this);
+        }
+        this.fireEvent('collapse', this);
+        
+        this.validate();
+    },
+
+    // private
+    collapseIf : function(e){
+        var in_combo  = e.within(this.el);
+        var in_list =  e.within(this.list);
+        var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
+        
+        if (in_combo || in_list || is_list) {
+            //e.stopPropagation();
+            return;
+        }
+        
+        if(this.tickable){
+            this.onTickableFooterButtonClick(e, false, false);
+        }
+
+        this.collapse();
+        
+    },
 
-    /** 
-    * @cfg {Boolean} grow 
-    * @hide 
-    */
-    /** 
-    * @cfg {Number} growMin 
-    * @hide 
-    */
-    /** 
-    * @cfg {Number} growMax 
-    * @hide 
-    */
     /**
-     * @hide
-     * @method autoSize
+     * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
      */
-});
-
-Roo.apply(Roo.bootstrap.ComboBox,  {
-    
-    header : {
-        tag: 'div',
-        cls: 'modal-header',
-        cn: [
-            {
-                tag: 'h4',
-                cls: 'modal-title'
+    expand : function(){
+       
+        if(this.isExpanded() || !this.hasFocus){
+            return;
+        }
+        
+        var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
+        this.list.setWidth(lw);
+        
+        Roo.log('expand');
+        
+        this.list.show();
+        
+        this.restrictHeight();
+        
+        if(this.tickable){
+            
+            this.tickItems = Roo.apply([], this.item);
+            
+            this.okBtn.show();
+            this.cancelBtn.show();
+            this.trigger.hide();
+            
+            if(this.editable){
+                this.tickableInputEl().focus();
             }
-        ]
+            
+        }
+        
+        Roo.get(document).on('mousedown', this.collapseIf, this);
+        Roo.get(document).on('mousewheel', this.collapseIf, this);
+        if (!this.editable) {
+            Roo.get(document).on('keydown', this.listKeyPress, this);
+        }
+        
+        this.fireEvent('expand', this);
     },
-    
-    body : {
-        tag: 'div',
-        cls: 'modal-body',
-        cn: [
-            {
-                tag: 'ul',
-                cls: 'list-group'
+
+    // private
+    // Implements the default empty TriggerField.onTriggerClick function
+    onTriggerClick : function(e)
+    {
+        Roo.log('trigger click');
+        
+        if(this.disabled || !this.triggerList){
+            return;
+        }
+        
+        this.page = 0;
+        this.loadNext = false;
+        
+        if(this.isExpanded()){
+            this.collapse();
+            if (!this.blockFocus) {
+                this.inputEl().focus();
             }
-        ]
-    },
-    
-    listItemRadio : {
-        tag: 'li',
-        cls: 'list-group-item',
-        cn: [
-            {
-                tag: 'span',
-                cls: 'roo-combobox-list-group-item-value'
-            },
-            {
-                tag: 'div',
-                cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
-                cn: [
-                    {
-                        tag: 'input',
-                        type: 'radio'
-                    },
-                    {
-                        tag: 'label'
-                    }
-                ]
+            
+        }else {
+            this.hasFocus = true;
+            if(this.triggerAction == 'all') {
+                this.doQuery(this.allQuery, true);
+            } else {
+                this.doQuery(this.getRawValue());
             }
-        ]
-    },
-    
-    listItemCheckbox : {
-        tag: 'li',
-        cls: 'list-group-item',
-        cn: [
-            {
-                tag: 'span',
-                cls: 'roo-combobox-list-group-item-value'
-            },
-            {
-                tag: 'div',
-                cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
-                cn: [
-                    {
-                        tag: 'input',
-                        type: 'checkbox'
-                    },
-                    {
-                        tag: 'label'
-                    }
-                ]
+            if (!this.blockFocus) {
+                this.inputEl().focus();
             }
-        ]
+        }
     },
     
-    emptyResult : {
-        tag: 'div',
-        cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
+    onTickableTriggerClick : function(e)
+    {
+        if(this.disabled){
+            return;
+        }
+        
+        this.page = 0;
+        this.loadNext = false;
+        this.hasFocus = true;
+        
+        if(this.triggerAction == 'all') {
+            this.doQuery(this.allQuery, true);
+        } else {
+            this.doQuery(this.getRawValue());
+        }
     },
     
-    footer : {
-        tag: 'div',
-        cls: 'modal-footer',
-        cn: [
-            {
-                tag: 'div',
-                cls: 'row',
-                cn: [
-                    {
-                        tag: 'div',
-                        cls: 'col-xs-6 text-left',
-                        cn: {
-                            tag: 'button',
-                            cls: 'btn btn-danger roo-touch-view-cancel',
-                            html: 'Cancel'
-                        }
-                    },
-                    {
-                        tag: 'div',
-                        cls: 'col-xs-6 text-right',
-                        cn: {
-                            tag: 'button',
-                            cls: 'btn btn-success roo-touch-view-ok',
-                            html: 'OK'
-                        }
-                    }
-                ]
-            }
-        ]
+    onSearchFieldClick : function(e)
+    {
+        if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
+            this.onTickableFooterButtonClick(e, false, false);
+            return;
+        }
         
-    }
-});
-
-Roo.apply(Roo.bootstrap.ComboBox,  {
+        if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
+            return;
+        }
+        
+        this.page = 0;
+        this.loadNext = false;
+        this.hasFocus = true;
+        
+        if(this.triggerAction == 'all') {
+            this.doQuery(this.allQuery, true);
+        } else {
+            this.doQuery(this.getRawValue());
+        }
+    },
     
-    touchViewTemplate : {
-        tag: 'div',
-        cls: 'modal fade roo-combobox-touch-view',
-        cn: [
-            {
-                tag: 'div',
-                cls: 'modal-dialog',
-                style : 'position:fixed', // we have to fix position....
-                cn: [
-                    {
-                        tag: 'div',
-                        cls: 'modal-content',
-                        cn: [
-                            Roo.bootstrap.ComboBox.header,
-                            Roo.bootstrap.ComboBox.body,
-                            Roo.bootstrap.ComboBox.footer
-                        ]
-                    }
-                ]
+    listKeyPress : function(e)
+    {
+        //Roo.log('listkeypress');
+        // scroll to first matching element based on key pres..
+        if (e.isSpecialKey()) {
+            return false;
+        }
+        var k = String.fromCharCode(e.getKey()).toUpperCase();
+        //Roo.log(k);
+        var match  = false;
+        var csel = this.view.getSelectedNodes();
+        var cselitem = false;
+        if (csel.length) {
+            var ix = this.view.indexOf(csel[0]);
+            cselitem  = this.store.getAt(ix);
+            if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
+                cselitem = false;
             }
-        ]
-    }
-});/*
- * 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.View
- * @extends Roo.util.Observable
- * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
- * This class also supports single and multi selection modes. <br>
- * Create a data model bound view:
- <pre><code>
- var store = new Roo.data.Store(...);
-
- var view = new Roo.View({
-    el : "my-element",
-    tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
-    singleSelect: true,
-    selectedClass: "ydataview-selected",
-    store: store
- });
-
- // listen for node click?
- view.on("click", function(vw, index, node, e){
- alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
- });
-
- // load XML data
- dataModel.load("foobar.xml");
- </code></pre>
- For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
- * <br><br>
- * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
- * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
- * 
- * Note: old style constructor is still suported (container, template, config)
- * 
- * @constructor
- * Create a new View
- * @param {Object} config The config object
- * 
- */
-Roo.View = function(config, depreciated_tpl, depreciated_config){
-    
-    this.parent = false;
-    
-    if (typeof(depreciated_tpl) == 'undefined') {
-        // new way.. - universal constructor.
-        Roo.apply(this, config);
-        this.el  = Roo.get(this.el);
-    } else {
-        // old format..
-        this.el  = Roo.get(config);
-        this.tpl = depreciated_tpl;
-        Roo.apply(this, depreciated_config);
-    }
-    this.wrapEl  = this.el.wrap().wrap();
-    ///this.el = this.wrapEla.appendChild(document.createElement("div"));
+            
+        }
+        
+        this.store.each(function(v) { 
+            if (cselitem) {
+                // start at existing selection.
+                if (cselitem.id == v.id) {
+                    cselitem = false;
+                }
+                return true;
+            }
+                
+            if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
+                match = this.store.indexOf(v);
+                return false;
+            }
+            return true;
+        }, this);
+        
+        if (match === false) {
+            return true; // no more action?
+        }
+        // scroll to?
+        this.view.select(match);
+        var sn = Roo.get(this.view.getSelectedNodes()[0]);
+        sn.scrollIntoView(sn.dom.parentNode, false);
+    },
     
+    onViewScroll : function(e, t){
+        
+        if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
+            return;
+        }
+        
+        this.hasQuery = true;
+        
+        this.loading = this.list.select('.loading', true).first();
+        
+        if(this.loading === null){
+            this.list.createChild({
+                tag: 'div',
+                cls: 'loading roo-select2-more-results roo-select2-active',
+                html: 'Loading more results...'
+            });
+            
+            this.loading = this.list.select('.loading', true).first();
+            
+            this.loading.setVisibilityMode(Roo.Element.DISPLAY);
+            
+            this.loading.hide();
+        }
+        
+        this.loading.show();
+        
+        var _combo = this;
+        
+        this.page++;
+        this.loadNext = true;
+        
+        (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+        
+        return;
+    },
     
-    if(typeof(this.tpl) == "string"){
-        this.tpl = new Roo.Template(this.tpl);
-    } else {
-        // support xtype ctors..
-        this.tpl = new Roo.factory(this.tpl, Roo);
-    }
+    addItem : function(o)
+    {   
+        var dv = ''; // display value
+        
+        if (this.displayField) {
+            dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+        } else {
+            // this is an error condition!!!
+            Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
+        }
+        
+        if(!dv.length){
+            return;
+        }
+        
+        var choice = this.choices.createChild({
+            tag: 'li',
+            cls: 'roo-select2-search-choice',
+            cn: [
+                {
+                    tag: 'div',
+                    html: dv
+                },
+                {
+                    tag: 'a',
+                    href: '#',
+                    cls: 'roo-select2-search-choice-close fa fa-times',
+                    tabindex: '-1'
+                }
+            ]
+            
+        }, this.searchField);
+        
+        var close = choice.select('a.roo-select2-search-choice-close', true).first();
+        
+        close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
+        
+        this.item.push(o);
+        
+        this.lastData = o;
+        
+        this.syncValue();
+        
+        this.inputEl().dom.value = '';
+        
+        this.validate();
+    },
     
+    onRemoveItem : function(e, _self, o)
+    {
+        e.preventDefault();
+        
+        this.lastItem = Roo.apply([], this.item);
+        
+        var index = this.item.indexOf(o.data) * 1;
+        
+        if( index < 0){
+            Roo.log('not this item?!');
+            return;
+        }
+        
+        this.item.splice(index, 1);
+        o.item.remove();
+        
+        this.syncValue();
+        
+        this.fireEvent('remove', this, e);
+        
+        this.validate();
+        
+    },
     
-    this.tpl.compile();
-    
-    /** @private */
-    this.addEvents({
-        /**
-         * @event beforeclick
-         * Fires before a click is processed. Returns false to cancel the default action.
-         * @param {Roo.View} this
-         * @param {Number} index The index of the target node
-         * @param {HTMLElement} node The target node
-         * @param {Roo.EventObject} e The raw event object
-         */
-            "beforeclick" : true,
-        /**
-         * @event click
-         * Fires when a template node is clicked.
-         * @param {Roo.View} this
-         * @param {Number} index The index of the target node
-         * @param {HTMLElement} node The target node
-         * @param {Roo.EventObject} e The raw event object
-         */
-            "click" : true,
-        /**
-         * @event dblclick
-         * Fires when a template node is double clicked.
-         * @param {Roo.View} this
-         * @param {Number} index The index of the target node
-         * @param {HTMLElement} node The target node
-         * @param {Roo.EventObject} e The raw event object
-         */
-            "dblclick" : true,
-        /**
-         * @event contextmenu
-         * Fires when a template node is right clicked.
-         * @param {Roo.View} this
-         * @param {Number} index The index of the target node
-         * @param {HTMLElement} node The target node
-         * @param {Roo.EventObject} e The raw event object
-         */
-            "contextmenu" : true,
-        /**
-         * @event selectionchange
-         * Fires when the selected nodes change.
-         * @param {Roo.View} this
-         * @param {Array} selections Array of the selected nodes
-         */
-            "selectionchange" : true,
-    
-        /**
-         * @event beforeselect
-         * Fires before a selection is made. If any handlers return false, the selection is cancelled.
-         * @param {Roo.View} this
-         * @param {HTMLElement} node The node to be selected
-         * @param {Array} selections Array of currently selected nodes
-         */
-            "beforeselect" : true,
-        /**
-         * @event preparedata
-         * Fires on every row to render, to allow you to change the data.
-         * @param {Roo.View} this
-         * @param {Object} data to be rendered (change this)
-         */
-          "preparedata" : true
-          
-          
-        });
-
+    syncValue : function()
+    {
+        if(!this.item.length){
+            this.clearValue();
+            return;
+        }
+            
+        var value = [];
+        var _this = this;
+        Roo.each(this.item, function(i){
+            if(_this.valueField){
+                value.push(i[_this.valueField]);
+                return;
+            }
 
+            value.push(i);
+        });
 
-    this.el.on({
-        "click": this.onClick,
-        "dblclick": this.onDblClick,
-        "contextmenu": this.onContextMenu,
-        scope:this
-    });
+        this.value = value.join(',');
 
-    this.selections = [];
-    this.nodes = [];
-    this.cmp = new Roo.CompositeElementLite([]);
-    if(this.store){
-        this.store = Roo.factory(this.store, Roo.data);
-        this.setStore(this.store, true);
-    }
-    
-    if ( this.footer && this.footer.xtype) {
-           
-         var fctr = this.wrapEl.appendChild(document.createElement("div"));
+        if(this.hiddenField){
+            this.hiddenField.dom.value = this.value;
+        }
         
-        this.footer.dataSource = this.store;
-        this.footer.container = fctr;
-        this.footer = Roo.factory(this.footer, Roo);
-        fctr.insertFirst(this.el);
+        this.store.fireEvent("datachanged", this.store);
         
-        // this is a bit insane - as the paging toolbar seems to detach the el..
-//        dom.parentNode.parentNode.parentNode
-         // they get detached?
-    }
-    
-    
-    Roo.View.superclass.constructor.call(this);
-    
-    
-};
-
-Roo.extend(Roo.View, Roo.util.Observable, {
-    
-     /**
-     * @cfg {Roo.data.Store} store Data store to load data from.
-     */
-    store : false,
-    
-    /**
-     * @cfg {String|Roo.Element} el The container element.
-     */
-    el : '',
-    
-    /**
-     * @cfg {String|Roo.Template} tpl The template used by this View 
-     */
-    tpl : false,
-    /**
-     * @cfg {String} dataName the named area of the template to use as the data area
-     *                          Works with domtemplates roo-name="name"
-     */
-    dataName: false,
-    /**
-     * @cfg {String} selectedClass The css class to add to selected nodes
-     */
-    selectedClass : "x-view-selected",
-     /**
-     * @cfg {String} emptyText The empty text to show when nothing is loaded.
-     */
-    emptyText : "",
-    
-    /**
-     * @cfg {String} text to display on mask (default Loading)
-     */
-    mask : false,
-    /**
-     * @cfg {Boolean} multiSelect Allow multiple selection
-     */
-    multiSelect : false,
-    /**
-     * @cfg {Boolean} singleSelect Allow single selection
-     */
-    singleSelect:  false,
-    
-    /**
-     * @cfg {Boolean} toggleSelect - selecting 
-     */
-    toggleSelect : false,
-    
-    /**
-     * @cfg {Boolean} tickable - selecting 
-     */
-    tickable : false,
-    
-    /**
-     * Returns the element this view is bound to.
-     * @return {Roo.Element}
-     */
-    getEl : function(){
-        return this.wrapEl;
+        this.validate();
     },
     
-    
-
-    /**
-     * Refreshes the view. - called by datachanged on the store. - do not call directly.
-     */
-    refresh : function(){
-        //Roo.log('refresh');
-        var t = this.tpl;
+    clearItem : function()
+    {
+        if(!this.multiple){
+            return;
+        }
         
-        // if we are using something like 'domtemplate', then
-        // the what gets used is:
-        // t.applySubtemplate(NAME, data, wrapping data..)
-        // the outer template then get' applied with
-        //     the store 'extra data'
-        // and the body get's added to the
-        //      roo-name="data" node?
-        //      <span class='roo-tpl-{name}'></span> ?????
+        this.item = [];
         
+        Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
+           c.remove();
+        });
         
+        this.syncValue();
         
-        this.clearSelections();
-        this.el.update("");
-        var html = [];
-        var records = this.store.getRange();
-        if(records.length < 1) {
-            
-            // is this valid??  = should it render a template??
-            
-            this.el.update(this.emptyText);
-            return;
+        this.validate();
+        
+        if(this.tickable && !Roo.isTouch){
+            this.view.refresh();
         }
-        var el = this.el;
-        if (this.dataName) {
-            this.el.update(t.apply(this.store.meta)); //????
-            el = this.el.child('.roo-tpl-' + this.dataName);
+    },
+    
+    inputEl: function ()
+    {
+        if(Roo.isIOS && this.useNativeIOS){
+            return this.el.select('select.roo-ios-select', true).first();
         }
         
-        for(var i = 0, len = records.length; i < len; i++){
-            var data = this.prepareData(records[i].data, i, records[i]);
-            this.fireEvent("preparedata", this, data, i, records[i]);
-            
-            var d = Roo.apply({}, data);
-            
-            if(this.tickable){
-                Roo.apply(d, {'roo-id' : Roo.id()});
-                
-                var _this = this;
-            
-                Roo.each(this.parent.item, function(item){
-                    if(item[_this.parent.valueField] != data[_this.parent.valueField]){
-                        return;
-                    }
-                    Roo.apply(d, {'roo-data-checked' : 'checked'});
-                });
-            }
-            
-            html[html.length] = Roo.util.Format.trim(
-                this.dataName ?
-                    t.applySubtemplate(this.dataName, d, this.store.meta) :
-                    t.apply(d)
-            );
+        if(Roo.isTouch && this.mobileTouchView){
+            return this.el.select('input.form-control',true).first();
         }
         
+        if(this.tickable){
+            return this.searchField;
+        }
         
-        
-        el.update(html.join(""));
-        this.nodes = el.dom.childNodes;
-        this.updateIndexes(0);
-    },
-    
-
-    /**
-     * Function to override to reformat the data that is sent to
-     * the template for each node.
-     * DEPRICATED - use the preparedata event handler.
-     * @param {Array/Object} data The raw data (array of colData for a data model bound view or
-     * a JSON object for an UpdateManager bound view).
-     */
-    prepareData : function(data, index, record)
-    {
-        this.fireEvent("preparedata", this, data, index, record);
-        return data;
-    },
-
-    onUpdate : function(ds, record){
-        // Roo.log('on update');   
-        this.clearSelections();
-        var index = this.store.indexOf(record);
-        var n = this.nodes[index];
-        this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
-        n.parentNode.removeChild(n);
-        this.updateIndexes(index, index);
+        return this.el.select('input.form-control',true).first();
     },
-
-    
     
-// --------- FIXME     
-    onAdd : function(ds, records, index)
+    onTickableFooterButtonClick : function(e, btn, el)
     {
-        //Roo.log(['on Add', ds, records, index] );        
-        this.clearSelections();
-        if(this.nodes.length == 0){
-            this.refresh();
+        e.preventDefault();
+        
+        this.lastItem = Roo.apply([], this.item);
+        
+        if(btn && btn.name == 'cancel'){
+            this.tickItems = Roo.apply([], this.item);
+            this.collapse();
             return;
         }
-        var n = this.nodes[index];
-        for(var i = 0, len = records.length; i < len; i++){
-            var d = this.prepareData(records[i].data, i, records[i]);
-            if(n){
-                this.tpl.insertBefore(n, d);
-            }else{
-                
-                this.tpl.append(this.el, d);
-            }
-        }
-        this.updateIndexes(index);
-    },
-
-    onRemove : function(ds, record, index){
-       // Roo.log('onRemove');
-        this.clearSelections();
-        var el = this.dataName  ?
-            this.el.child('.roo-tpl-' + this.dataName) :
-            this.el; 
         
-        el.dom.removeChild(this.nodes[index]);
-        this.updateIndexes(index);
-    },
-
-    /**
-     * Refresh an individual node.
-     * @param {Number} index
-     */
-    refreshNode : function(index){
-        this.onUpdate(this.store, this.store.getAt(index));
-    },
-
-    updateIndexes : function(startIndex, endIndex){
-        var ns = this.nodes;
-        startIndex = startIndex || 0;
-        endIndex = endIndex || ns.length - 1;
-        for(var i = startIndex; i <= endIndex; i++){
-            ns[i].nodeIndex = i;
-        }
+        this.clearItem();
+        
+        var _this = this;
+        
+        Roo.each(this.tickItems, function(o){
+            _this.addItem(o);
+        });
+        
+        this.collapse();
+        
     },
-
-    /**
-     * Changes the data store this view uses and refresh the view.
-     * @param {Store} store
-     */
-    setStore : function(store, initial){
-        if(!initial && this.store){
-            this.store.un("datachanged", this.refresh);
-            this.store.un("add", this.onAdd);
-            this.store.un("remove", this.onRemove);
-            this.store.un("update", this.onUpdate);
-            this.store.un("clear", this.refresh);
-            this.store.un("beforeload", this.onBeforeLoad);
-            this.store.un("load", this.onLoad);
-            this.store.un("loadexception", this.onLoad);
+    
+    validate : function()
+    {
+        if(this.getVisibilityEl().hasClass('hidden')){
+            return true;
         }
-        if(store){
-          
-            store.on("datachanged", this.refresh, this);
-            store.on("add", this.onAdd, this);
-            store.on("remove", this.onRemove, this);
-            store.on("update", this.onUpdate, this);
-            store.on("clear", this.refresh, this);
-            store.on("beforeload", this.onBeforeLoad, this);
-            store.on("load", this.onLoad, this);
-            store.on("loadexception", this.onLoad, this);
+        
+        var v = this.getRawValue();
+        
+        if(this.multiple){
+            v = this.getValue();
         }
         
-        if(store){
-            this.refresh();
+        if(this.disabled || this.allowBlank || v.length){
+            this.markValid();
+            return true;
         }
+        
+        this.markInvalid();
+        return false;
     },
-    /**
-     * onbeforeLoad - masks the loading area.
-     *
-     */
-    onBeforeLoad : function(store,opts)
+    
+    tickableInputEl : function()
     {
-         //Roo.log('onBeforeLoad');   
-        if (!opts.add) {
-            this.el.update("");
+        if(!this.tickable || !this.editable){
+            return this.inputEl();
         }
-        this.el.mask(this.mask ? this.mask : "Loading" ); 
-    },
-    onLoad : function ()
-    {
-        this.el.unmask();
+        
+        return this.inputEl().select('.roo-select2-search-field-input', true).first();
     },
     
-
-    /**
-     * Returns the template node the passed child belongs to or null if it doesn't belong to one.
-     * @param {HTMLElement} node
-     * @return {HTMLElement} The template node
-     */
-    findItemFromChild : function(node){
-        var el = this.dataName  ?
-            this.el.child('.roo-tpl-' + this.dataName,true) :
-            this.el.dom; 
+    
+    getAutoCreateTouchView : function()
+    {
+        var id = Roo.id();
         
-        if(!node || node.parentNode == el){
-                   return node;
-           }
-           var p = node.parentNode;
-           while(p && p != el){
-            if(p.parentNode == el){
-               return p;
-            }
-            p = p.parentNode;
-        }
-           return null;
-    },
-
-    /** @ignore */
-    onClick : function(e){
-        var item = this.findItemFromChild(e.getTarget());
-        if(item){
-            var index = this.indexOf(item);
-            if(this.onItemClick(item, index, e) !== false){
-                this.fireEvent("click", this, index, item, e);
-            }
-        }else{
-            this.clearSelections();
+        var cfg = {
+            cls: 'form-group' //input-group
+        };
+        
+        var input =  {
+            tag: 'input',
+            id : id,
+            type : this.inputType,
+            cls : 'form-control x-combo-noedit',
+            autocomplete: 'new-password',
+            placeholder : this.placeholder || '',
+            readonly : true
+        };
+        
+        if (this.name) {
+            input.name = this.name;
         }
-    },
-
-    /** @ignore */
-    onContextMenu : function(e){
-        var item = this.findItemFromChild(e.getTarget());
-        if(item){
-            this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
+        
+        if (this.size) {
+            input.cls += ' input-' + this.size;
         }
-    },
-
-    /** @ignore */
-    onDblClick : function(e){
-        var item = this.findItemFromChild(e.getTarget());
-        if(item){
-            this.fireEvent("dblclick", this, this.indexOf(item), item, e);
+        
+        if (this.disabled) {
+            input.disabled = true;
         }
-    },
-
-    onItemClick : function(item, index, e)
-    {
-        if(this.fireEvent("beforeclick", this, index, item, e) === false){
-            return false;
+        
+        var inputblock = {
+            cls : 'roo-combobox-wrap',
+            cn : [
+                input
+            ]
+        };
+        
+        if(this.before){
+            inputblock.cls += ' input-group';
+            
+            inputblock.cn.unshift({
+                tag :'span',
+                cls : 'input-group-addon input-group-prepend input-group-text',
+                html : this.before
+            });
         }
-        if (this.toggleSelect) {
-            var m = this.isSelected(item) ? 'unselect' : 'select';
-            //Roo.log(m);
-            var _t = this;
-            _t[m](item, true, false);
-            return true;
+        
+        if(this.removable && !this.multiple){
+            inputblock.cls += ' roo-removable';
+            
+            inputblock.cn.push({
+                tag: 'button',
+                html : 'x',
+                cls : 'roo-combo-removable-btn close'
+            });
         }
-        if(this.multiSelect || this.singleSelect){
-            if(this.multiSelect && e.shiftKey && this.lastSelection){
-                this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
-            }else{
-                this.select(item, this.multiSelect && e.ctrlKey);
-                this.lastSelection = item;
-            }
+
+        if(this.hasFeedback && !this.allowBlank){
             
-            if(!this.tickable){
-                e.preventDefault();
-            }
+            inputblock.cls += ' has-feedback';
+            
+            inputblock.cn.push({
+                tag: 'span',
+                cls: 'glyphicon form-control-feedback'
+            });
             
         }
-        return true;
-    },
-
-    /**
-     * Get the number of selected nodes.
-     * @return {Number}
-     */
-    getSelectionCount : function(){
-        return this.selections.length;
-    },
-
-    /**
-     * Get the currently selected nodes.
-     * @return {Array} An array of HTMLElements
-     */
-    getSelectedNodes : function(){
-        return this.selections;
-    },
-
-    /**
-     * Get the indexes of the selected nodes.
-     * @return {Array}
-     */
-    getSelectedIndexes : function(){
-        var indexes = [], s = this.selections;
-        for(var i = 0, len = s.length; i < len; i++){
-            indexes.push(s[i].nodeIndex);
+        
+        if (this.after) {
+            
+            inputblock.cls += (this.before) ? '' : ' input-group';
+            
+            inputblock.cn.push({
+                tag :'span',
+                cls : 'input-group-addon input-group-append input-group-text',
+                html : this.after
+            });
         }
-        return indexes;
-    },
 
-    /**
-     * Clear all selections
-     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
-     */
-    clearSelections : function(suppressEvent){
-        if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
-            this.cmp.elements = this.selections;
-            this.cmp.removeClass(this.selectedClass);
-            this.selections = [];
-            if(!suppressEvent){
-                this.fireEvent("selectionchange", this, this.selections);
-            }
-        }
-    },
+        
+        var ibwrap = inputblock;
+        
+        if(this.multiple){
+            ibwrap = {
+                tag: 'ul',
+                cls: 'roo-select2-choices',
+                cn:[
+                    {
+                        tag: 'li',
+                        cls: 'roo-select2-search-field',
+                        cn: [
 
-    /**
-     * Returns true if the passed node is selected
-     * @param {HTMLElement/Number} node The node or node index
-     * @return {Boolean}
-     */
-    isSelected : function(node){
-        var s = this.selections;
-        if(s.length < 1){
-            return false;
+                            inputblock
+                        ]
+                    }
+                ]
+            };
+        
+            
         }
-        node = this.getNode(node);
-        return s.indexOf(node) !== -1;
-    },
-
-    /**
-     * Selects nodes.
-     * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
-     * @param {Boolean} keepExisting (optional) true to keep existing selections
-     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
-     */
-    select : function(nodeInfo, keepExisting, suppressEvent){
-        if(nodeInfo instanceof Array){
-            if(!keepExisting){
-                this.clearSelections(true);
-            }
-            for(var i = 0, len = nodeInfo.length; i < len; i++){
-                this.select(nodeInfo[i], true, true);
+        
+        var combobox = {
+            cls: 'roo-select2-container input-group roo-touchview-combobox ',
+            cn: [
+                {
+                    tag: 'input',
+                    type : 'hidden',
+                    cls: 'form-hidden-field'
+                },
+                ibwrap
+            ]
+        };
+        
+        if(!this.multiple && this.showToggleBtn){
+            
+            var caret = {
+                cls: 'caret'
+            };
+            
+            if (this.caret != false) {
+                caret = {
+                     tag: 'i',
+                     cls: 'fa fa-' + this.caret
+                };
+                
             }
-            return;
-        } 
-        var node = this.getNode(nodeInfo);
-        if(!node || this.isSelected(node)){
-            return; // already selected.
-        }
-        if(!keepExisting){
-            this.clearSelections(true);
+            
+            combobox.cn.push({
+                tag :'span',
+                cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
+                cn : [
+                    Roo.bootstrap.version == 3 ? caret : '',
+                    {
+                        tag: 'span',
+                        cls: 'combobox-clear',
+                        cn  : [
+                            {
+                                tag : 'i',
+                                cls: 'icon-remove'
+                            }
+                        ]
+                    }
+                ]
+
+            })
         }
         
-        if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
-            Roo.fly(node).addClass(this.selectedClass);
-            this.selections.push(node);
-            if(!suppressEvent){
-                this.fireEvent("selectionchange", this, this.selections);
-            }
+        if(this.multiple){
+            combobox.cls += ' roo-select2-container-multi';
         }
         
+        var required =  this.allowBlank ?  {
+                    tag : 'i',
+                    style: 'display: none'
+                } : {
+                   tag : 'i',
+                   cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
+                   tooltip : 'This field is required'
+                };
         
-    },
-      /**
-     * Unselects nodes.
-     * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
-     * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
-     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
-     */
-    unselect : function(nodeInfo, keepExisting, suppressEvent)
-    {
-        if(nodeInfo instanceof Array){
-            Roo.each(this.selections, function(s) {
-                this.unselect(s, nodeInfo);
-            }, this);
-            return;
-        }
-        var node = this.getNode(nodeInfo);
-        if(!node || !this.isSelected(node)){
-            //Roo.log("not selected");
-            return; // not selected.
-        }
-        // fireevent???
-        var ns = [];
-        Roo.each(this.selections, function(s) {
-            if (s == node ) {
-                Roo.fly(node).removeClass(this.selectedClass);
-
-                return;
-            }
-            ns.push(s);
-        },this);
+        var align = this.labelAlign || this.parentLabelAlign();
         
-        this.selections= ns;
-        this.fireEvent("selectionchange", this, this.selections);
-    },
+        if (align ==='left' && this.fieldLabel.length) {
 
-    /**
-     * Gets a template node.
-     * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
-     * @return {HTMLElement} The node or null if it wasn't found
-     */
-    getNode : function(nodeInfo){
-        if(typeof nodeInfo == "string"){
-            return document.getElementById(nodeInfo);
-        }else if(typeof nodeInfo == "number"){
-            return this.nodes[nodeInfo];
-        }
-        return nodeInfo;
-    },
+            cfg.cn = [
+                required,
+                {
+                    tag: 'label',
+                    cls : 'control-label col-form-label',
+                    html : this.fieldLabel
 
-    /**
-     * Gets a range template nodes.
-     * @param {Number} startIndex
-     * @param {Number} endIndex
-     * @return {Array} An array of nodes
-     */
-    getNodes : function(start, end){
-        var ns = this.nodes;
-        start = start || 0;
-        end = typeof end == "undefined" ? ns.length - 1 : end;
-        var nodes = [];
-        if(start <= end){
-            for(var i = start; i <= end; i++){
-                nodes.push(ns[i]);
-            }
-        } else{
-            for(var i = start; i >= end; i--){
-                nodes.push(ns[i]);
-            }
-        }
-        return nodes;
-    },
-
-    /**
-     * Finds the index of the passed node
-     * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
-     * @return {Number} The index of the node or -1
-     */
-    indexOf : function(node){
-        node = this.getNode(node);
-        if(typeof node.nodeIndex == "number"){
-            return node.nodeIndex;
-        }
-        var ns = this.nodes;
-        for(var i = 0, len = ns.length; i < len; i++){
-            if(ns[i] == node){
-                return i;
-            }
-        }
-        return -1;
-    }
-});
-/*
- * - LGPL
- *
- * based on jquery fullcalendar
- * 
- */
-
-Roo.bootstrap = Roo.bootstrap || {};
-/**
- * @class Roo.bootstrap.Calendar
- * @extends Roo.bootstrap.Component
- * Bootstrap Calendar class
- * @cfg {Boolean} loadMask (true|false) default false
- * @cfg {Object} header generate the user specific header of the calendar, default false
-
- * @constructor
- * Create a new Container
- * @param {Object} config The config object
- */
-
-
-
-Roo.bootstrap.Calendar = function(config){
-    Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
-     this.addEvents({
-        /**
-            * @event select
-            * Fires when a date is selected
-            * @param {DatePicker} this
-            * @param {Date} date The selected date
-            */
-        'select': true,
-        /**
-            * @event monthchange
-            * Fires when the displayed month changes 
-            * @param {DatePicker} this
-            * @param {Date} date The selected month
-            */
-        'monthchange': true,
-        /**
-            * @event evententer
-            * Fires when mouse over an event
-            * @param {Calendar} this
-            * @param {event} Event
-            */
-        'evententer': true,
-        /**
-            * @event eventleave
-            * Fires when the mouse leaves an
-            * @param {Calendar} this
-            * @param {event}
-            */
-        'eventleave': true,
-        /**
-            * @event eventclick
-            * Fires when the mouse click an
-            * @param {Calendar} this
-            * @param {event}
-            */
-        'eventclick': true
-        
-    });
-
-};
+                },
+                {
+                    cls : 'roo-combobox-wrap ', 
+                    cn: [
+                        combobox
+                    ]
+                }
+            ];
+            
+            var labelCfg = cfg.cn[1];
+            var contentCfg = cfg.cn[2];
+            
 
-Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
-    
-     /**
-     * @cfg {Number} startDay
-     * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
-     */
-    startDay : 0,
-    
-    loadMask : false,
-    
-    header : false,
-      
-    getAutoCreate : function(){
-        
-        
-        var fc_button = function(name, corner, style, content ) {
-            return Roo.apply({},{
-                tag : 'span',
-                cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
-                         (corner.length ?
-                            'fc-corner-' + corner.split(' ').join(' fc-corner-') :
-                            ''
-                        ),
-                html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
-                unselectable: 'on'
-            });
-        };
-        
-        var header = {};
-        
-        if(!this.header){
-            header = {
-                tag : 'table',
-                cls : 'fc-header',
-                style : 'width:100%',
-                cn : [
+            if(this.indicatorpos == 'right'){
+                cfg.cn = [
                     {
-                        tag: 'tr',
+                        tag: 'label',
+                        'for' :  id,
+                        cls : 'control-label col-form-label',
                         cn : [
                             {
-                                tag : 'td',
-                                cls : 'fc-header-left',
-                                cn : [
-                                    fc_button('prev', 'left', 'arrow', '&#8249;' ),
-                                    fc_button('next', 'right', 'arrow', '&#8250;' ),
-                                    { tag: 'span', cls: 'fc-header-space' },
-                                    fc_button('today', 'left right', '', 'today' )  // neds state disabled..
-
-
-                                ]
-                            },
-
-                            {
-                                tag : 'td',
-                                cls : 'fc-header-center',
-                                cn : [
-                                    {
-                                        tag: 'span',
-                                        cls: 'fc-header-title',
-                                        cn : {
-                                            tag: 'H2',
-                                            html : 'month / year'
-                                        }
-                                    }
-
-                                ]
+                                tag : 'span',
+                                html : this.fieldLabel
                             },
-                            {
-                                tag : 'td',
-                                cls : 'fc-header-right',
-                                cn : [
-                              /*      fc_button('month', 'left', '', 'month' ),
-                                    fc_button('week', '', '', 'week' ),
-                                    fc_button('day', 'right', '', 'day' )
-                                */    
-
-                                ]
-                            }
-
+                            required
                         ]
-                    }
-                ]
-            };
-        }
-        
-        header = this.header;
-        
-       
-        var cal_heads = function() {
-            var ret = [];
-            // fixme - handle this.
-            
-            for (var i =0; i < Date.dayNames.length; i++) {
-                var d = Date.dayNames[i];
-                ret.push({
-                    tag: 'th',
-                    cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
-                    html : d.substring(0,3)
-                });
-                
-            }
-            ret[0].cls += ' fc-first';
-            ret[6].cls += ' fc-last';
-            return ret;
-        };
-        var cal_cell = function(n) {
-            return  {
-                tag: 'td',
-                cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
-                cn : [
+                    },
                     {
-                        cn : [
-                            {
-                                cls: 'fc-day-number',
-                                html: 'D'
-                            },
-                            {
-                                cls: 'fc-day-content',
-                             
-                                cn : [
-                                     {
-                                        style: 'position: relative;' // height: 17px;
-                                    }
-                                ]
-                            }
-                            
-                            
+                        cls : "roo-combobox-wrap ",
+                        cn: [
+                            combobox
                         ]
                     }
-                ]
+
+                ];
                 
+                labelCfg = cfg.cn[0];
+                contentCfg = cfg.cn[1];
             }
-        };
-        var cal_rows = function() {
             
-            var ret = [];
-            for (var r = 0; r < 6; r++) {
-                var row= {
-                    tag : 'tr',
-                    cls : 'fc-week',
-                    cn : []
-                };
-                
-                for (var i =0; i < Date.dayNames.length; i++) {
-                    var d = Date.dayNames[i];
-                    row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
-
-                }
-                row.cn[0].cls+=' fc-first';
-                row.cn[0].cn[0].style = 'min-height:90px';
-                row.cn[6].cls+=' fc-last';
-                ret.push(row);
-                
+           
+            
+            if(this.labelWidth > 12){
+                labelCfg.style = "width: " + this.labelWidth + 'px';
+            }
+           
+            if(this.labelWidth < 13 && this.labelmd == 0){
+                this.labelmd = this.labelWidth;
             }
-            ret[0].cls += ' fc-first';
-            ret[4].cls += ' fc-prev-last';
-            ret[5].cls += ' fc-last';
-            return ret;
             
-        };
-        
-        var cal_table = {
-            tag: 'table',
-            cls: 'fc-border-separate',
-            style : 'width:100%',
-            cellspacing  : 0,
-            cn : [
-                { 
-                    tag: 'thead',
-                    cn : [
-                        { 
-                            tag: 'tr',
-                            cls : 'fc-first fc-last',
-                            cn : cal_heads()
-                        }
-                    ]
+            if(this.labellg > 0){
+                labelCfg.cls += ' col-lg-' + this.labellg;
+                contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+            }
+            
+            if(this.labelmd > 0){
+                labelCfg.cls += ' col-md-' + this.labelmd;
+                contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+            }
+            
+            if(this.labelsm > 0){
+                labelCfg.cls += ' col-sm-' + this.labelsm;
+                contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+            }
+            
+            if(this.labelxs > 0){
+                labelCfg.cls += ' col-xs-' + this.labelxs;
+                contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+            }
+                
+                
+        } else if ( this.fieldLabel.length) {
+            cfg.cn = [
+               required,
+                {
+                    tag: 'label',
+                    cls : 'control-label',
+                    html : this.fieldLabel
+
                 },
-                { 
-                    tag: 'tbody',
-                    cn : cal_rows()
-                }
-                  
-            ]
-        };
-         
-         var cfg = {
-            cls : 'fc fc-ltr',
-            cn : [
-                header,
                 {
-                    cls : 'fc-content',
-                    style : "position: relative;",
-                    cn : [
-                        {
-                            cls : 'fc-view fc-view-month fc-grid',
-                            style : 'position: relative',
-                            unselectable : 'on',
-                            cn : [
-                                {
-                                    cls : 'fc-event-container',
-                                    style : 'position:absolute;z-index:8;top:0;left:0;'
-                                },
-                                cal_table
-                            ]
-                        }
+                    cls : '', 
+                    cn: [
+                        combobox
                     ]
-    
                 }
-           ] 
+            ];
             
-        };
+            if(this.indicatorpos == 'right'){
+                cfg.cn = [
+                    {
+                        tag: 'label',
+                        cls : 'control-label',
+                        html : this.fieldLabel,
+                        cn : [
+                            required
+                        ]
+                    },
+                    {
+                        cls : '', 
+                        cn: [
+                            combobox
+                        ]
+                    }
+                ];
+            }
+        } else {
+            cfg.cn = combobox;    
+        }
         
-         
+        
+        var settings = this;
+        
+        ['xs','sm','md','lg'].map(function(size){
+            if (settings[size]) {
+                cfg.cls += ' col-' + size + '-' + settings[size];
+            }
+        });
         
         return cfg;
     },
     
-    
-    initEvents : function()
+    initTouchView : function()
     {
-        if(!this.store){
-            throw "can not find store for calendar";
+        this.renderTouchView();
+        
+        this.touchViewEl.on('scroll', function(){
+            this.el.dom.scrollTop = 0;
+        }, this);
+        
+        this.originalValue = this.getValue();
+        
+        this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
+        
+        this.inputEl().on("click", this.showTouchView, this);
+        if (this.triggerEl) {
+            this.triggerEl.on("click", this.showTouchView, this);
         }
         
-        var mark = {
-            tag: "div",
-            cls:"x-dlg-mask",
-            style: "text-align:center",
-            cn: [
-                {
-                    tag: "div",
-                    style: "background-color:white;width:50%;margin:250 auto",
-                    cn: [
-                        {
-                            tag: "img",
-                            src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
-                        },
-                        {
-                            tag: "span",
-                            html: "Loading"
-                        }
-                        
-                    ]
-                }
-            ]
-        };
-        this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
         
-        var size = this.el.select('.fc-content', true).first().getSize();
-        this.maskEl.setSize(size.width, size.height);
-        this.maskEl.enableDisplayMode("block");
-        if(!this.loadMask){
-            this.maskEl.hide();
+        this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
+        this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
+        
+        this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
+        
+        this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
+        this.store.on('load', this.onTouchViewLoad, this);
+        this.store.on('loadexception', this.onTouchViewLoadException, this);
+        
+        if(this.hiddenName){
+            
+            this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+            
+            this.hiddenField.dom.value =
+                this.hiddenValue !== undefined ? this.hiddenValue :
+                this.value !== undefined ? this.value : '';
+        
+            this.el.dom.removeAttribute('name');
+            this.hiddenField.dom.setAttribute('name', this.hiddenName);
         }
         
-        this.store = Roo.factory(this.store, Roo.data);
-        this.store.on('load', this.onLoad, this);
-        this.store.on('beforeload', this.onBeforeLoad, this);
+        if(this.multiple){
+            this.choices = this.el.select('ul.roo-select2-choices', true).first();
+            this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+        }
         
-        this.resize();
+        if(this.removable && !this.multiple){
+            var close = this.closeTriggerEl();
+            if(close){
+                close.setVisibilityMode(Roo.Element.DISPLAY).hide();
+                close.on('click', this.removeBtnClick, this, close);
+            }
+        }
+        /*
+         * fix the bug in Safari iOS8
+         */
+        this.inputEl().on("focus", function(e){
+            document.activeElement.blur();
+        }, this);
         
-        this.cells = this.el.select('.fc-day',true);
-        //Roo.log(this.cells);
-        this.textNodes = this.el.query('.fc-day-number');
-        this.cells.addClassOnOver('fc-state-hover');
+        this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
         
-        this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
-        this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
-        this.el.select('.fc-button-today',true).on('click', this.showToday, this);
-        this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
+        return;
         
-        this.on('monthchange', this.onMonthChange, this);
         
-        this.update(new Date().clearTime());
     },
     
-    resize : function() {
-        var sz  = this.el.getSize();
+    renderTouchView : function()
+    {
+        this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
+        this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
+        this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
+        this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        this.touchViewBodyEl.setStyle('overflow', 'auto');
+        
+        this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
+        this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
+        this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
         
-        this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
-        this.el.select('.fc-day-content div',true).setHeight(34);
     },
     
-    
-    // private
-    showPrevMonth : function(e){
-        this.update(this.activeDate.add("mo", -1));
-    },
-    showToday : function(e){
-        this.update(new Date().clearTime());
-    },
-    // private
-    showNextMonth : function(e){
-        this.update(this.activeDate.add("mo", 1));
-    },
+    showTouchView : function()
+    {
+        if(this.disabled){
+            return;
+        }
+        
+        this.touchViewHeaderEl.hide();
 
-    // private
-    showPrevYear : function(){
-        this.update(this.activeDate.add("y", -1));
-    },
+        if(this.modalTitle.length){
+            this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
+            this.touchViewHeaderEl.show();
+        }
 
-    // private
-    showNextYear : function(){
-        this.update(this.activeDate.add("y", 1));
+        this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+        this.touchViewEl.show();
+
+        this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
+        
+        //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
+        //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+
+        var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+
+        if(this.modalTitle.length){
+            bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+        }
+        
+        this.touchViewBodyEl.setHeight(bodyHeight);
+
+        if(this.animate){
+            var _this = this;
+            (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
+        }else{
+            this.touchViewEl.addClass(['in','show']);
+        }
+        
+        if(this._touchViewMask){
+            Roo.get(document.body).addClass("x-body-masked");
+            this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
+            this._touchViewMask.setStyle('z-index', 10000);
+            this._touchViewMask.addClass('show');
+        }
+        
+        this.doTouchViewQuery();
+        
     },
+    
+    hideTouchView : function()
+    {
+        this.touchViewEl.removeClass(['in','show']);
 
+        if(this.animate){
+            var _this = this;
+            (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
+        }else{
+            this.touchViewEl.setStyle('display', 'none');
+        }
+        
+        if(this._touchViewMask){
+            this._touchViewMask.removeClass('show');
+            Roo.get(document.body).removeClass("x-body-masked");
+        }
+    },
     
-   // private
-    update : function(date)
+    setTouchViewValue : function()
     {
-        var vd = this.activeDate;
-        this.activeDate = date;
-//        if(vd && this.el){
-//            var t = date.getTime();
-//            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
-//                Roo.log('using add remove');
-//                
-//                this.fireEvent('monthchange', this, date);
-//                
-//                this.cells.removeClass("fc-state-highlight");
-//                this.cells.each(function(c){
-//                   if(c.dateValue == t){
-//                       c.addClass("fc-state-highlight");
-//                       setTimeout(function(){
-//                            try{c.dom.firstChild.focus();}catch(e){}
-//                       }, 50);
-//                       return false;
-//                   }
-//                   return true;
-//                });
-//                return;
-//            }
-//        }
+        if(this.multiple){
+            this.clearItem();
         
-        var days = date.getDaysInMonth();
+            var _this = this;
+
+            Roo.each(this.tickItems, function(o){
+                this.addItem(o);
+            }, this);
+        }
         
-        var firstOfMonth = date.getFirstDateOfMonth();
-        var startingPos = firstOfMonth.getDay()-this.startDay;
+        this.hideTouchView();
+    },
+    
+    doTouchViewQuery : function()
+    {
+        var qe = {
+            query: '',
+            forceAll: true,
+            combo: this,
+            cancel:false
+        };
         
-        if(startingPos < this.startDay){
-            startingPos += 7;
+        if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
+            return false;
         }
         
-        var pm = date.add(Date.MONTH, -1);
-        var prevStart = pm.getDaysInMonth()-startingPos;
-//        
-        this.cells = this.el.select('.fc-day',true);
-        this.textNodes = this.el.query('.fc-day-number');
-        this.cells.addClassOnOver('fc-state-hover');
+        if(!this.alwaysQuery || this.mode == 'local'){
+            this.onTouchViewLoad();
+            return;
+        }
         
-        var cells = this.cells.elements;
-        var textEls = this.textNodes;
+        this.store.load();
+    },
+    
+    onTouchViewBeforeLoad : function(combo,opts)
+    {
+        return;
+    },
+
+    // private
+    onTouchViewLoad : function()
+    {
+        if(this.store.getCount() < 1){
+            this.onTouchViewEmptyResults();
+            return;
+        }
         
-        Roo.each(cells, function(cell){
-            cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
-        });
+        this.clearTouchView();
         
-        days += startingPos;
-
-        // convert everything to numbers so it's fast
-        var day = 86400000;
-        var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
-        //Roo.log(d);
-        //Roo.log(pm);
-        //Roo.log(prevStart);
+        var rawValue = this.getRawValue();
         
-        var today = new Date().clearTime().getTime();
-        var sel = date.clearTime().getTime();
-        var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
-        var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
-        var ddMatch = this.disabledDatesRE;
-        var ddText = this.disabledDatesText;
-        var ddays = this.disabledDays ? this.disabledDays.join("") : false;
-        var ddaysText = this.disabledDaysText;
-        var format = this.format;
+        var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
         
-        var setCellClass = function(cal, cell){
-            cell.row = 0;
-            cell.events = [];
-            cell.more = [];
-            //Roo.log('set Cell Class');
-            cell.title = "";
-            var t = d.getTime();
-            
-            //Roo.log(d);
+        this.tickItems = [];
+        
+        this.store.data.each(function(d, rowIndex){
+            var row = this.touchViewListGroup.createChild(template);
             
-            cell.dateValue = t;
-            if(t == today){
-                cell.className += " fc-today";
-                cell.className += " fc-state-highlight";
-                cell.title = cal.todayText;
+            if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
+                row.addClass(d.data.cls);
             }
-            if(t == sel){
-                // disable highlight in other month..
-                //cell.className += " fc-state-highlight";
+            
+            if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
+                var cfg = {
+                    data : d.data,
+                    html : d.data[this.displayField]
+                };
                 
-            }
-            // disabling
-            if(t < min) {
-                cell.className = " fc-state-disabled";
-                cell.title = cal.minText;
-                return;
-            }
-            if(t > max) {
-                cell.className = " fc-state-disabled";
-                cell.title = cal.maxText;
-                return;
-            }
-            if(ddays){
-                if(ddays.indexOf(d.getDay()) != -1){
-                    cell.title = ddaysText;
-                    cell.className = " fc-state-disabled";
+                if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
+                    row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
                 }
             }
-            if(ddMatch && format){
-                var fvalue = d.dateFormat(format);
-                if(ddMatch.test(fvalue)){
-                    cell.title = ddText.replace("%0", fvalue);
-                    cell.className = " fc-state-disabled";
-                }
+            row.removeClass('selected');
+            if(!this.multiple && this.valueField &&
+                    typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
+            {
+                // radio buttons..
+                row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+                row.addClass('selected');
             }
             
-            if (!cell.initialClassName) {
-                cell.initialClassName = cell.dom.className;
+            if(this.multiple && this.valueField &&
+                    typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
+            {
+                
+                // checkboxes...
+                row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+                this.tickItems.push(d.data);
             }
             
-            cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
-        };
-
-        var i = 0;
+            row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
+            
+        }, this);
         
-        for(; i < startingPos; i++) {
-            textEls[i].innerHTML = (++prevStart);
-            d.setDate(d.getDate()+1);
-            
-            cells[i].className = "fc-past fc-other-month";
-            setCellClass(this, cells[i]);
-        }
-        
-        var intDay = 0;
-        
-        for(; i < days; i++){
-            intDay = i - startingPos + 1;
-            textEls[i].innerHTML = (intDay);
-            d.setDate(d.getDate()+1);
-            
-            cells[i].className = ''; // "x-date-active";
-            setCellClass(this, cells[i]);
-        }
-        var extraDays = 0;
+        var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
         
-        for(; i < 42; i++) {
-            textEls[i].innerHTML = (++extraDays);
-            d.setDate(d.getDate()+1);
-            
-            cells[i].className = "fc-future fc-other-month";
-            setCellClass(this, cells[i]);
+        var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+
+        if(this.modalTitle.length){
+            bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
         }
+
+        var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
         
-        this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
-        
-        var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
-        
-        this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
-        this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
-        
-        if(totalRows != 6){
-            this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
-            this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
+        if(this.mobile_restrict_height && listHeight < bodyHeight){
+            this.touchViewBodyEl.setHeight(listHeight);
         }
         
-        this.fireEvent('monthchange', this, date);
-        
+        var _this = this;
         
-        /*
-        if(!this.internalRender){
-            var main = this.el.dom.firstChild;
-            var w = main.offsetWidth;
-            this.el.setWidth(w + this.el.getBorderWidth("lr"));
-            Roo.fly(main).setWidth(w);
-            this.internalRender = true;
-            // opera does not respect the auto grow header center column
-            // then, after it gets a width opera refuses to recalculate
-            // without a second pass
-            if(Roo.isOpera && !this.secondPass){
-                main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
-                this.secondPass = true;
-                this.update.defer(10, this, [date]);
-            }
+        if(firstChecked && listHeight > bodyHeight){
+            (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
         }
-        */
         
     },
     
-    findCell : function(dt) {
-        dt = dt.clearTime().getTime();
-        var ret = false;
-        this.cells.each(function(c){
-            //Roo.log("check " +c.dateValue + '?=' + dt);
-            if(c.dateValue == dt){
-                ret = c;
-                return false;
-            }
-            return true;
-        });
-        
-        return ret;
+    onTouchViewLoadException : function()
+    {
+        this.hideTouchView();
     },
     
-    findCells : function(ev) {
-        var s = ev.start.clone().clearTime().getTime();
-       // Roo.log(s);
-        var e= ev.end.clone().clearTime().getTime();
-       // Roo.log(e);
-        var ret = [];
-        this.cells.each(function(c){
-             ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
-            
-            if(c.dateValue > e){
-                return ;
-            }
-            if(c.dateValue < s){
-                return ;
-            }
-            ret.push(c);
-        });
+    onTouchViewEmptyResults : function()
+    {
+        this.clearTouchView();
+        
+        this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
+        
+        this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
         
-        return ret;    
     },
     
-//    findBestRow: function(cells)
-//    {
-//        var ret = 0;
-//        
-//        for (var i =0 ; i < cells.length;i++) {
-//            ret  = Math.max(cells[i].rows || 0,ret);
-//        }
-//        return ret;
-//        
-//    },
-    
+    clearTouchView : function()
+    {
+        this.touchViewListGroup.dom.innerHTML = '';
+    },
     
-    addItem : function(ev)
+    onTouchViewClick : function(e, el, o)
     {
-        // look for vertical location slot in
-        var cells = this.findCells(ev);
+        e.preventDefault();
         
-//        ev.row = this.findBestRow(cells);
+        var row = o.row;
+        var rowIndex = o.rowIndex;
         
-        // work out the location.
+        var r = this.store.getAt(rowIndex);
         
-        var crow = false;
-        var rows = [];
-        for(var i =0; i < cells.length; i++) {
-            
-            cells[i].row = cells[0].row;
-            
-            if(i == 0){
-                cells[i].row = cells[i].row + 1;
-            }
+        if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
             
-            if (!crow) {
-                crow = {
-                    start : cells[i],
-                    end :  cells[i]
-                };
-                continue;
+            if(!this.multiple){
+                Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
+                    c.dom.removeAttribute('checked');
+                }, this);
+
+                row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+
+                this.setFromData(r.data);
+
+                var close = this.closeTriggerEl();
+
+                if(close){
+                    close.show();
+                }
+
+                this.hideTouchView();
+
+                this.fireEvent('select', this, r, rowIndex);
+
+                return;
             }
-            if (crow.start.getY() == cells[i].getY()) {
-                // on same row.
-                crow.end = cells[i];
-                continue;
+
+            if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
+                row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
+                this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
+                return;
             }
-            // different row.
-            rows.push(crow);
-            crow = {
-                start: cells[i],
-                end : cells[i]
-            };
-            
+
+            row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+            this.addItem(r.data);
+            this.tickItems.push(r.data);
         }
-        
-        rows.push(crow);
-        ev.els = [];
-        ev.rows = rows;
-        ev.cells = cells;
-        
-        cells[0].events.push(ev);
-        
-        this.calevents.push(ev);
     },
     
-    clearEvents: function() {
+    getAutoCreateNativeIOS : function()
+    {
+        var cfg = {
+            cls: 'form-group' //input-group,
+        };
         
-        if(!this.calevents){
-            return;
+        var combobox =  {
+            tag: 'select',
+            cls : 'roo-ios-select'
+        };
+        
+        if (this.name) {
+            combobox.name = this.name;
         }
         
-        Roo.each(this.cells.elements, function(c){
-            c.row = 0;
-            c.events = [];
-            c.more = [];
-        });
+        if (this.disabled) {
+            combobox.disabled = true;
+        }
         
-        Roo.each(this.calevents, function(e) {
-            Roo.each(e.els, function(el) {
-                el.un('mouseenter' ,this.onEventEnter, this);
-                el.un('mouseleave' ,this.onEventLeave, this);
-                el.remove();
-            },this);
-        },this);
+        var settings = this;
         
-        Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
-            e.remove();
+        ['xs','sm','md','lg'].map(function(size){
+            if (settings[size]) {
+                cfg.cls += ' col-' + size + '-' + settings[size];
+            }
         });
         
+        cfg.cn = combobox;
+        
+        return cfg;
+        
     },
     
-    renderEvents: function()
-    {   
-        var _this = this;
+    initIOSView : function()
+    {
+        this.store.on('load', this.onIOSViewLoad, this);
         
-        this.cells.each(function(c) {
+        return;
+    },
+    
+    onIOSViewLoad : function()
+    {
+        if(this.store.getCount() < 1){
+            return;
+        }
+        
+        this.clearIOSView();
+        
+        if(this.allowBlank) {
             
-            if(c.row < 5){
-                return;
-            }
+            var default_text = '-- SELECT --';
             
-            var ev = c.events;
+            if(this.placeholder.length){
+                default_text = this.placeholder;
+            }
             
-            var r = 4;
-            if(c.row != c.events.length){
-                r = 4 - (4 - (c.row - c.events.length));
+            if(this.emptyTitle.length){
+                default_text += ' - ' + this.emptyTitle + ' -';
             }
             
-            c.events = ev.slice(0, r);
-            c.more = ev.slice(r);
+            var opt = this.inputEl().createChild({
+                tag: 'option',
+                value : 0,
+                html : default_text
+            });
             
-            if(c.more.length && c.more.length == 1){
-                c.events.push(c.more.pop());
-            }
+            var o = {};
+            o[this.valueField] = 0;
+            o[this.displayField] = default_text;
             
-            c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
+            this.ios_options.push({
+                data : o,
+                el : opt
+            });
             
-        });
+        }
+        
+        this.store.data.each(function(d, rowIndex){
             
-        this.cells.each(function(c) {
+            var html = '';
             
-            c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
+            if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
+                html = d.data[this.displayField];
+            }
             
+            var value = '';
             
-            for (var e = 0; e < c.events.length; e++){
-                var ev = c.events[e];
-                var rows = ev.rows;
-                
-                for(var i = 0; i < rows.length; i++) {
-                
-                    // how many rows should it span..
-
-                    var  cfg = {
-                        cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
-                        style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
-
-                        unselectable : "on",
-                        cn : [
-                            {
-                                cls: 'fc-event-inner',
-                                cn : [
-    //                                {
-    //                                  tag:'span',
-    //                                  cls: 'fc-event-time',
-    //                                  html : cells.length > 1 ? '' : ev.time
-    //                                },
-                                    {
-                                      tag:'span',
-                                      cls: 'fc-event-title',
-                                      html : String.format('{0}', ev.title)
-                                    }
-
-
-                                ]
-                            },
-                            {
-                                cls: 'ui-resizable-handle ui-resizable-e',
-                                html : '&nbsp;&nbsp;&nbsp'
-                            }
-
-                        ]
-                    };
-
-                    if (i == 0) {
-                        cfg.cls += ' fc-event-start';
-                    }
-                    if ((i+1) == rows.length) {
-                        cfg.cls += ' fc-event-end';
-                    }
-
-                    var ctr = _this.el.select('.fc-event-container',true).first();
-                    var cg = ctr.createChild(cfg);
-
-                    var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
-                    var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
-
-                    var r = (c.more.length) ? 1 : 0;
-                    cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
-                    cg.setWidth(ebox.right - sbox.x -2);
-
-                    cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
-                    cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
-                    cg.on('click', _this.onEventClick, _this, ev);
-
-                    ev.els.push(cg);
-                    
-                }
-                
+            if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
+                value = d.data[this.valueField];
             }
             
+            var option = {
+                tag: 'option',
+                value : value,
+                html : html
+            };
             
-            if(c.more.length){
-                var  cfg = {
-                    cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
-                    style : 'position: absolute',
-                    unselectable : "on",
-                    cn : [
-                        {
-                            cls: 'fc-event-inner',
-                            cn : [
-                                {
-                                  tag:'span',
-                                  cls: 'fc-event-title',
-                                  html : 'More'
-                                }
-
-
-                            ]
-                        },
-                        {
-                            cls: 'ui-resizable-handle ui-resizable-e',
-                            html : '&nbsp;&nbsp;&nbsp'
-                        }
-
-                    ]
-                };
-
-                var ctr = _this.el.select('.fc-event-container',true).first();
-                var cg = ctr.createChild(cfg);
-
-                var sbox = c.select('.fc-day-content',true).first().getBox();
-                var ebox = c.select('.fc-day-content',true).first().getBox();
-                //Roo.log(cg);
-                cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
-                cg.setWidth(ebox.right - sbox.x -2);
-
-                cg.on('click', _this.onMoreEventClick, _this, c.more);
-                
+            if(this.value == d.data[this.valueField]){
+                option['selected'] = true;
             }
             
-        });
-        
+            var opt = this.inputEl().createChild(option);
+            
+            this.ios_options.push({
+                data : d.data,
+                el : opt
+            });
+            
+        }, this);
         
+        this.inputEl().on('change', function(){
+           this.fireEvent('select', this);
+        }, this);
         
     },
     
-    onEventEnter: function (e, el,event,d) {
-        this.fireEvent('evententer', this, el, event);
-    },
-    
-    onEventLeave: function (e, el,event,d) {
-        this.fireEvent('eventleave', this, el, event);
-    },
-    
-    onEventClick: function (e, el,event,d) {
-        this.fireEvent('eventclick', this, el, event);
-    },
-    
-    onMonthChange: function () {
-        this.store.load();
-    },
-    
-    onMoreEventClick: function(e, el, more)
+    clearIOSView: function()
     {
-        var _this = this;
-        
-        this.calpopover.placement = 'right';
-        this.calpopover.setTitle('More');
-        
-        this.calpopover.setContent('');
-        
-        var ctr = this.calpopover.el.select('.popover-content', true).first();
-        
-        Roo.each(more, function(m){
-            var cfg = {
-                cls : 'fc-event-hori fc-event-draggable',
-                html : m.title
-            };
-            var cg = ctr.createChild(cfg);
-            
-            cg.on('click', _this.onEventClick, _this, m);
-        });
-        
-        this.calpopover.show(el);
-        
+        this.inputEl().dom.innerHTML = '';
         
+        this.ios_options = [];
     },
     
-    onLoad: function () 
-    {   
-        this.calevents = [];
-        var cal = this;
+    setIOSValue: function(v)
+    {
+        this.value = v;
         
-        if(this.store.getCount() > 0){
-            this.store.data.each(function(d){
-               cal.addItem({
-                    id : d.data.id,
-                    start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
-                    end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
-                    time : d.data.start_time,
-                    title : d.data.title,
-                    description : d.data.description,
-                    venue : d.data.venue
-                });
-            });
+        if(!this.ios_options){
+            return;
         }
         
-        this.renderEvents();
-        
-        if(this.calevents.length && this.loadMask){
-            this.maskEl.hide();
-        }
-    },
-    
-    onBeforeLoad: function()
-    {
-        this.clearEvents();
-        if(this.loadMask){
-            this.maskEl.show();
-        }
+        Roo.each(this.ios_options, function(opts){
+           
+           opts.el.dom.removeAttribute('selected');
+           
+           if(opts.data[this.valueField] != v){
+               return;
+           }
+           
+           opts.el.dom.setAttribute('selected', true);
+           
+        }, this);
     }
-});
 
- /*
- * - LGPL
- *
- * element
- * 
- */
-
-/**
- * @class Roo.bootstrap.Popover
- * @extends Roo.bootstrap.Component
- * Bootstrap Popover class
- * @cfg {String} html contents of the popover   (or false to use children..)
- * @cfg {String} title of popover (or false to hide)
- * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
- * @cfg {String} trigger click || hover (or false to trigger manually)
- * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
- * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
- *      - if false and it has a 'parent' then it will be automatically added to that element
- *      - if string - Roo.get  will be called 
- * @cfg {Number} delay - delay before showing
- * @constructor
- * Create a new Popover
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Popover = function(config){
-    Roo.bootstrap.Popover.superclass.constructor.call(this, config);
-    
-    this.addEvents({
-        // raw events
-         /**
-         * @event show
-         * After the popover show
-         * 
-         * @param {Roo.bootstrap.Popover} this
-         */
-        "show" : true,
-        /**
-         * @event hide
-         * After the popover hide
-         * 
-         * @param {Roo.bootstrap.Popover} this
-         */
-        "hide" : true
-    });
-};
+    /** 
+    * @cfg {Boolean} grow 
+    * @hide 
+    */
+    /** 
+    * @cfg {Number} growMin 
+    * @hide 
+    */
+    /** 
+    * @cfg {Number} growMax 
+    * @hide 
+    */
+    /**
+     * @hide
+     * @method autoSize
+     */
+});
 
-Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
-    
-    title: false,
-    html: false,
-    
-    placement : 'right',
-    trigger : 'hover', // hover
-    modal : false,
-    delay : 0,
-    
-    over: false,
-    
-    can_build_overlaid : false,
-    
-    maskEl : false, // the mask element
-    headerEl : false,
-    contentEl : false,
-    alignEl : false, // when show is called with an element - this get's stored.
+Roo.apply(Roo.bootstrap.ComboBox,  {
     
-    getChildContainer : function()
-    {
-        return this.contentEl;
-        
-    },
-    getPopoverHeader : function()
-    {
-        this.title = true; // flag not to hide it..
-        this.headerEl.addClass('p-0');
-        return this.headerEl
+    header : {
+        tag: 'div',
+        cls: 'modal-header',
+        cn: [
+            {
+                tag: 'h4',
+                cls: 'modal-title'
+            }
+        ]
     },
     
-    
-    getAutoCreate : function(){
-         
-        var cfg = {
-           cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
-           style: 'display:block',
-           cn : [
-                {
-                    cls : 'arrow'
-                },
-                {
-                    cls : 'popover-inner ',
-                    cn : [
-                        {
-                            tag: 'h3',
-                            cls: 'popover-title popover-header',
-                            html : this.title === false ? '' : this.title
-                        },
-                        {
-                            cls : 'popover-content popover-body '  + (this.cls || ''),
-                            html : this.html || ''
-                        }
-                    ]
-                    
-                }
-           ]
-        };
-        
-        return cfg;
-    },
-    /**
-     * @param {string} the title
-     */
-    setTitle: function(str)
-    {
-        this.title = str;
-        if (this.el) {
-            this.headerEl.dom.innerHTML = str;
-        }
-        
-    },
-    /**
-     * @param {string} the body content
-     */
-    setContent: function(str)
-    {
-        this.html = str;
-        if (this.contentEl) {
-            this.contentEl.dom.innerHTML = str;
-        }
-        
-    },
-    // as it get's added to the bottom of the page.
-    onRender : function(ct, position)
-    {
-        Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
-        
-        
-        
-        if(!this.el){
-            var cfg = Roo.apply({},  this.getAutoCreate());
-            cfg.id = Roo.id();
-            
-            if (this.cls) {
-                cfg.cls += ' ' + this.cls;
+    body : {
+        tag: 'div',
+        cls: 'modal-body',
+        cn: [
+            {
+                tag: 'ul',
+                cls: 'list-group'
             }
-            if (this.style) {
-                cfg.style = this.style;
+        ]
+    },
+    
+    listItemRadio : {
+        tag: 'li',
+        cls: 'list-group-item',
+        cn: [
+            {
+                tag: 'span',
+                cls: 'roo-combobox-list-group-item-value'
+            },
+            {
+                tag: 'div',
+                cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
+                cn: [
+                    {
+                        tag: 'input',
+                        type: 'radio'
+                    },
+                    {
+                        tag: 'label'
+                    }
+                ]
             }
-            //Roo.log("adding to ");
-            this.el = Roo.get(document.body).createChild(cfg, position);
-//            Roo.log(this.el);
-        }
-        
-        this.contentEl = this.el.select('.popover-content',true).first();
-        this.headerEl =  this.el.select('.popover-title',true).first();
-        
-        var nitems = [];
-        if(typeof(this.items) != 'undefined'){
-            var items = this.items;
-            delete this.items;
-
-            for(var i =0;i < items.length;i++) {
-                nitems.push(this.addxtype(Roo.apply({}, items[i])));
+        ]
+    },
+    
+    listItemCheckbox : {
+        tag: 'li',
+        cls: 'list-group-item',
+        cn: [
+            {
+                tag: 'span',
+                cls: 'roo-combobox-list-group-item-value'
+            },
+            {
+                tag: 'div',
+                cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
+                cn: [
+                    {
+                        tag: 'input',
+                        type: 'checkbox'
+                    },
+                    {
+                        tag: 'label'
+                    }
+                ]
             }
-        }
-
-        this.items = nitems;
-        
-        this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
-        Roo.EventManager.onWindowResize(this.resizeMask, this, true);
-        
-        
-        
-        this.initEvents();
+        ]
     },
     
-    resizeMask : function()
-    {
-        this.maskEl.setSize(
-            Roo.lib.Dom.getViewWidth(true),
-            Roo.lib.Dom.getViewHeight(true)
-        );
+    emptyResult : {
+        tag: 'div',
+        cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
     },
     
-    initEvents : function()
-    {
-        
-        if (!this.modal) { 
-            Roo.bootstrap.Popover.register(this);
-        }
-         
-        this.arrowEl = this.el.select('.arrow',true).first();
-        this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
-        this.el.enableDisplayMode('block');
-        this.el.hide();
-        
-        if (this.over === false && !this.parent()) {
-            return; 
-        }
-        if (this.triggers === false) {
-            return;
-        }
-         
-        // support parent
-        var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
-        var triggers = this.trigger ? this.trigger.split(' ') : [];
-        Roo.each(triggers, function(trigger) {
-        
-            if (trigger == 'click') {
-                on_el.on('click', this.toggle, this);
-            } else if (trigger != 'manual') {
-                var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
-                var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
-      
-                on_el.on(eventIn  ,this.enter, this);
-                on_el.on(eventOut, this.leave, this);
+    footer : {
+        tag: 'div',
+        cls: 'modal-footer',
+        cn: [
+            {
+                tag: 'div',
+                cls: 'row',
+                cn: [
+                    {
+                        tag: 'div',
+                        cls: 'col-xs-6 text-left',
+                        cn: {
+                            tag: 'button',
+                            cls: 'btn btn-danger roo-touch-view-cancel',
+                            html: 'Cancel'
+                        }
+                    },
+                    {
+                        tag: 'div',
+                        cls: 'col-xs-6 text-right',
+                        cn: {
+                            tag: 'button',
+                            cls: 'btn btn-success roo-touch-view-ok',
+                            html: 'OK'
+                        }
+                    }
+                ]
             }
-        }, this);
-    },
+        ]
+        
+    }
+});
+
+Roo.apply(Roo.bootstrap.ComboBox,  {
     
+    touchViewTemplate : {
+        tag: 'div',
+        cls: 'modal fade roo-combobox-touch-view',
+        cn: [
+            {
+                tag: 'div',
+                cls: 'modal-dialog',
+                style : 'position:fixed', // we have to fix position....
+                cn: [
+                    {
+                        tag: 'div',
+                        cls: 'modal-content',
+                        cn: [
+                            Roo.bootstrap.ComboBox.header,
+                            Roo.bootstrap.ComboBox.body,
+                            Roo.bootstrap.ComboBox.footer
+                        ]
+                    }
+                ]
+            }
+        ]
+    }
+});/*
+ * 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.View
+ * @extends Roo.util.Observable
+ * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
+ * This class also supports single and multi selection modes. <br>
+ * Create a data model bound view:
+ <pre><code>
+ var store = new Roo.data.Store(...);
+
+ var view = new Roo.View({
+    el : "my-element",
+    tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
+    singleSelect: true,
+    selectedClass: "ydataview-selected",
+    store: store
+ });
+
+ // listen for node click?
+ view.on("click", function(vw, index, node, e){
+ alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
+ });
+
+ // load XML data
+ dataModel.load("foobar.xml");
+ </code></pre>
+ For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
+ * <br><br>
+ * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
+ * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
+ * 
+ * Note: old style constructor is still suported (container, template, config)
+ * 
+ * @constructor
+ * Create a new View
+ * @param {Object} config The config object
+ * 
+ */
+Roo.View = function(config, depreciated_tpl, depreciated_config){
     
-    // private
-    timeout : null,
-    hoverState : null,
+    this.parent = false;
     
-    toggle : function () {
-        this.hoverState == 'in' ? this.leave() : this.enter();
-    },
+    if (typeof(depreciated_tpl) == 'undefined') {
+        // new way.. - universal constructor.
+        Roo.apply(this, config);
+        this.el  = Roo.get(this.el);
+    } else {
+        // old format..
+        this.el  = Roo.get(config);
+        this.tpl = depreciated_tpl;
+        Roo.apply(this, depreciated_config);
+    }
+    this.wrapEl  = this.el.wrap().wrap();
+    ///this.el = this.wrapEla.appendChild(document.createElement("div"));
     
-    enter : function () {
-        
-        clearTimeout(this.timeout);
     
-        this.hoverState = 'in';
+    if(typeof(this.tpl) == "string"){
+        this.tpl = new Roo.Template(this.tpl);
+    } else {
+        // support xtype ctors..
+        this.tpl = new Roo.factory(this.tpl, Roo);
+    }
     
-        if (!this.delay || !this.delay.show) {
-            this.show();
-            return;
-        }
-        var _t = this;
-        this.timeout = setTimeout(function () {
-            if (_t.hoverState == 'in') {
-                _t.show();
-            }
-        }, this.delay.show)
-    },
     
-    leave : function() {
-        clearTimeout(this.timeout);
+    this.tpl.compile();
     
-        this.hoverState = 'out';
+    /** @private */
+    this.addEvents({
+        /**
+         * @event beforeclick
+         * Fires before a click is processed. Returns false to cancel the default action.
+         * @param {Roo.View} this
+         * @param {Number} index The index of the target node
+         * @param {HTMLElement} node The target node
+         * @param {Roo.EventObject} e The raw event object
+         */
+            "beforeclick" : true,
+        /**
+         * @event click
+         * Fires when a template node is clicked.
+         * @param {Roo.View} this
+         * @param {Number} index The index of the target node
+         * @param {HTMLElement} node The target node
+         * @param {Roo.EventObject} e The raw event object
+         */
+            "click" : true,
+        /**
+         * @event dblclick
+         * Fires when a template node is double clicked.
+         * @param {Roo.View} this
+         * @param {Number} index The index of the target node
+         * @param {HTMLElement} node The target node
+         * @param {Roo.EventObject} e The raw event object
+         */
+            "dblclick" : true,
+        /**
+         * @event contextmenu
+         * Fires when a template node is right clicked.
+         * @param {Roo.View} this
+         * @param {Number} index The index of the target node
+         * @param {HTMLElement} node The target node
+         * @param {Roo.EventObject} e The raw event object
+         */
+            "contextmenu" : true,
+        /**
+         * @event selectionchange
+         * Fires when the selected nodes change.
+         * @param {Roo.View} this
+         * @param {Array} selections Array of the selected nodes
+         */
+            "selectionchange" : true,
     
-        if (!this.delay || !this.delay.hide) {
-            this.hide();
-            return;
-        }
-        var _t = this;
-        this.timeout = setTimeout(function () {
-            if (_t.hoverState == 'out') {
-                _t.hide();
-            }
-        }, this.delay.hide)
-    },
-    /**
-     * Show the popover
-     * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
-     * @param {string} (left|right|top|bottom) position
-     */
-    show : function (on_el, placement)
-    {
-        this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
-        on_el = on_el || false; // default to false
-         
-        if (!on_el) {
-            if (this.parent() && (this.over == 'parent' || (this.over === false))) {
-                on_el = this.parent().el;
-            } else if (this.over) {
-                on_el = Roo.get(this.over);
-            }
-            
-        }
-        
-        this.alignEl = Roo.get( on_el );
+        /**
+         * @event beforeselect
+         * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+         * @param {Roo.View} this
+         * @param {HTMLElement} node The node to be selected
+         * @param {Array} selections Array of currently selected nodes
+         */
+            "beforeselect" : true,
+        /**
+         * @event preparedata
+         * Fires on every row to render, to allow you to change the data.
+         * @param {Roo.View} this
+         * @param {Object} data to be rendered (change this)
+         */
+          "preparedata" : true
+          
+          
+        });
 
-        if (!this.el) {
-            this.render(document.body);
-        }
-        
-        
-         
+
+
+    this.el.on({
+        "click": this.onClick,
+        "dblclick": this.onDblClick,
+        "contextmenu": this.onContextMenu,
+        scope:this
+    });
+
+    this.selections = [];
+    this.nodes = [];
+    this.cmp = new Roo.CompositeElementLite([]);
+    if(this.store){
+        this.store = Roo.factory(this.store, Roo.data);
+        this.setStore(this.store, true);
+    }
+    
+    if ( this.footer && this.footer.xtype) {
+           
+         var fctr = this.wrapEl.appendChild(document.createElement("div"));
         
-        if (this.title === false) {
-            this.headerEl.hide();
-        }
+        this.footer.dataSource = this.store;
+        this.footer.container = fctr;
+        this.footer = Roo.factory(this.footer, Roo);
+        fctr.insertFirst(this.el);
         
-       
-        this.el.show();
-        this.el.dom.style.display = 'block';
-         
-        if (this.alignEl) {
-            this.updatePosition(this.placement, true);
-             
-        } else {
-            // this is usually just done by the builder = to show the popoup in the middle of the scren.
-            var es = this.el.getSize();
-            var x = Roo.lib.Dom.getViewWidth()/2;
-            var y = Roo.lib.Dom.getViewHeight()/2;
-            this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
-            
-        }
+        // this is a bit insane - as the paging toolbar seems to detach the el..
+//        dom.parentNode.parentNode.parentNode
+         // they get detached?
+    }
+    
+    
+    Roo.View.superclass.constructor.call(this);
+    
+    
+};
+
+Roo.extend(Roo.View, Roo.util.Observable, {
+    
+     /**
+     * @cfg {Roo.data.Store} store Data store to load data from.
+     */
+    store : false,
+    
+    /**
+     * @cfg {String|Roo.Element} el The container element.
+     */
+    el : '',
+    
+    /**
+     * @cfg {String|Roo.Template} tpl The template used by this View 
+     */
+    tpl : false,
+    /**
+     * @cfg {String} dataName the named area of the template to use as the data area
+     *                          Works with domtemplates roo-name="name"
+     */
+    dataName: false,
+    /**
+     * @cfg {String} selectedClass The css class to add to selected nodes
+     */
+    selectedClass : "x-view-selected",
+     /**
+     * @cfg {String} emptyText The empty text to show when nothing is loaded.
+     */
+    emptyText : "",
+    
+    /**
+     * @cfg {String} text to display on mask (default Loading)
+     */
+    mask : false,
+    /**
+     * @cfg {Boolean} multiSelect Allow multiple selection
+     */
+    multiSelect : false,
+    /**
+     * @cfg {Boolean} singleSelect Allow single selection
+     */
+    singleSelect:  false,
+    
+    /**
+     * @cfg {Boolean} toggleSelect - selecting 
+     */
+    toggleSelect : false,
+    
+    /**
+     * @cfg {Boolean} tickable - selecting 
+     */
+    tickable : false,
+    
+    /**
+     * Returns the element this view is bound to.
+     * @return {Roo.Element}
+     */
+    getEl : function(){
+        return this.wrapEl;
+    },
+    
+    
 
+    /**
+     * Refreshes the view. - called by datachanged on the store. - do not call directly.
+     */
+    refresh : function(){
+        //Roo.log('refresh');
+        var t = this.tpl;
         
-        //var arrow = this.el.select('.arrow',true).first();
-        //arrow.set(align[2], 
+        // if we are using something like 'domtemplate', then
+        // the what gets used is:
+        // t.applySubtemplate(NAME, data, wrapping data..)
+        // the outer template then get' applied with
+        //     the store 'extra data'
+        // and the body get's added to the
+        //      roo-name="data" node?
+        //      <span class='roo-tpl-{name}'></span> ?????
         
-        this.el.addClass('in');
         
-         
         
-        this.hoverState = 'in';
+        this.clearSelections();
+        this.el.update("");
+        var html = [];
+        var records = this.store.getRange();
+        if(records.length < 1) {
+            
+            // is this valid??  = should it render a template??
+            
+            this.el.update(this.emptyText);
+            return;
+        }
+        var el = this.el;
+        if (this.dataName) {
+            this.el.update(t.apply(this.store.meta)); //????
+            el = this.el.child('.roo-tpl-' + this.dataName);
+        }
         
-        if (this.modal) {
-            this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
-            this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
-            this.maskEl.dom.style.display = 'block';
-            this.maskEl.addClass('show');
+        for(var i = 0, len = records.length; i < len; i++){
+            var data = this.prepareData(records[i].data, i, records[i]);
+            this.fireEvent("preparedata", this, data, i, records[i]);
+            
+            var d = Roo.apply({}, data);
+            
+            if(this.tickable){
+                Roo.apply(d, {'roo-id' : Roo.id()});
+                
+                var _this = this;
+            
+                Roo.each(this.parent.item, function(item){
+                    if(item[_this.parent.valueField] != data[_this.parent.valueField]){
+                        return;
+                    }
+                    Roo.apply(d, {'roo-data-checked' : 'checked'});
+                });
+            }
+            
+            html[html.length] = Roo.util.Format.trim(
+                this.dataName ?
+                    t.applySubtemplate(this.dataName, d, this.store.meta) :
+                    t.apply(d)
+            );
         }
-        this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
-        this.fireEvent('show', this);
         
+        
+        
+        el.update(html.join(""));
+        this.nodes = el.dom.childNodes;
+        this.updateIndexes(0);
     },
+    
+
     /**
-     * fire this manually after loading a grid in the table for example
-     * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
-     * @param {Boolean} try and move it if we cant get right position.
+     * Function to override to reformat the data that is sent to
+     * the template for each node.
+     * DEPRICATED - use the preparedata event handler.
+     * @param {Array/Object} data The raw data (array of colData for a data model bound view or
+     * a JSON object for an UpdateManager bound view).
      */
-    updatePosition : function(placement, try_move)
+    prepareData : function(data, index, record)
     {
-        // allow for calling with no parameters
-        placement = placement   ? placement :  this.placement;
-        try_move = typeof(try_move) == 'undefined' ? true : try_move;
-        
-        this.el.removeClass([
-            'fade','top','bottom', 'left', 'right','in',
-            'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
-        ]);
-        this.el.addClass(placement + ' bs-popover-' + placement);
-        
-        if (!this.alignEl ) {
-            return false;
-        }
-        
-        switch (placement) {
-            case 'right':
-                var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
-                var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
-                if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
-                    //normal display... or moved up/down.
-                    this.el.setXY(offset);
-                    var xy = this.alignEl.getAnchorXY('tr', false);
-                    xy[0]+=2;xy[1]+=5;
-                    this.arrowEl.setXY(xy);
-                    return true;
-                }
-                // continue through...
-                return this.updatePosition('left', false);
-                
-            
-            case 'left':
-                var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
-                var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
-                if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
-                    //normal display... or moved up/down.
-                    this.el.setXY(offset);
-                    var xy = this.alignEl.getAnchorXY('tl', false);
-                    xy[0]-=10;xy[1]+=5; // << fix me
-                    this.arrowEl.setXY(xy);
-                    return true;
-                }
-                // call self...
-                return this.updatePosition('right', false);
-            
-            case 'top':
-                var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
-                var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
-                if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
-                    //normal display... or moved up/down.
-                    this.el.setXY(offset);
-                    var xy = this.alignEl.getAnchorXY('t', false);
-                    xy[1]-=10; // << fix me
-                    this.arrowEl.setXY(xy);
-                    return true;
-                }
-                // fall through
-               return this.updatePosition('bottom', false);
-            
-            case 'bottom':
-                 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
-                var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
-                if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
-                    //normal display... or moved up/down.
-                    this.el.setXY(offset);
-                    var xy = this.alignEl.getAnchorXY('b', false);
-                     xy[1]+=2; // << fix me
-                    this.arrowEl.setXY(xy);
-                    return true;
-                }
-                // fall through
-                return this.updatePosition('top', false);
-                
-            
-        }
-        
-        
-        return false;
+        this.fireEvent("preparedata", this, data, index, record);
+        return data;
     },
+
+    onUpdate : function(ds, record){
+        // Roo.log('on update');   
+        this.clearSelections();
+        var index = this.store.indexOf(record);
+        var n = this.nodes[index];
+        this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
+        n.parentNode.removeChild(n);
+        this.updateIndexes(index, index);
+    },
+
     
-    hide : function()
-    {
-        this.el.setXY([0,0]);
-        this.el.removeClass('in');
-        this.el.hide();
-        this.hoverState = null;
-        this.maskEl.hide(); // always..
-        this.fireEvent('hide', this);
-    }
     
-});
-
+// --------- FIXME     
+    onAdd : function(ds, records, index)
+    {
+        //Roo.log(['on Add', ds, records, index] );        
+        this.clearSelections();
+        if(this.nodes.length == 0){
+            this.refresh();
+            return;
+        }
+        var n = this.nodes[index];
+        for(var i = 0, len = records.length; i < len; i++){
+            var d = this.prepareData(records[i].data, i, records[i]);
+            if(n){
+                this.tpl.insertBefore(n, d);
+            }else{
+                
+                this.tpl.append(this.el, d);
+            }
+        }
+        this.updateIndexes(index);
+    },
 
-Roo.apply(Roo.bootstrap.Popover, {
+    onRemove : function(ds, record, index){
+       // Roo.log('onRemove');
+        this.clearSelections();
+        var el = this.dataName  ?
+            this.el.child('.roo-tpl-' + this.dataName) :
+            this.el; 
+        
+        el.dom.removeChild(this.nodes[index]);
+        this.updateIndexes(index);
+    },
 
-    alignment : {
-        'left' : ['r-l', [-10,0], 'left bs-popover-left'],
-        'right' : ['l-br', [10,0], 'right bs-popover-right'],
-        'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
-        'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
+    /**
+     * Refresh an individual node.
+     * @param {Number} index
+     */
+    refreshNode : function(index){
+        this.onUpdate(this.store, this.store.getAt(index));
     },
-    
-    zIndex : 20001,
 
-    clickHander : false,
-    
-    
+    updateIndexes : function(startIndex, endIndex){
+        var ns = this.nodes;
+        startIndex = startIndex || 0;
+        endIndex = endIndex || ns.length - 1;
+        for(var i = startIndex; i <= endIndex; i++){
+            ns[i].nodeIndex = i;
+        }
+    },
 
-    onMouseDown : function(e)
-    {
-        if (this.popups.length &&  !e.getTarget(".roo-popover")) {
-            /// what is nothing is showing..
-            this.hideAll();
+    /**
+     * Changes the data store this view uses and refresh the view.
+     * @param {Store} store
+     */
+    setStore : function(store, initial){
+        if(!initial && this.store){
+            this.store.un("datachanged", this.refresh);
+            this.store.un("add", this.onAdd);
+            this.store.un("remove", this.onRemove);
+            this.store.un("update", this.onUpdate);
+            this.store.un("clear", this.refresh);
+            this.store.un("beforeload", this.onBeforeLoad);
+            this.store.un("load", this.onLoad);
+            this.store.un("loadexception", this.onLoad);
+        }
+        if(store){
+          
+            store.on("datachanged", this.refresh, this);
+            store.on("add", this.onAdd, this);
+            store.on("remove", this.onRemove, this);
+            store.on("update", this.onUpdate, this);
+            store.on("clear", this.refresh, this);
+            store.on("beforeload", this.onBeforeLoad, this);
+            store.on("load", this.onLoad, this);
+            store.on("loadexception", this.onLoad, this);
+        }
+        
+        if(store){
+            this.refresh();
         }
-         
     },
-    
-    
-    popups : [],
-    
-    register : function(popup)
+    /**
+     * onbeforeLoad - masks the loading area.
+     *
+     */
+    onBeforeLoad : function(store,opts)
     {
-        if (!Roo.bootstrap.Popover.clickHandler) {
-            Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
+         //Roo.log('onBeforeLoad');   
+        if (!opts.add) {
+            this.el.update("");
         }
-        // hide other popups.
-        popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
-        popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
-        this.hideAll(); //<< why?
-        //this.popups.push(popup);
+        this.el.mask(this.mask ? this.mask : "Loading" ); 
     },
-    hideAll : function()
+    onLoad : function ()
     {
-        this.popups.forEach(function(p) {
-            p.hide();
-        });
-    },
-    onShow : function() {
-        Roo.bootstrap.Popover.popups.push(this);
+        this.el.unmask();
     },
-    onHide : function() {
-        Roo.bootstrap.Popover.popups.remove(this);
-    } 
+    
 
-});/*
- * - LGPL
- *
- * Card header - holder for the card header elements.
- * 
- */
+    /**
+     * Returns the template node the passed child belongs to or null if it doesn't belong to one.
+     * @param {HTMLElement} node
+     * @return {HTMLElement} The template node
+     */
+    findItemFromChild : function(node){
+        var el = this.dataName  ?
+            this.el.child('.roo-tpl-' + this.dataName,true) :
+            this.el.dom; 
+        
+        if(!node || node.parentNode == el){
+                   return node;
+           }
+           var p = node.parentNode;
+           while(p && p != el){
+            if(p.parentNode == el){
+               return p;
+            }
+            p = p.parentNode;
+        }
+           return null;
+    },
 
-/**
- * @class Roo.bootstrap.PopoverNav
- * @extends Roo.bootstrap.NavGroup
- * Bootstrap Popover header navigation class
- * @constructor
- * Create a new Popover Header Navigation 
- * @param {Object} config The config object
- */
+    /** @ignore */
+    onClick : function(e){
+        var item = this.findItemFromChild(e.getTarget());
+        if(item){
+            var index = this.indexOf(item);
+            if(this.onItemClick(item, index, e) !== false){
+                this.fireEvent("click", this, index, item, e);
+            }
+        }else{
+            this.clearSelections();
+        }
+    },
 
-Roo.bootstrap.PopoverNav = function(config){
-    Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
-};
+    /** @ignore */
+    onContextMenu : function(e){
+        var item = this.findItemFromChild(e.getTarget());
+        if(item){
+            this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
+        }
+    },
 
-Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
-    
-    
-    container_method : 'getPopoverHeader' 
-    
-     
-    
-    
-   
-});
-
-
- /*
- * - LGPL
- *
- * Progress
- * 
- */
-
-/**
- * @class Roo.bootstrap.Progress
- * @extends Roo.bootstrap.Component
- * Bootstrap Progress class
- * @cfg {Boolean} striped striped of the progress bar
- * @cfg {Boolean} active animated of the progress bar
- * 
- * 
- * @constructor
- * Create a new Progress
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Progress = function(config){
-    Roo.bootstrap.Progress.superclass.constructor.call(this, config);
-};
+    /** @ignore */
+    onDblClick : function(e){
+        var item = this.findItemFromChild(e.getTarget());
+        if(item){
+            this.fireEvent("dblclick", this, this.indexOf(item), item, e);
+        }
+    },
 
-Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
-    
-    striped : false,
-    active: false,
-    
-    getAutoCreate : function(){
-        var cfg = {
-            tag: 'div',
-            cls: 'progress'
-        };
-        
-        
-        if(this.striped){
-            cfg.cls += ' progress-striped';
+    onItemClick : function(item, index, e)
+    {
+        if(this.fireEvent("beforeclick", this, index, item, e) === false){
+            return false;
         }
-      
-        if(this.active){
-            cfg.cls += ' active';
+        if (this.toggleSelect) {
+            var m = this.isSelected(item) ? 'unselect' : 'select';
+            //Roo.log(m);
+            var _t = this;
+            _t[m](item, true, false);
+            return true;
         }
-        
-        
-        return cfg;
-    }
-   
-});
-
+        if(this.multiSelect || this.singleSelect){
+            if(this.multiSelect && e.shiftKey && this.lastSelection){
+                this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
+            }else{
+                this.select(item, this.multiSelect && e.ctrlKey);
+                this.lastSelection = item;
+            }
+            
+            if(!this.tickable){
+                e.preventDefault();
+            }
+            
+        }
+        return true;
+    },
 
- /*
- * - LGPL
- *
- * ProgressBar
- * 
- */
+    /**
+     * Get the number of selected nodes.
+     * @return {Number}
+     */
+    getSelectionCount : function(){
+        return this.selections.length;
+    },
 
-/**
- * @class Roo.bootstrap.ProgressBar
- * @extends Roo.bootstrap.Component
- * Bootstrap ProgressBar class
- * @cfg {Number} aria_valuenow aria-value now
- * @cfg {Number} aria_valuemin aria-value min
- * @cfg {Number} aria_valuemax aria-value max
- * @cfg {String} label label for the progress bar
- * @cfg {String} panel (success | info | warning | danger )
- * @cfg {String} role role of the progress bar
- * @cfg {String} sr_only text
- * 
- * 
- * @constructor
- * Create a new ProgressBar
- * @param {Object} config The config object
- */
+    /**
+     * Get the currently selected nodes.
+     * @return {Array} An array of HTMLElements
+     */
+    getSelectedNodes : function(){
+        return this.selections;
+    },
 
-Roo.bootstrap.ProgressBar = function(config){
-    Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
-};
+    /**
+     * Get the indexes of the selected nodes.
+     * @return {Array}
+     */
+    getSelectedIndexes : function(){
+        var indexes = [], s = this.selections;
+        for(var i = 0, len = s.length; i < len; i++){
+            indexes.push(s[i].nodeIndex);
+        }
+        return indexes;
+    },
 
-Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
-    
-    aria_valuenow : 0,
-    aria_valuemin : 0,
-    aria_valuemax : 100,
-    label : false,
-    panel : false,
-    role : false,
-    sr_only: false,
-    
-    getAutoCreate : function()
-    {
-        
-        var cfg = {
-            tag: 'div',
-            cls: 'progress-bar',
-            style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
-        };
-        
-        if(this.sr_only){
-            cfg.cn = {
-                tag: 'span',
-                cls: 'sr-only',
-                html: this.sr_only
+    /**
+     * Clear all selections
+     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
+     */
+    clearSelections : function(suppressEvent){
+        if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
+            this.cmp.elements = this.selections;
+            this.cmp.removeClass(this.selectedClass);
+            this.selections = [];
+            if(!suppressEvent){
+                this.fireEvent("selectionchange", this, this.selections);
             }
         }
-        
-        if(this.role){
-            cfg.role = this.role;
-        }
-        
-        if(this.aria_valuenow){
-            cfg['aria-valuenow'] = this.aria_valuenow;
+    },
+
+    /**
+     * Returns true if the passed node is selected
+     * @param {HTMLElement/Number} node The node or node index
+     * @return {Boolean}
+     */
+    isSelected : function(node){
+        var s = this.selections;
+        if(s.length < 1){
+            return false;
         }
-        
-        if(this.aria_valuemin){
-            cfg['aria-valuemin'] = this.aria_valuemin;
+        node = this.getNode(node);
+        return s.indexOf(node) !== -1;
+    },
+
+    /**
+     * Selects nodes.
+     * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+     * @param {Boolean} keepExisting (optional) true to keep existing selections
+     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+     */
+    select : function(nodeInfo, keepExisting, suppressEvent){
+        if(nodeInfo instanceof Array){
+            if(!keepExisting){
+                this.clearSelections(true);
+            }
+            for(var i = 0, len = nodeInfo.length; i < len; i++){
+                this.select(nodeInfo[i], true, true);
+            }
+            return;
+        } 
+        var node = this.getNode(nodeInfo);
+        if(!node || this.isSelected(node)){
+            return; // already selected.
         }
-        
-        if(this.aria_valuemax){
-            cfg['aria-valuemax'] = this.aria_valuemax;
+        if(!keepExisting){
+            this.clearSelections(true);
         }
         
-        if(this.label && !this.sr_only){
-            cfg.html = this.label;
+        if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
+            Roo.fly(node).addClass(this.selectedClass);
+            this.selections.push(node);
+            if(!suppressEvent){
+                this.fireEvent("selectionchange", this, this.selections);
+            }
         }
         
-        if(this.panel){
-            cfg.cls += ' progress-bar-' + this.panel;
-        }
         
-        return cfg;
     },
-    
-    update : function(aria_valuenow)
+      /**
+     * Unselects nodes.
+     * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+     * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
+     * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+     */
+    unselect : function(nodeInfo, keepExisting, suppressEvent)
     {
-        this.aria_valuenow = aria_valuenow;
-        
-        this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
-    }
-   
-});
-
-
- /*
+        if(nodeInfo instanceof Array){
+            Roo.each(this.selections, function(s) {
+                this.unselect(s, nodeInfo);
+            }, this);
+            return;
+        }
+        var node = this.getNode(nodeInfo);
+        if(!node || !this.isSelected(node)){
+            //Roo.log("not selected");
+            return; // not selected.
+        }
+        // fireevent???
+        var ns = [];
+        Roo.each(this.selections, function(s) {
+            if (s == node ) {
+                Roo.fly(node).removeClass(this.selectedClass);
+
+                return;
+            }
+            ns.push(s);
+        },this);
+        
+        this.selections= ns;
+        this.fireEvent("selectionchange", this, this.selections);
+    },
+
+    /**
+     * Gets a template node.
+     * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
+     * @return {HTMLElement} The node or null if it wasn't found
+     */
+    getNode : function(nodeInfo){
+        if(typeof nodeInfo == "string"){
+            return document.getElementById(nodeInfo);
+        }else if(typeof nodeInfo == "number"){
+            return this.nodes[nodeInfo];
+        }
+        return nodeInfo;
+    },
+
+    /**
+     * Gets a range template nodes.
+     * @param {Number} startIndex
+     * @param {Number} endIndex
+     * @return {Array} An array of nodes
+     */
+    getNodes : function(start, end){
+        var ns = this.nodes;
+        start = start || 0;
+        end = typeof end == "undefined" ? ns.length - 1 : end;
+        var nodes = [];
+        if(start <= end){
+            for(var i = start; i <= end; i++){
+                nodes.push(ns[i]);
+            }
+        } else{
+            for(var i = start; i >= end; i--){
+                nodes.push(ns[i]);
+            }
+        }
+        return nodes;
+    },
+
+    /**
+     * Finds the index of the passed node
+     * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
+     * @return {Number} The index of the node or -1
+     */
+    indexOf : function(node){
+        node = this.getNode(node);
+        if(typeof node.nodeIndex == "number"){
+            return node.nodeIndex;
+        }
+        var ns = this.nodes;
+        for(var i = 0, len = ns.length; i < len; i++){
+            if(ns[i] == node){
+                return i;
+            }
+        }
+        return -1;
+    }
+});
+/*
  * - LGPL
  *
- * column
+ * based on jquery fullcalendar
  * 
  */
 
+Roo.bootstrap = Roo.bootstrap || {};
 /**
- * @class Roo.bootstrap.TabGroup
- * @extends Roo.bootstrap.Column
- * Bootstrap Column class
- * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
- * @cfg {Boolean} carousel true to make the group behave like a carousel
- * @cfg {Boolean} bullets show bullets for the panels
- * @cfg {Boolean} autoslide (true|false) auto slide .. default false
- * @cfg {Number} timer auto slide timer .. default 0 millisecond
- * @cfg {Boolean} showarrow (true|false) show arrow default true
- * 
+ * @class Roo.bootstrap.Calendar
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Calendar class
+ * @cfg {Boolean} loadMask (true|false) default false
+ * @cfg {Object} header generate the user specific header of the calendar, default false
+
  * @constructor
- * Create a new TabGroup
+ * Create a new Container
  * @param {Object} config The config object
  */
 
-Roo.bootstrap.TabGroup = function(config){
-    Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
-    if (!this.navId) {
-        this.navId = Roo.id();
-    }
-    this.tabs = [];
-    Roo.bootstrap.TabGroup.register(this);
-    
+
+
+Roo.bootstrap.Calendar = function(config){
+    Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
+     this.addEvents({
+        /**
+            * @event select
+            * Fires when a date is selected
+            * @param {DatePicker} this
+            * @param {Date} date The selected date
+            */
+        'select': true,
+        /**
+            * @event monthchange
+            * Fires when the displayed month changes 
+            * @param {DatePicker} this
+            * @param {Date} date The selected month
+            */
+        'monthchange': true,
+        /**
+            * @event evententer
+            * Fires when mouse over an event
+            * @param {Calendar} this
+            * @param {event} Event
+            */
+        'evententer': true,
+        /**
+            * @event eventleave
+            * Fires when the mouse leaves an
+            * @param {Calendar} this
+            * @param {event}
+            */
+        'eventleave': true,
+        /**
+            * @event eventclick
+            * Fires when the mouse click an
+            * @param {Calendar} this
+            * @param {event}
+            */
+        'eventclick': true
+        
+    });
+
 };
 
-Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
+Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
     
-    carousel : false,
-    transition : false,
-    bullets : 0,
-    timer : 0,
-    autoslide : false,
-    slideFn : false,
-    slideOnTouch : false,
-    showarrow : true,
+     /**
+     * @cfg {Number} startDay
+     * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+     */
+    startDay : 0,
     
-    getAutoCreate : function()
-    {
-        var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
-        
-        cfg.cls += ' tab-content';
+    loadMask : false,
+    
+    header : false,
+      
+    getAutoCreate : function(){
         
-        if (this.carousel) {
-            cfg.cls += ' carousel slide';
-            
-            cfg.cn = [{
-               cls : 'carousel-inner',
-               cn : []
-            }];
         
-            if(this.bullets  && !Roo.isTouch){
-                
-                var bullets = {
-                    cls : 'carousel-bullets',
-                    cn : []
-                };
-               
-                if(this.bullets_cls){
-                    bullets.cls = bullets.cls + ' ' + this.bullets_cls;
-                }
-                
-                bullets.cn.push({
-                    cls : 'clear'
-                });
-                
-                cfg.cn[0].cn.push(bullets);
-            }
-            
-            if(this.showarrow){
-                cfg.cn[0].cn.push({
-                    tag : 'div',
-                    class : 'carousel-arrow',
-                    cn : [
-                        {
-                            tag : 'div',
-                            class : 'carousel-prev',
-                            cn : [
-                                {
-                                    tag : 'i',
-                                    class : 'fa fa-chevron-left'
-                                }
-                            ]
-                        },
-                        {
-                            tag : 'div',
-                            class : 'carousel-next',
-                            cn : [
-                                {
-                                    tag : 'i',
-                                    class : 'fa fa-chevron-right'
-                                }
-                            ]
-                        }
-                    ]
-                });
-            }
-            
-        }
+        var fc_button = function(name, corner, style, content ) {
+            return Roo.apply({},{
+                tag : 'span',
+                cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
+                         (corner.length ?
+                            'fc-corner-' + corner.split(' ').join(' fc-corner-') :
+                            ''
+                        ),
+                html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
+                unselectable: 'on'
+            });
+        };
         
-        return cfg;
-    },
-    
-    initEvents:  function()
-    {
-//        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
-//            this.el.on("touchstart", this.onTouchStart, this);
-//        }
+        var header = {};
         
-        if(this.autoslide){
-            var _this = this;
-            
-            this.slideFn = window.setInterval(function() {
-                _this.showPanelNext();
-            }, this.timer);
+        if(!this.header){
+            header = {
+                tag : 'table',
+                cls : 'fc-header',
+                style : 'width:100%',
+                cn : [
+                    {
+                        tag: 'tr',
+                        cn : [
+                            {
+                                tag : 'td',
+                                cls : 'fc-header-left',
+                                cn : [
+                                    fc_button('prev', 'left', 'arrow', '&#8249;' ),
+                                    fc_button('next', 'right', 'arrow', '&#8250;' ),
+                                    { tag: 'span', cls: 'fc-header-space' },
+                                    fc_button('today', 'left right', '', 'today' )  // neds state disabled..
+
+
+                                ]
+                            },
+
+                            {
+                                tag : 'td',
+                                cls : 'fc-header-center',
+                                cn : [
+                                    {
+                                        tag: 'span',
+                                        cls: 'fc-header-title',
+                                        cn : {
+                                            tag: 'H2',
+                                            html : 'month / year'
+                                        }
+                                    }
+
+                                ]
+                            },
+                            {
+                                tag : 'td',
+                                cls : 'fc-header-right',
+                                cn : [
+                              /*      fc_button('month', 'left', '', 'month' ),
+                                    fc_button('week', '', '', 'week' ),
+                                    fc_button('day', 'right', '', 'day' )
+                                */    
+
+                                ]
+                            }
+
+                        ]
+                    }
+                ]
+            };
         }
         
-        if(this.showarrow){
-            this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
-            this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
-        }
+        header = this.header;
         
+       
+        var cal_heads = function() {
+            var ret = [];
+            // fixme - handle this.
+            
+            for (var i =0; i < Date.dayNames.length; i++) {
+                var d = Date.dayNames[i];
+                ret.push({
+                    tag: 'th',
+                    cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
+                    html : d.substring(0,3)
+                });
+                
+            }
+            ret[0].cls += ' fc-first';
+            ret[6].cls += ' fc-last';
+            return ret;
+        };
+        var cal_cell = function(n) {
+            return  {
+                tag: 'td',
+                cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
+                cn : [
+                    {
+                        cn : [
+                            {
+                                cls: 'fc-day-number',
+                                html: 'D'
+                            },
+                            {
+                                cls: 'fc-day-content',
+                             
+                                cn : [
+                                     {
+                                        style: 'position: relative;' // height: 17px;
+                                    }
+                                ]
+                            }
+                            
+                            
+                        ]
+                    }
+                ]
+                
+            }
+        };
+        var cal_rows = function() {
+            
+            var ret = [];
+            for (var r = 0; r < 6; r++) {
+                var row= {
+                    tag : 'tr',
+                    cls : 'fc-week',
+                    cn : []
+                };
+                
+                for (var i =0; i < Date.dayNames.length; i++) {
+                    var d = Date.dayNames[i];
+                    row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
+
+                }
+                row.cn[0].cls+=' fc-first';
+                row.cn[0].cn[0].style = 'min-height:90px';
+                row.cn[6].cls+=' fc-last';
+                ret.push(row);
+                
+            }
+            ret[0].cls += ' fc-first';
+            ret[4].cls += ' fc-prev-last';
+            ret[5].cls += ' fc-last';
+            return ret;
+            
+        };
         
-    },
-    
-//    onTouchStart : function(e, el, o)
-//    {
-//        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
-//            return;
-//        }
-//        
-//        this.showPanelNext();
-//    },
-    
+        var cal_table = {
+            tag: 'table',
+            cls: 'fc-border-separate',
+            style : 'width:100%',
+            cellspacing  : 0,
+            cn : [
+                { 
+                    tag: 'thead',
+                    cn : [
+                        { 
+                            tag: 'tr',
+                            cls : 'fc-first fc-last',
+                            cn : cal_heads()
+                        }
+                    ]
+                },
+                { 
+                    tag: 'tbody',
+                    cn : cal_rows()
+                }
+                  
+            ]
+        };
+         
+         var cfg = {
+            cls : 'fc fc-ltr',
+            cn : [
+                header,
+                {
+                    cls : 'fc-content',
+                    style : "position: relative;",
+                    cn : [
+                        {
+                            cls : 'fc-view fc-view-month fc-grid',
+                            style : 'position: relative',
+                            unselectable : 'on',
+                            cn : [
+                                {
+                                    cls : 'fc-event-container',
+                                    style : 'position:absolute;z-index:8;top:0;left:0;'
+                                },
+                                cal_table
+                            ]
+                        }
+                    ]
     
-    getChildContainer : function()
-    {
-        return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
+                }
+           ] 
+            
+        };
+        
+         
+        
+        return cfg;
     },
     
-    /**
-    * register a Navigation item
-    * @param {Roo.bootstrap.NavItem} the navitem to add
-    */
-    register : function(item)
-    {
-        this.tabs.push( item);
-        item.navId = this.navId; // not really needed..
-        this.addBullet();
-    
-    },
     
-    getActivePanel : function()
+    initEvents : function()
     {
-        var r = false;
-        Roo.each(this.tabs, function(t) {
-            if (t.active) {
-                r = t;
-                return false;
-            }
-            return null;
-        });
-        return r;
+        if(!this.store){
+            throw "can not find store for calendar";
+        }
         
-    },
-    getPanelByName : function(n)
-    {
-        var r = false;
-        Roo.each(this.tabs, function(t) {
-            if (t.tabId == n) {
-                r = t;
-                return false;
-            }
-            return null;
-        });
-        return r;
-    },
-    indexOfPanel : function(p)
-    {
-        var r = false;
-        Roo.each(this.tabs, function(t,i) {
-            if (t.tabId == p.tabId) {
-                r = i;
-                return false;
-            }
-            return null;
-        });
-        return r;
-    },
-    /**
-     * show a specific panel
-     * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
-     * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
-     */
-    showPanel : function (pan)
-    {
-        if(this.transition || typeof(pan) == 'undefined'){
-            Roo.log("waiting for the transitionend");
-            return false;
-        }
-        
-        if (typeof(pan) == 'number') {
-            pan = this.tabs[pan];
-        }
-        
-        if (typeof(pan) == 'string') {
-            pan = this.getPanelByName(pan);
-        }
-        
-        var cur = this.getActivePanel();
-        
-        if(!pan || !cur){
-            Roo.log('pan or acitve pan is undefined');
-            return false;
-        }
-        
-        if (pan.tabId == this.getActivePanel().tabId) {
-            return true;
-        }
-        
-        if (false === cur.fireEvent('beforedeactivate')) {
-            return false;
-        }
-        
-        if(this.bullets > 0 && !Roo.isTouch){
-            this.setActiveBullet(this.indexOfPanel(pan));
-        }
+        var mark = {
+            tag: "div",
+            cls:"x-dlg-mask",
+            style: "text-align:center",
+            cn: [
+                {
+                    tag: "div",
+                    style: "background-color:white;width:50%;margin:250 auto",
+                    cn: [
+                        {
+                            tag: "img",
+                            src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
+                        },
+                        {
+                            tag: "span",
+                            html: "Loading"
+                        }
+                        
+                    ]
+                }
+            ]
+        };
+        this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
         
-        if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
-            
-            //class="carousel-item carousel-item-next carousel-item-left"
-            
-            this.transition = true;
-            var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
-            var lr = dir == 'next' ? 'left' : 'right';
-            pan.el.addClass(dir); // or prev
-            pan.el.addClass('carousel-item-' + dir); // or prev
-            pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
-            cur.el.addClass(lr); // or right
-            pan.el.addClass(lr);
-            cur.el.addClass('carousel-item-' +lr); // or right
-            pan.el.addClass('carousel-item-' +lr);
-            
-            
-            var _this = this;
-            cur.el.on('transitionend', function() {
-                Roo.log("trans end?");
-                
-                pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
-                pan.setActive(true);
-                
-                cur.el.removeClass([lr, 'carousel-item-' + lr]);
-                cur.setActive(false);
-                
-                _this.transition = false;
-                
-            }, this, { single:  true } );
-            
-            return true;
+        var size = this.el.select('.fc-content', true).first().getSize();
+        this.maskEl.setSize(size.width, size.height);
+        this.maskEl.enableDisplayMode("block");
+        if(!this.loadMask){
+            this.maskEl.hide();
         }
         
-        cur.setActive(false);
-        pan.setActive(true);
+        this.store = Roo.factory(this.store, Roo.data);
+        this.store.on('load', this.onLoad, this);
+        this.store.on('beforeload', this.onBeforeLoad, this);
         
-        return true;
+        this.resize();
         
-    },
-    showPanelNext : function()
-    {
-        var i = this.indexOfPanel(this.getActivePanel());
+        this.cells = this.el.select('.fc-day',true);
+        //Roo.log(this.cells);
+        this.textNodes = this.el.query('.fc-day-number');
+        this.cells.addClassOnOver('fc-state-hover');
         
-        if (i >= this.tabs.length - 1 && !this.autoslide) {
-            return;
-        }
+        this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
+        this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
+        this.el.select('.fc-button-today',true).on('click', this.showToday, this);
+        this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
         
-        if (i >= this.tabs.length - 1 && this.autoslide) {
-            i = -1;
-        }
+        this.on('monthchange', this.onMonthChange, this);
         
-        this.showPanel(this.tabs[i+1]);
+        this.update(new Date().clearTime());
     },
     
-    showPanelPrev : function()
-    {
-        var i = this.indexOfPanel(this.getActivePanel());
-        
-        if (i  < 1 && !this.autoslide) {
-            return;
-        }
-        
-        if (i < 1 && this.autoslide) {
-            i = this.tabs.length;
-        }
+    resize : function() {
+        var sz  = this.el.getSize();
         
-        this.showPanel(this.tabs[i-1]);
+        this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
+        this.el.select('.fc-day-content div',true).setHeight(34);
     },
     
     
-    addBullet: function()
-    {
-        if(!this.bullets || Roo.isTouch){
-            return;
-        }
-        var ctr = this.el.select('.carousel-bullets',true).first();
-        var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
-        var bullet = ctr.createChild({
-            cls : 'bullet bullet-' + i
-        },ctr.dom.lastChild);
-        
-        
-        var _this = this;
-        
-        bullet.on('click', (function(e, el, o, ii, t){
-
-            e.preventDefault();
-
-            this.showPanel(ii);
+    // private
+    showPrevMonth : function(e){
+        this.update(this.activeDate.add("mo", -1));
+    },
+    showToday : function(e){
+        this.update(new Date().clearTime());
+    },
+    // private
+    showNextMonth : function(e){
+        this.update(this.activeDate.add("mo", 1));
+    },
 
-            if(this.autoslide && this.slideFn){
-                clearInterval(this.slideFn);
-                this.slideFn = window.setInterval(function() {
-                    _this.showPanelNext();
-                }, this.timer);
-            }
+    // private
+    showPrevYear : function(){
+        this.update(this.activeDate.add("y", -1));
+    },
 
-        }).createDelegate(this, [i, bullet], true));
-                
-        
+    // private
+    showNextYear : function(){
+        this.update(this.activeDate.add("y", 1));
     },
-     
-    setActiveBullet : function(i)
+
+    
+   // private
+    update : function(date)
     {
-        if(Roo.isTouch){
-            return;
-        }
+        var vd = this.activeDate;
+        this.activeDate = date;
+//        if(vd && this.el){
+//            var t = date.getTime();
+//            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
+//                Roo.log('using add remove');
+//                
+//                this.fireEvent('monthchange', this, date);
+//                
+//                this.cells.removeClass("fc-state-highlight");
+//                this.cells.each(function(c){
+//                   if(c.dateValue == t){
+//                       c.addClass("fc-state-highlight");
+//                       setTimeout(function(){
+//                            try{c.dom.firstChild.focus();}catch(e){}
+//                       }, 50);
+//                       return false;
+//                   }
+//                   return true;
+//                });
+//                return;
+//            }
+//        }
         
-        Roo.each(this.el.select('.bullet', true).elements, function(el){
-            el.removeClass('selected');
-        });
-
-        var bullet = this.el.select('.bullet-' + i, true).first();
+        var days = date.getDaysInMonth();
         
-        if(!bullet){
-            return;
+        var firstOfMonth = date.getFirstDateOfMonth();
+        var startingPos = firstOfMonth.getDay()-this.startDay;
+        
+        if(startingPos < this.startDay){
+            startingPos += 7;
         }
         
-        bullet.addClass('selected');
-    }
-    
-    
-  
-});
-
+        var pm = date.add(Date.MONTH, -1);
+        var prevStart = pm.getDaysInMonth()-startingPos;
+//        
+        this.cells = this.el.select('.fc-day',true);
+        this.textNodes = this.el.query('.fc-day-number');
+        this.cells.addClassOnOver('fc-state-hover');
+        
+        var cells = this.cells.elements;
+        var textEls = this.textNodes;
+        
+        Roo.each(cells, function(cell){
+            cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
+        });
+        
+        days += startingPos;
 
-Roo.apply(Roo.bootstrap.TabGroup, {
-    
-    groups: {},
-     /**
-    * register a Navigation Group
-    * @param {Roo.bootstrap.NavGroup} the navgroup to add
-    */
-    register : function(navgrp)
-    {
-        this.groups[navgrp.navId] = navgrp;
-       
-    },
-    /**
-    * fetch a Navigation Group based on the navigation ID
-    * if one does not exist , it will get created.
-    * @param {string} the navgroup to add
-    * @returns {Roo.bootstrap.NavGroup} the navgroup 
-    */
-    get: function(navId) {
-        if (typeof(this.groups[navId]) == 'undefined') {
-            this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
-        }
-        return this.groups[navId] ;
-    }
-    
-    
-    
-});
-
- /*
- * - LGPL
- *
- * TabPanel
- * 
- */
-
-/**
- * @class Roo.bootstrap.TabPanel
- * @extends Roo.bootstrap.Component
- * Bootstrap TabPanel class
- * @cfg {Boolean} active panel active
- * @cfg {String} html panel content
- * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
- * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
- * @cfg {String} href click to link..
- * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
- * 
- * 
- * @constructor
- * Create a new TabPanel
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TabPanel = function(config){
-    Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
-    this.addEvents({
-        /**
-            * @event changed
-            * Fires when the active status changes
-            * @param {Roo.bootstrap.TabPanel} this
-            * @param {Boolean} state the new state
-           
-         */
-        'changed': true,
-        /**
-            * @event beforedeactivate
-            * Fires before a tab is de-activated - can be used to do validation on a form.
-            * @param {Roo.bootstrap.TabPanel} this
-            * @return {Boolean} false if there is an error
-           
-         */
-        'beforedeactivate': true
-     });
-    
-    this.tabId = this.tabId || Roo.id();
-  
-};
-
-Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
-    
-    active: false,
-    html: false,
-    tabId: false,
-    navId : false,
-    href : '',
-    touchSlide : false,
-    getAutoCreate : function(){
+        // convert everything to numbers so it's fast
+        var day = 86400000;
+        var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
+        //Roo.log(d);
+        //Roo.log(pm);
+        //Roo.log(prevStart);
         
-       
-       var cfg = {
-            tag: 'div',
-            // item is needed for carousel - not sure if it has any effect otherwise
-            cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
-            html: this.html || ''
+        var today = new Date().clearTime().getTime();
+        var sel = date.clearTime().getTime();
+        var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
+        var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
+        var ddMatch = this.disabledDatesRE;
+        var ddText = this.disabledDatesText;
+        var ddays = this.disabledDays ? this.disabledDays.join("") : false;
+        var ddaysText = this.disabledDaysText;
+        var format = this.format;
+        
+        var setCellClass = function(cal, cell){
+            cell.row = 0;
+            cell.events = [];
+            cell.more = [];
+            //Roo.log('set Cell Class');
+            cell.title = "";
+            var t = d.getTime();
+            
+            //Roo.log(d);
+            
+            cell.dateValue = t;
+            if(t == today){
+                cell.className += " fc-today";
+                cell.className += " fc-state-highlight";
+                cell.title = cal.todayText;
+            }
+            if(t == sel){
+                // disable highlight in other month..
+                //cell.className += " fc-state-highlight";
+                
+            }
+            // disabling
+            if(t < min) {
+                cell.className = " fc-state-disabled";
+                cell.title = cal.minText;
+                return;
+            }
+            if(t > max) {
+                cell.className = " fc-state-disabled";
+                cell.title = cal.maxText;
+                return;
+            }
+            if(ddays){
+                if(ddays.indexOf(d.getDay()) != -1){
+                    cell.title = ddaysText;
+                    cell.className = " fc-state-disabled";
+                }
+            }
+            if(ddMatch && format){
+                var fvalue = d.dateFormat(format);
+                if(ddMatch.test(fvalue)){
+                    cell.title = ddText.replace("%0", fvalue);
+                    cell.className = " fc-state-disabled";
+                }
+            }
+            
+            if (!cell.initialClassName) {
+                cell.initialClassName = cell.dom.className;
+            }
+            
+            cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
         };
+
+        var i = 0;
         
-        if(this.active){
-            cfg.cls += ' active';
+        for(; i < startingPos; i++) {
+            textEls[i].innerHTML = (++prevStart);
+            d.setDate(d.getDate()+1);
+            
+            cells[i].className = "fc-past fc-other-month";
+            setCellClass(this, cells[i]);
         }
         
-        if(this.tabId){
-            cfg.tabId = this.tabId;
+        var intDay = 0;
+        
+        for(; i < days; i++){
+            intDay = i - startingPos + 1;
+            textEls[i].innerHTML = (intDay);
+            d.setDate(d.getDate()+1);
+            
+            cells[i].className = ''; // "x-date-active";
+            setCellClass(this, cells[i]);
         }
+        var extraDays = 0;
         
-       
+        for(; i < 42; i++) {
+            textEls[i].innerHTML = (++extraDays);
+            d.setDate(d.getDate()+1);
+            
+            cells[i].className = "fc-future fc-other-month";
+            setCellClass(this, cells[i]);
+        }
         
-        return cfg;
-    },
-    
-    initEvents:  function()
-    {
-        var p = this.parent();
+        this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
         
-        this.navId = this.navId || p.navId;
+        var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
         
-        if (typeof(this.navId) != 'undefined') {
-            // not really needed.. but just in case.. parent should be a NavGroup.
-            var tg = Roo.bootstrap.TabGroup.get(this.navId);
-            
-            tg.register(this);
-            
-            var i = tg.tabs.length - 1;
-            
-            if(this.active && tg.bullets > 0 && i < tg.bullets){
-                tg.setActiveBullet(i);
-            }
+        this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
+        this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
+        
+        if(totalRows != 6){
+            this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
+            this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
         }
         
-        this.el.on('click', this.onClick, this);
+        this.fireEvent('monthchange', this, date);
         
-        if(Roo.isTouch && this.touchSlide){
-            this.el.on("touchstart", this.onTouchStart, this);
-            this.el.on("touchmove", this.onTouchMove, this);
-            this.el.on("touchend", this.onTouchEnd, this);
+        
+        /*
+        if(!this.internalRender){
+            var main = this.el.dom.firstChild;
+            var w = main.offsetWidth;
+            this.el.setWidth(w + this.el.getBorderWidth("lr"));
+            Roo.fly(main).setWidth(w);
+            this.internalRender = true;
+            // opera does not respect the auto grow header center column
+            // then, after it gets a width opera refuses to recalculate
+            // without a second pass
+            if(Roo.isOpera && !this.secondPass){
+                main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
+                this.secondPass = true;
+                this.update.defer(10, this, [date]);
+            }
         }
+        */
         
     },
     
-    onRender : function(ct, position)
-    {
-        Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
+    findCell : function(dt) {
+        dt = dt.clearTime().getTime();
+        var ret = false;
+        this.cells.each(function(c){
+            //Roo.log("check " +c.dateValue + '?=' + dt);
+            if(c.dateValue == dt){
+                ret = c;
+                return false;
+            }
+            return true;
+        });
+        
+        return ret;
     },
     
-    setActive : function(state)
-    {
-        Roo.log("panel - set active " + this.tabId + "=" + state);
-        
-        this.active = state;
-        if (!state) {
-            this.el.removeClass('active');
+    findCells : function(ev) {
+        var s = ev.start.clone().clearTime().getTime();
+       // Roo.log(s);
+        var e= ev.end.clone().clearTime().getTime();
+       // Roo.log(e);
+        var ret = [];
+        this.cells.each(function(c){
+             ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
             
-        } else  if (!this.el.hasClass('active')) {
-            this.el.addClass('active');
-        }
+            if(c.dateValue > e){
+                return ;
+            }
+            if(c.dateValue < s){
+                return ;
+            }
+            ret.push(c);
+        });
         
-        this.fireEvent('changed', this, state);
+        return ret;    
     },
     
-    onClick : function(e)
+//    findBestRow: function(cells)
+//    {
+//        var ret = 0;
+//        
+//        for (var i =0 ; i < cells.length;i++) {
+//            ret  = Math.max(cells[i].rows || 0,ret);
+//        }
+//        return ret;
+//        
+//    },
+    
+    
+    addItem : function(ev)
     {
-        e.preventDefault();
+        // look for vertical location slot in
+        var cells = this.findCells(ev);
         
-        if(!this.href.length){
-            return;
+//        ev.row = this.findBestRow(cells);
+        
+        // work out the location.
+        
+        var crow = false;
+        var rows = [];
+        for(var i =0; i < cells.length; i++) {
+            
+            cells[i].row = cells[0].row;
+            
+            if(i == 0){
+                cells[i].row = cells[i].row + 1;
+            }
+            
+            if (!crow) {
+                crow = {
+                    start : cells[i],
+                    end :  cells[i]
+                };
+                continue;
+            }
+            if (crow.start.getY() == cells[i].getY()) {
+                // on same row.
+                crow.end = cells[i];
+                continue;
+            }
+            // different row.
+            rows.push(crow);
+            crow = {
+                start: cells[i],
+                end : cells[i]
+            };
+            
         }
         
-        window.location.href = this.href;
-    },
-    
-    startX : 0,
-    startY : 0,
-    endX : 0,
-    endY : 0,
-    swiping : false,
-    
-    onTouchStart : function(e)
-    {
-        this.swiping = false;
+        rows.push(crow);
+        ev.els = [];
+        ev.rows = rows;
+        ev.cells = cells;
         
-        this.startX = e.browserEvent.touches[0].clientX;
-        this.startY = e.browserEvent.touches[0].clientY;
-    },
-    
-    onTouchMove : function(e)
-    {
-        this.swiping = true;
+        cells[0].events.push(ev);
         
-        this.endX = e.browserEvent.touches[0].clientX;
-        this.endY = e.browserEvent.touches[0].clientY;
+        this.calevents.push(ev);
     },
     
-    onTouchEnd : function(e)
-    {
-        if(!this.swiping){
-            this.onClick(e);
+    clearEvents: function() {
+        
+        if(!this.calevents){
             return;
         }
         
-        var tabGroup = this.parent();
+        Roo.each(this.cells.elements, function(c){
+            c.row = 0;
+            c.events = [];
+            c.more = [];
+        });
         
-        if(this.endX > this.startX){ // swiping right
-            tabGroup.showPanelPrev();
-            return;
-        }
+        Roo.each(this.calevents, function(e) {
+            Roo.each(e.els, function(el) {
+                el.un('mouseenter' ,this.onEventEnter, this);
+                el.un('mouseleave' ,this.onEventLeave, this);
+                el.remove();
+            },this);
+        },this);
         
-        if(this.startX > this.endX){ // swiping left
-            tabGroup.showPanelNext();
-            return;
-        }
-    }
-    
+        Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
+            e.remove();
+        });
+        
+    },
     
-});
+    renderEvents: function()
+    {   
+        var _this = this;
+        
+        this.cells.each(function(c) {
+            
+            if(c.row < 5){
+                return;
+            }
+            
+            var ev = c.events;
+            
+            var r = 4;
+            if(c.row != c.events.length){
+                r = 4 - (4 - (c.row - c.events.length));
+            }
+            
+            c.events = ev.slice(0, r);
+            c.more = ev.slice(r);
+            
+            if(c.more.length && c.more.length == 1){
+                c.events.push(c.more.pop());
+            }
+            
+            c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
+            
+        });
+            
+        this.cells.each(function(c) {
+            
+            c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
+            
+            
+            for (var e = 0; e < c.events.length; e++){
+                var ev = c.events[e];
+                var rows = ev.rows;
+                
+                for(var i = 0; i < rows.length; i++) {
+                
+                    // how many rows should it span..
 
+                    var  cfg = {
+                        cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
+                        style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
 
- /*
- * - LGPL
- *
- * DateField
- * 
- */
+                        unselectable : "on",
+                        cn : [
+                            {
+                                cls: 'fc-event-inner',
+                                cn : [
+    //                                {
+    //                                  tag:'span',
+    //                                  cls: 'fc-event-time',
+    //                                  html : cells.length > 1 ? '' : ev.time
+    //                                },
+                                    {
+                                      tag:'span',
+                                      cls: 'fc-event-title',
+                                      html : String.format('{0}', ev.title)
+                                    }
 
-/**
- * @class Roo.bootstrap.DateField
- * @extends Roo.bootstrap.Input
- * Bootstrap DateField class
- * @cfg {Number} weekStart default 0
- * @cfg {String} viewMode default empty, (months|years)
- * @cfg {String} minViewMode default empty, (months|years)
- * @cfg {Number} startDate default -Infinity
- * @cfg {Number} endDate default Infinity
- * @cfg {Boolean} todayHighlight default false
- * @cfg {Boolean} todayBtn default false
- * @cfg {Boolean} calendarWeeks default false
- * @cfg {Object} daysOfWeekDisabled default empty
- * @cfg {Boolean} singleMode default false (true | false)
- * 
- * @cfg {Boolean} keyboardNavigation default true
- * @cfg {String} language default en
- * 
- * @constructor
- * Create a new DateField
- * @param {Object} config The config object
- */
 
-Roo.bootstrap.DateField = function(config){
-    Roo.bootstrap.DateField.superclass.constructor.call(this, config);
-     this.addEvents({
-            /**
-             * @event show
-             * Fires when this field show.
-             * @param {Roo.bootstrap.DateField} this
-             * @param {Mixed} date The date value
-             */
-            show : true,
-            /**
-             * @event show
-             * Fires when this field hide.
-             * @param {Roo.bootstrap.DateField} this
-             * @param {Mixed} date The date value
-             */
-            hide : true,
-            /**
-             * @event select
-             * Fires when select a date.
-             * @param {Roo.bootstrap.DateField} this
-             * @param {Mixed} date The date value
-             */
-            select : true,
-            /**
-             * @event beforeselect
-             * Fires when before select a date.
-             * @param {Roo.bootstrap.DateField} this
-             * @param {Mixed} date The date value
-             */
-            beforeselect : true
-        });
-};
+                                ]
+                            },
+                            {
+                                cls: 'ui-resizable-handle ui-resizable-e',
+                                html : '&nbsp;&nbsp;&nbsp'
+                            }
 
-Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
-    
-    /**
-     * @cfg {String} format
-     * The default date format string which can be overriden for localization support.  The format must be
-     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
-     */
-    format : "m/d/y",
-    /**
-     * @cfg {String} altFormats
-     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
-     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
-     */
-    altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
-    
-    weekStart : 0,
-    
-    viewMode : '',
-    
-    minViewMode : '',
-    
-    todayHighlight : false,
-    
-    todayBtn: false,
-    
-    language: 'en',
-    
-    keyboardNavigation: true,
-    
-    calendarWeeks: false,
-    
-    startDate: -Infinity,
-    
-    endDate: Infinity,
-    
-    daysOfWeekDisabled: [],
-    
-    _events: [],
-    
-    singleMode : false,
-    
-    UTCDate: function()
-    {
-        return new Date(Date.UTC.apply(Date, arguments));
+                        ]
+                    };
+
+                    if (i == 0) {
+                        cfg.cls += ' fc-event-start';
+                    }
+                    if ((i+1) == rows.length) {
+                        cfg.cls += ' fc-event-end';
+                    }
+
+                    var ctr = _this.el.select('.fc-event-container',true).first();
+                    var cg = ctr.createChild(cfg);
+
+                    var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
+                    var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
+
+                    var r = (c.more.length) ? 1 : 0;
+                    cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
+                    cg.setWidth(ebox.right - sbox.x -2);
+
+                    cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
+                    cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
+                    cg.on('click', _this.onEventClick, _this, ev);
+
+                    ev.els.push(cg);
+                    
+                }
+                
+            }
+            
+            
+            if(c.more.length){
+                var  cfg = {
+                    cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
+                    style : 'position: absolute',
+                    unselectable : "on",
+                    cn : [
+                        {
+                            cls: 'fc-event-inner',
+                            cn : [
+                                {
+                                  tag:'span',
+                                  cls: 'fc-event-title',
+                                  html : 'More'
+                                }
+
+
+                            ]
+                        },
+                        {
+                            cls: 'ui-resizable-handle ui-resizable-e',
+                            html : '&nbsp;&nbsp;&nbsp'
+                        }
+
+                    ]
+                };
+
+                var ctr = _this.el.select('.fc-event-container',true).first();
+                var cg = ctr.createChild(cfg);
+
+                var sbox = c.select('.fc-day-content',true).first().getBox();
+                var ebox = c.select('.fc-day-content',true).first().getBox();
+                //Roo.log(cg);
+                cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
+                cg.setWidth(ebox.right - sbox.x -2);
+
+                cg.on('click', _this.onMoreEventClick, _this, c.more);
+                
+            }
+            
+        });
+        
+        
+        
     },
     
-    UTCToday: function()
-    {
-        var today = new Date();
-        return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
+    onEventEnter: function (e, el,event,d) {
+        this.fireEvent('evententer', this, el, event);
     },
     
-    getDate: function() {
-            var d = this.getUTCDate();
-            return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
+    onEventLeave: function (e, el,event,d) {
+        this.fireEvent('eventleave', this, el, event);
     },
     
-    getUTCDate: function() {
-            return this.date;
+    onEventClick: function (e, el,event,d) {
+        this.fireEvent('eventclick', this, el, event);
     },
     
-    setDate: function(d) {
-            this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
+    onMonthChange: function () {
+        this.store.load();
     },
     
-    setUTCDate: function(d) {
-            this.date = d;
-            this.setValue(this.formatDate(this.date));
-    },
-        
-    onRender: function(ct, position)
+    onMoreEventClick: function(e, el, more)
     {
+        var _this = this;
         
-        Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
-        
-        this.language = this.language || 'en';
-        this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
-        this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
-        
-        this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
-        this.format = this.format || 'm/d/y';
-        this.isInline = false;
-        this.isInput = true;
-        this.component = this.el.select('.add-on', true).first() || false;
-        this.component = (this.component && this.component.length === 0) ? false : this.component;
-        this.hasInput = this.component && this.inputEl().length;
-        
-        if (typeof(this.minViewMode === 'string')) {
-            switch (this.minViewMode) {
-                case 'months':
-                    this.minViewMode = 1;
-                    break;
-                case 'years':
-                    this.minViewMode = 2;
-                    break;
-                default:
-                    this.minViewMode = 0;
-                    break;
-            }
-        }
-        
-        if (typeof(this.viewMode === 'string')) {
-            switch (this.viewMode) {
-                case 'months':
-                    this.viewMode = 1;
-                    break;
-                case 'years':
-                    this.viewMode = 2;
-                    break;
-                default:
-                    this.viewMode = 0;
-                    break;
-            }
-        }
-                
-        this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
-        
-//        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
-        
-        this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-        
-        this.picker().on('mousedown', this.onMousedown, this);
-        this.picker().on('click', this.onClick, this);
-        
-        this.picker().addClass('datepicker-dropdown');
+        this.calpopover.placement = 'right';
+        this.calpopover.setTitle('More');
         
-        this.startViewMode = this.viewMode;
+        this.calpopover.setContent('');
         
-        if(this.singleMode){
-            Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
-                v.setVisibilityMode(Roo.Element.DISPLAY);
-                v.hide();
-            });
-            
-            Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
-                v.setStyle('width', '189px');
-            });
-        }
+        var ctr = this.calpopover.el.select('.popover-content', true).first();
         
-        Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
-            if(!this.calendarWeeks){
-                v.remove();
-                return;
-            }
+        Roo.each(more, function(m){
+            var cfg = {
+                cls : 'fc-event-hori fc-event-draggable',
+                html : m.title
+            };
+            var cg = ctr.createChild(cfg);
             
-            v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
-            v.attr('colspan', function(i, val){
-                return parseInt(val) + 1;
-            });
+            cg.on('click', _this.onEventClick, _this, m);
         });
-                       
-        
-        this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
-        
-        this.setStartDate(this.startDate);
-        this.setEndDate(this.endDate);
         
-        this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+        this.calpopover.show(el);
         
-        this.fillDow();
-        this.fillMonths();
-        this.update();
-        this.showMode();
         
-        if(this.isInline) {
-            this.showPopup();
-        }
     },
     
-    picker : function()
-    {
-        return this.pickerEl;
-//        return this.el.select('.datepicker', true).first();
+    onLoad: function () 
+    {   
+        this.calevents = [];
+        var cal = this;
+        
+        if(this.store.getCount() > 0){
+            this.store.data.each(function(d){
+               cal.addItem({
+                    id : d.data.id,
+                    start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
+                    end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
+                    time : d.data.start_time,
+                    title : d.data.title,
+                    description : d.data.description,
+                    venue : d.data.venue
+                });
+            });
+        }
+        
+        this.renderEvents();
+        
+        if(this.calevents.length && this.loadMask){
+            this.maskEl.hide();
+        }
     },
     
-    fillDow: function()
+    onBeforeLoad: function()
     {
-        var dowCnt = this.weekStart;
+        this.clearEvents();
+        if(this.loadMask){
+            this.maskEl.show();
+        }
+    }
+});
+
+ /*
+ * - LGPL
+ *
+ * element
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.Popover
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Popover class
+ * @cfg {String} html contents of the popover   (or false to use children..)
+ * @cfg {String} title of popover (or false to hide)
+ * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
+ * @cfg {String} trigger click || hover (or false to trigger manually)
+ * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
+ * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
+ *      - if false and it has a 'parent' then it will be automatically added to that element
+ *      - if string - Roo.get  will be called 
+ * @cfg {Number} delay - delay before showing
+ * @constructor
+ * Create a new Popover
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Popover = function(config){
+    Roo.bootstrap.Popover.superclass.constructor.call(this, config);
+    
+    this.addEvents({
+        // raw events
+         /**
+         * @event show
+         * After the popover show
+         * 
+         * @param {Roo.bootstrap.Popover} this
+         */
+        "show" : true,
+        /**
+         * @event hide
+         * After the popover hide
+         * 
+         * @param {Roo.bootstrap.Popover} this
+         */
+        "hide" : true
+    });
+};
+
+Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
+    
+    title: false,
+    html: false,
+    
+    placement : 'right',
+    trigger : 'hover', // hover
+    modal : false,
+    delay : 0,
+    
+    over: false,
+    
+    can_build_overlaid : false,
+    
+    maskEl : false, // the mask element
+    headerEl : false,
+    contentEl : false,
+    alignEl : false, // when show is called with an element - this get's stored.
+    
+    getChildContainer : function()
+    {
+        return this.contentEl;
         
-        var dow = {
-            tag: 'tr',
-            cn: [
-                
-            ]
+    },
+    getPopoverHeader : function()
+    {
+        this.title = true; // flag not to hide it..
+        this.headerEl.addClass('p-0');
+        return this.headerEl
+    },
+    
+    
+    getAutoCreate : function(){
+         
+        var cfg = {
+           cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
+           style: 'display:block',
+           cn : [
+                {
+                    cls : 'arrow'
+                },
+                {
+                    cls : 'popover-inner ',
+                    cn : [
+                        {
+                            tag: 'h3',
+                            cls: 'popover-title popover-header',
+                            html : this.title === false ? '' : this.title
+                        },
+                        {
+                            cls : 'popover-content popover-body '  + (this.cls || ''),
+                            html : this.html || ''
+                        }
+                    ]
+                    
+                }
+           ]
         };
         
-        if(this.calendarWeeks){
-            dow.cn.push({
-                tag: 'th',
-                cls: 'cw',
-                html: '&nbsp;'
-            })
+        return cfg;
+    },
+    /**
+     * @param {string} the title
+     */
+    setTitle: function(str)
+    {
+        this.title = str;
+        if (this.el) {
+            this.headerEl.dom.innerHTML = str;
         }
         
-        while (dowCnt < this.weekStart + 7) {
-            dow.cn.push({
-                tag: 'th',
-                cls: 'dow',
-                html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
-            });
+    },
+    /**
+     * @param {string} the body content
+     */
+    setContent: function(str)
+    {
+        this.html = str;
+        if (this.contentEl) {
+            this.contentEl.dom.innerHTML = str;
         }
         
-        this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
     },
-    
-    fillMonths: function()
-    {    
-        var i = 0;
-        var months = this.picker().select('>.datepicker-months td', true).first();
+    // as it get's added to the bottom of the page.
+    onRender : function(ct, position)
+    {
+        Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
         
-        months.dom.innerHTML = '';
         
-        while (i < 12) {
-            var month = {
-                tag: 'span',
-                cls: 'month',
-                html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
-            };
+        
+        if(!this.el){
+            var cfg = Roo.apply({},  this.getAutoCreate());
+            cfg.id = Roo.id();
             
-            months.createChild(month);
+            if (this.cls) {
+                cfg.cls += ' ' + this.cls;
+            }
+            if (this.style) {
+                cfg.style = this.style;
+            }
+            //Roo.log("adding to ");
+            this.el = Roo.get(document.body).createChild(cfg, position);
+//            Roo.log(this.el);
         }
         
-    },
-    
-    update: function()
-    {
-        this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
+        this.contentEl = this.el.select('.popover-content',true).first();
+        this.headerEl =  this.el.select('.popover-title',true).first();
         
-        if (this.date < this.startDate) {
-            this.viewDate = new Date(this.startDate);
-        } else if (this.date > this.endDate) {
-            this.viewDate = new Date(this.endDate);
-        } else {
-            this.viewDate = new Date(this.date);
+        var nitems = [];
+        if(typeof(this.items) != 'undefined'){
+            var items = this.items;
+            delete this.items;
+
+            for(var i =0;i < items.length;i++) {
+                nitems.push(this.addxtype(Roo.apply({}, items[i])));
+            }
         }
+
+        this.items = nitems;
         
-        this.fill();
-    },
-    
-    fill: function() 
-    {
-        var d = new Date(this.viewDate),
-                year = d.getUTCFullYear(),
-                month = d.getUTCMonth(),
-                startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
-                startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
-                endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
-                endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
-                currentDate = this.date && this.date.valueOf(),
-                today = this.UTCToday();
+        this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
+        Roo.EventManager.onWindowResize(this.resizeMask, this, true);
         
-        this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
         
-//        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
         
-//        this.picker.select('>tfoot th.today').
-//                                             .text(dates[this.language].today)
-//                                             .toggle(this.todayBtn !== false);
+        this.initEvents();
+    },
     
-        this.updateNavArrows();
-        this.fillMonths();
-                                                
-        var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
-        
-        day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
-         
-        prevMonth.setUTCDate(day);
-        
-        prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
-        
-        var nextMonth = new Date(prevMonth);
-        
-        nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
-        
-        nextMonth = nextMonth.valueOf();
-        
-        var fillMonths = false;
-        
-        this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
-        
-        while(prevMonth.valueOf() <= nextMonth) {
-            var clsName = '';
-            
-            if (prevMonth.getUTCDay() === this.weekStart) {
-                if(fillMonths){
-                    this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
-                }
-                    
-                fillMonths = {
-                    tag: 'tr',
-                    cn: []
-                };
-                
-                if(this.calendarWeeks){
-                    // ISO 8601: First week contains first thursday.
-                    // ISO also states week starts on Monday, but we can be more abstract here.
-                    var
-                    // Start of current week: based on weekstart/current date
-                    ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
-                    // Thursday of this week
-                    th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
-                    // First Thursday of year, year from thursday
-                    yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
-                    // Calendar week: ms between thursdays, div ms per day, div 7 days
-                    calWeek =  (th - yth) / 864e5 / 7 + 1;
-                    
-                    fillMonths.cn.push({
-                        tag: 'td',
-                        cls: 'cw',
-                        html: calWeek
-                    });
-                }
-            }
-            
-            if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
-                clsName += ' old';
-            } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
-                clsName += ' new';
-            }
-            if (this.todayHighlight &&
-                prevMonth.getUTCFullYear() == today.getFullYear() &&
-                prevMonth.getUTCMonth() == today.getMonth() &&
-                prevMonth.getUTCDate() == today.getDate()) {
-                clsName += ' today';
-            }
-            
-            if (currentDate && prevMonth.valueOf() === currentDate) {
-                clsName += ' active';
-            }
-            
-            if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
-                    this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
-                    clsName += ' disabled';
-            }
-            
-            fillMonths.cn.push({
-                tag: 'td',
-                cls: 'day ' + clsName,
-                html: prevMonth.getDate()
-            });
-            
-            prevMonth.setDate(prevMonth.getDate()+1);
-        }
-          
-        var currentYear = this.date && this.date.getUTCFullYear();
-        var currentMonth = this.date && this.date.getUTCMonth();
-        
-        this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
-        
-        Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
-            v.removeClass('active');
-            
-            if(currentYear === year && k === currentMonth){
-                v.addClass('active');
-            }
-            
-            if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
-                v.addClass('disabled');
-            }
-            
-        });
-        
-        
-        year = parseInt(year/10, 10) * 10;
-        
-        this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
-        
-        this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
-        
-        year -= 1;
-        for (var i = -1; i < 11; i++) {
-            this.picker().select('>.datepicker-years tbody td',true).first().createChild({
-                tag: 'span',
-                cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
-                html: year
-            });
-            
-            year += 1;
-        }
-    },
-    
-    showMode: function(dir) 
+    resizeMask : function()
     {
-        if (dir) {
-            this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
-        }
-        
-        Roo.each(this.picker().select('>div',true).elements, function(v){
-            v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-            v.hide();
-        });
-        this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
+        this.maskEl.setSize(
+            Roo.lib.Dom.getViewWidth(true),
+            Roo.lib.Dom.getViewHeight(true)
+        );
     },
     
-    place: function()
+    initEvents : function()
     {
-        if(this.isInline) {
-            return;
-        }
-        
-        this.picker().removeClass(['bottom', 'top']);
         
-        if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
-            /*
-             * place to the top of element!
-             *
-             */
-            
-            this.picker().addClass('top');
-            this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
-            
-            return;
+        if (!this.modal) { 
+            Roo.bootstrap.Popover.register(this);
         }
+         
+        this.arrowEl = this.el.select('.arrow',true).first();
+        this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
+        this.el.enableDisplayMode('block');
+        this.el.hide();
         
-        this.picker().addClass('bottom');
-        
-        this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
-    },
-    
-    parseDate : function(value)
-    {
-        if(!value || value instanceof Date){
-            return value;
+        if (this.over === false && !this.parent()) {
+            return; 
         }
-        var v = Date.parseDate(value, this.format);
-        if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
-            v = Date.parseDate(value, 'Y-m-d');
+        if (this.triggers === false) {
+            return;
         }
-        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]);
+         
+        // support parent
+        var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
+        var triggers = this.trigger ? this.trigger.split(' ') : [];
+        Roo.each(triggers, function(trigger) {
+        
+            if (trigger == 'click') {
+                on_el.on('click', this.toggle, this);
+            } else if (trigger != 'manual') {
+                var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
+                var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
+      
+                on_el.on(eventIn  ,this.enter, this);
+                on_el.on(eventOut, this.leave, this);
             }
-        }
-        return v;
+        }, this);
     },
     
-    formatDate : function(date, fmt)
-    {   
-        return (!date || !(date instanceof Date)) ?
-        date : date.dateFormat(fmt || this.format);
-    },
     
-    onFocus : function()
-    {
-        Roo.bootstrap.DateField.superclass.onFocus.call(this);
-        this.showPopup();
-    },
+    // private
+    timeout : null,
+    hoverState : null,
     
-    onBlur : function()
-    {
-        Roo.bootstrap.DateField.superclass.onBlur.call(this);
-        
-        var d = this.inputEl().getValue();
-        
-        this.setValue(d);
-                
-        this.hidePopup();
+    toggle : function () {
+        this.hoverState == 'in' ? this.leave() : this.enter();
     },
     
-    showPopup : function()
-    {
-        this.picker().show();
-        this.update();
-        this.place();
+    enter : function () {
         
-        this.fireEvent('showpopup', this, this.date);
-    },
+        clearTimeout(this.timeout);
     
-    hidePopup : function()
-    {
-        if(this.isInline) {
+        this.hoverState = 'in';
+    
+        if (!this.delay || !this.delay.show) {
+            this.show();
             return;
         }
-        this.picker().hide();
-        this.viewMode = this.startViewMode;
-        this.showMode();
-        
-        this.fireEvent('hidepopup', this, this.date);
-        
+        var _t = this;
+        this.timeout = setTimeout(function () {
+            if (_t.hoverState == 'in') {
+                _t.show();
+            }
+        }, this.delay.show)
     },
     
-    onMousedown: function(e)
-    {
-        e.stopPropagation();
-        e.preventDefault();
-    },
+    leave : function() {
+        clearTimeout(this.timeout);
     
-    keyup: function(e)
-    {
-        Roo.bootstrap.DateField.superclass.keyup.call(this);
-        this.update();
+        this.hoverState = 'out';
+    
+        if (!this.delay || !this.delay.hide) {
+            this.hide();
+            return;
+        }
+        var _t = this;
+        this.timeout = setTimeout(function () {
+            if (_t.hoverState == 'out') {
+                _t.hide();
+            }
+        }, this.delay.hide)
     },
-
-    setValue: function(v)
+    /**
+     * Show the popover
+     * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
+     * @param {string} (left|right|top|bottom) position
+     */
+    show : function (on_el, placement)
     {
-        if(this.fireEvent('beforeselect', this, v) !== false){
-            var d = new Date(this.parseDate(v) ).clearTime();
-        
-            if(isNaN(d.getTime())){
-                this.date = this.viewDate = '';
-                Roo.bootstrap.DateField.superclass.setValue.call(this, '');
-                return;
+        this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
+        on_el = on_el || false; // default to false
+         
+        if (!on_el) {
+            if (this.parent() && (this.over == 'parent' || (this.over === false))) {
+                on_el = this.parent().el;
+            } else if (this.over) {
+                on_el = Roo.get(this.over);
             }
+            
+        }
+        
+        this.alignEl = Roo.get( on_el );
 
-            v = this.formatDate(d);
-
-            Roo.bootstrap.DateField.superclass.setValue.call(this, v);
-
-            this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
-
-            this.update();
+        if (!this.el) {
+            this.render(document.body);
+        }
+        
+        
+         
+        
+        if (this.title === false) {
+            this.headerEl.hide();
+        }
+        
+       
+        this.el.show();
+        this.el.dom.style.display = 'block';
+         
+        if (this.alignEl) {
+            this.updatePosition(this.placement, true);
+             
+        } else {
+            // this is usually just done by the builder = to show the popoup in the middle of the scren.
+            var es = this.el.getSize();
+            var x = Roo.lib.Dom.getViewWidth()/2;
+            var y = Roo.lib.Dom.getViewHeight()/2;
+            this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
+            
+        }
 
-            this.fireEvent('select', this, this.date);
+        
+        //var arrow = this.el.select('.arrow',true).first();
+        //arrow.set(align[2], 
+        
+        this.el.addClass('in');
+        
+         
+        
+        this.hoverState = 'in';
+        
+        if (this.modal) {
+            this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
+            this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
+            this.maskEl.dom.style.display = 'block';
+            this.maskEl.addClass('show');
         }
+        this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
+        this.fireEvent('show', this);
+        
     },
-    
-    getValue: function()
-    {
-        return this.formatDate(this.date);
-    },
-    
-    fireKey: function(e)
+    /**
+     * fire this manually after loading a grid in the table for example
+     * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
+     * @param {Boolean} try and move it if we cant get right position.
+     */
+    updatePosition : function(placement, try_move)
     {
-        if (!this.picker().isVisible()){
-            if (e.keyCode == 27) { // allow escape to hide and re-show picker
-                this.showPopup();
-            }
-            return;
-        }
+        // allow for calling with no parameters
+        placement = placement   ? placement :  this.placement;
+        try_move = typeof(try_move) == 'undefined' ? true : try_move;
         
-        var dateChanged = false,
-        dir, day, month,
-        newDate, newViewDate;
+        this.el.removeClass([
+            'fade','top','bottom', 'left', 'right','in',
+            'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
+        ]);
+        this.el.addClass(placement + ' bs-popover-' + placement);
         
-        switch(e.keyCode){
-            case 27: // escape
-                this.hidePopup();
-                e.preventDefault();
-                break;
-            case 37: // left
-            case 39: // right
-                if (!this.keyboardNavigation) {
-                    break;
+        if (!this.alignEl ) {
+            return false;
+        }
+        
+        switch (placement) {
+            case 'right':
+                var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
+                var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
+                if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
+                    //normal display... or moved up/down.
+                    this.el.setXY(offset);
+                    var xy = this.alignEl.getAnchorXY('tr', false);
+                    xy[0]+=2;xy[1]+=5;
+                    this.arrowEl.setXY(xy);
+                    return true;
                 }
-                dir = e.keyCode == 37 ? -1 : 1;
+                // continue through...
+                return this.updatePosition('left', false);
                 
-                if (e.ctrlKey){
-                    newDate = this.moveYear(this.date, dir);
-                    newViewDate = this.moveYear(this.viewDate, dir);
-                } else if (e.shiftKey){
-                    newDate = this.moveMonth(this.date, dir);
-                    newViewDate = this.moveMonth(this.viewDate, dir);
-                } else {
-                    newDate = new Date(this.date);
-                    newDate.setUTCDate(this.date.getUTCDate() + dir);
-                    newViewDate = new Date(this.viewDate);
-                    newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
-                }
-                if (this.dateWithinRange(newDate)){
-                    this.date = newDate;
-                    this.viewDate = newViewDate;
-                    this.setValue(this.formatDate(this.date));
-//                    this.update();
-                    e.preventDefault();
-                    dateChanged = true;
-                }
-                break;
-            case 38: // up
-            case 40: // down
-                if (!this.keyboardNavigation) {
-                    break;
+            
+            case 'left':
+                var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
+                var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
+                if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
+                    //normal display... or moved up/down.
+                    this.el.setXY(offset);
+                    var xy = this.alignEl.getAnchorXY('tl', false);
+                    xy[0]-=10;xy[1]+=5; // << fix me
+                    this.arrowEl.setXY(xy);
+                    return true;
                 }
-                dir = e.keyCode == 38 ? -1 : 1;
-                if (e.ctrlKey){
-                    newDate = this.moveYear(this.date, dir);
-                    newViewDate = this.moveYear(this.viewDate, dir);
-                } else if (e.shiftKey){
-                    newDate = this.moveMonth(this.date, dir);
-                    newViewDate = this.moveMonth(this.viewDate, dir);
-                } else {
-                    newDate = new Date(this.date);
-                    newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
-                    newViewDate = new Date(this.viewDate);
-                    newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+                // call self...
+                return this.updatePosition('right', false);
+            
+            case 'top':
+                var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
+                var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
+                if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
+                    //normal display... or moved up/down.
+                    this.el.setXY(offset);
+                    var xy = this.alignEl.getAnchorXY('t', false);
+                    xy[1]-=10; // << fix me
+                    this.arrowEl.setXY(xy);
+                    return true;
                 }
-                if (this.dateWithinRange(newDate)){
-                    this.date = newDate;
-                    this.viewDate = newViewDate;
-                    this.setValue(this.formatDate(this.date));
-//                    this.update();
-                    e.preventDefault();
-                    dateChanged = true;
+                // fall through
+               return this.updatePosition('bottom', false);
+            
+            case 'bottom':
+                 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
+                var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
+                if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
+                    //normal display... or moved up/down.
+                    this.el.setXY(offset);
+                    var xy = this.alignEl.getAnchorXY('b', false);
+                     xy[1]+=2; // << fix me
+                    this.arrowEl.setXY(xy);
+                    return true;
                 }
-                break;
-            case 13: // enter
-                this.setValue(this.formatDate(this.date));
-                this.hidePopup();
-                e.preventDefault();
-                break;
-            case 9: // tab
-                this.setValue(this.formatDate(this.date));
-                this.hidePopup();
-                break;
-            case 16: // shift
-            case 17: // ctrl
-            case 18: // alt
-                break;
-            default :
-                this.hidePopup();
+                // fall through
+                return this.updatePosition('top', false);
                 
-        }
-    },
-    
-    
-    onClick: function(e) 
-    {
-        e.stopPropagation();
-        e.preventDefault();
-        
-        var target = e.getTarget();
-        
-        if(target.nodeName.toLowerCase() === 'i'){
-            target = Roo.get(target).dom.parentNode;
+            
         }
         
-        var nodeName = target.nodeName;
-        var className = target.className;
-        var html = target.innerHTML;
-        //Roo.log(nodeName);
         
-        switch(nodeName.toLowerCase()) {
-            case 'th':
-                switch(className) {
-                    case 'switch':
-                        this.showMode(1);
-                        break;
-                    case 'prev':
-                    case 'next':
-                        var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
-                        switch(this.viewMode){
-                                case 0:
-                                        this.viewDate = this.moveMonth(this.viewDate, dir);
-                                        break;
-                                case 1:
-                                case 2:
-                                        this.viewDate = this.moveYear(this.viewDate, dir);
-                                        break;
-                        }
-                        this.fill();
-                        break;
-                    case 'today':
-                        var date = new Date();
-                        this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
-//                        this.fill()
-                        this.setValue(this.formatDate(this.date));
-                        
-                        this.hidePopup();
-                        break;
-                }
-                break;
-            case 'span':
-                if (className.indexOf('disabled') < 0) {
-                if (!this.viewDate) {
-                    this.viewDate = new Date();
-                }
-                this.viewDate.setUTCDate(1);
-                    if (className.indexOf('month') > -1) {
-                        this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
-                    } else {
-                        var year = parseInt(html, 10) || 0;
-                        this.viewDate.setUTCFullYear(year);
-                        
-                    }
-                    
-                    if(this.singleMode){
-                        this.setValue(this.formatDate(this.viewDate));
-                        this.hidePopup();
-                        return;
-                    }
-                    
-                    this.showMode(-1);
-                    this.fill();
-                }
-                break;
-                
-            case 'td':
-                //Roo.log(className);
-                if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
-                    var day = parseInt(html, 10) || 1;
-                    var year =  (this.viewDate || new Date()).getUTCFullYear(),
-                        month = (this.viewDate || new Date()).getUTCMonth();
-
-                    if (className.indexOf('old') > -1) {
-                        if(month === 0 ){
-                            month = 11;
-                            year -= 1;
-                        }else{
-                            month -= 1;
-                        }
-                    } else if (className.indexOf('new') > -1) {
-                        if (month == 11) {
-                            month = 0;
-                            year += 1;
-                        } else {
-                            month += 1;
-                        }
-                    }
-                    //Roo.log([year,month,day]);
-                    this.date = this.UTCDate(year, month, day,0,0,0,0);
-                    this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
-//                    this.fill();
-                    //Roo.log(this.formatDate(this.date));
-                    this.setValue(this.formatDate(this.date));
-                    this.hidePopup();
-                }
-                break;
-        }
+        return false;
     },
     
-    setStartDate: function(startDate)
+    hide : function()
     {
-        this.startDate = startDate || -Infinity;
-        if (this.startDate !== -Infinity) {
-            this.startDate = this.parseDate(this.startDate);
-        }
-        this.update();
-        this.updateNavArrows();
+        this.el.setXY([0,0]);
+        this.el.removeClass('in');
+        this.el.hide();
+        this.hoverState = null;
+        this.maskEl.hide(); // always..
+        this.fireEvent('hide', this);
+    }
+    
+});
+
+
+Roo.apply(Roo.bootstrap.Popover, {
+
+    alignment : {
+        'left' : ['r-l', [-10,0], 'left bs-popover-left'],
+        'right' : ['l-br', [10,0], 'right bs-popover-right'],
+        'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
+        'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
     },
+    
+    zIndex : 20001,
 
-    setEndDate: function(endDate)
+    clickHander : false,
+    
+    
+
+    onMouseDown : function(e)
     {
-        this.endDate = endDate || Infinity;
-        if (this.endDate !== Infinity) {
-            this.endDate = this.parseDate(this.endDate);
+        if (this.popups.length &&  !e.getTarget(".roo-popover")) {
+            /// what is nothing is showing..
+            this.hideAll();
         }
-        this.update();
-        this.updateNavArrows();
+         
     },
     
-    setDaysOfWeekDisabled: function(daysOfWeekDisabled)
+    
+    popups : [],
+    
+    register : function(popup)
     {
-        this.daysOfWeekDisabled = daysOfWeekDisabled || [];
-        if (typeof(this.daysOfWeekDisabled) !== 'object') {
-            this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
+        if (!Roo.bootstrap.Popover.clickHandler) {
+            Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
         }
-        this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
-            return parseInt(d, 10);
-        });
-        this.update();
-        this.updateNavArrows();
+        // hide other popups.
+        popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
+        popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
+        this.hideAll(); //<< why?
+        //this.popups.push(popup);
     },
-    
-    updateNavArrows: function() 
+    hideAll : function()
     {
-        if(this.singleMode){
-            return;
-        }
-        
-        var d = new Date(this.viewDate),
-        year = d.getUTCFullYear(),
-        month = d.getUTCMonth();
-        
-        Roo.each(this.picker().select('.prev', true).elements, function(v){
-            v.show();
-            switch (this.viewMode) {
-                case 0:
-
-                    if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
-                        v.hide();
-                    }
-                    break;
-                case 1:
-                case 2:
-                    if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
-                        v.hide();
-                    }
-                    break;
-            }
+        this.popups.forEach(function(p) {
+            p.hide();
         });
-        
-        Roo.each(this.picker().select('.next', true).elements, function(v){
-            v.show();
-            switch (this.viewMode) {
-                case 0:
-
-                    if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
-                        v.hide();
-                    }
-                    break;
-                case 1:
-                case 2:
-                    if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
-                        v.hide();
-                    }
-                    break;
-            }
-        })
     },
+    onShow : function() {
+        Roo.bootstrap.Popover.popups.push(this);
+    },
+    onHide : function() {
+        Roo.bootstrap.Popover.popups.remove(this);
+    } 
+
+});/*
+ * - LGPL
+ *
+ * Card header - holder for the card header elements.
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.PopoverNav
+ * @extends Roo.bootstrap.NavGroup
+ * Bootstrap Popover header navigation class
+ * @constructor
+ * Create a new Popover Header Navigation 
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.PopoverNav = function(config){
+    Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
     
-    moveMonth: function(date, dir)
-    {
-        if (!dir) {
-            return date;
-        }
-        var new_date = new Date(date.valueOf()),
-        day = new_date.getUTCDate(),
-        month = new_date.getUTCMonth(),
-        mag = Math.abs(dir),
-        new_month, test;
-        dir = dir > 0 ? 1 : -1;
-        if (mag == 1){
-            test = dir == -1
-            // If going back one month, make sure month is not current month
-            // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
-            ? function(){
-                return new_date.getUTCMonth() == month;
-            }
-            // If going forward one month, make sure month is as expected
-            // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
-            : function(){
-                return new_date.getUTCMonth() != new_month;
-            };
-            new_month = month + dir;
-            new_date.setUTCMonth(new_month);
-            // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
-            if (new_month < 0 || new_month > 11) {
-                new_month = (new_month + 12) % 12;
-            }
-        } else {
-            // For magnitudes >1, move one month at a time...
-            for (var i=0; i<mag; i++) {
-                // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
-                new_date = this.moveMonth(new_date, dir);
-            }
-            // ...then reset the day, keeping it in the new month
-            new_month = new_date.getUTCMonth();
-            new_date.setUTCDate(day);
-            test = function(){
-                return new_month != new_date.getUTCMonth();
-            };
+    
+    container_method : 'getPopoverHeader' 
+    
+     
+    
+    
+   
+});
+
+
+ /*
+ * - LGPL
+ *
+ * Progress
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.Progress
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Progress class
+ * @cfg {Boolean} striped striped of the progress bar
+ * @cfg {Boolean} active animated of the progress bar
+ * 
+ * 
+ * @constructor
+ * Create a new Progress
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Progress = function(config){
+    Roo.bootstrap.Progress.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
+    
+    striped : false,
+    active: false,
+    
+    getAutoCreate : function(){
+        var cfg = {
+            tag: 'div',
+            cls: 'progress'
+        };
+        
+        
+        if(this.striped){
+            cfg.cls += ' progress-striped';
         }
-        // Common date-resetting loop -- if date is beyond end of month, make it
-        // end of month
-        while (test()){
-            new_date.setUTCDate(--day);
-            new_date.setUTCMonth(new_month);
+      
+        if(this.active){
+            cfg.cls += ' active';
         }
-        return new_date;
-    },
+        
+        
+        return cfg;
+    }
+   
+});
 
-    moveYear: function(date, dir)
-    {
-        return this.moveMonth(date, dir*12);
-    },
 
-    dateWithinRange: function(date)
-    {
-        return date >= this.startDate && date <= this.endDate;
-    },
+ /*
+ * - LGPL
+ *
+ * ProgressBar
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.ProgressBar
+ * @extends Roo.bootstrap.Component
+ * Bootstrap ProgressBar class
+ * @cfg {Number} aria_valuenow aria-value now
+ * @cfg {Number} aria_valuemin aria-value min
+ * @cfg {Number} aria_valuemax aria-value max
+ * @cfg {String} label label for the progress bar
+ * @cfg {String} panel (success | info | warning | danger )
+ * @cfg {String} role role of the progress bar
+ * @cfg {String} sr_only text
+ * 
+ * 
+ * @constructor
+ * Create a new ProgressBar
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.ProgressBar = function(config){
+    Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
+};
 
+Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
     
-    remove: function() 
-    {
-        this.picker().remove();
-    },
+    aria_valuenow : 0,
+    aria_valuemin : 0,
+    aria_valuemax : 100,
+    label : false,
+    panel : false,
+    role : false,
+    sr_only: false,
     
-    validateValue : function(value)
+    getAutoCreate : function()
     {
-        if(this.getVisibilityEl().hasClass('hidden')){
-            return true;
-        }
         
-        if(value.length < 1)  {
-            if(this.allowBlank){
-                return true;
-            }
-            return false;
-        }
+        var cfg = {
+            tag: 'div',
+            cls: 'progress-bar',
+            style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
+        };
         
-        if(value.length < this.minLength){
-            return false;
-        }
-        if(value.length > this.maxLength){
-            return false;
-        }
-        if(this.vtype){
-            var vt = Roo.form.VTypes;
-            if(!vt[this.vtype](value, this)){
-                return false;
+        if(this.sr_only){
+            cfg.cn = {
+                tag: 'span',
+                cls: 'sr-only',
+                html: this.sr_only
             }
         }
-        if(typeof this.validator == "function"){
-            var msg = this.validator(value);
-            if(msg !== true){
-                return false;
-            }
+        
+        if(this.role){
+            cfg.role = this.role;
         }
         
-        if(this.regex && !this.regex.test(value)){
-            return false;
+        if(this.aria_valuenow){
+            cfg['aria-valuenow'] = this.aria_valuenow;
         }
         
-        if(typeof(this.parseDate(value)) == 'undefined'){
-            return false;
+        if(this.aria_valuemin){
+            cfg['aria-valuemin'] = this.aria_valuemin;
         }
         
-        if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
-            return false;
-        }      
+        if(this.aria_valuemax){
+            cfg['aria-valuemax'] = this.aria_valuemax;
+        }
         
-        if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
-            return false;
-        } 
+        if(this.label && !this.sr_only){
+            cfg.html = this.label;
+        }
         
+        if(this.panel){
+            cfg.cls += ' progress-bar-' + this.panel;
+        }
         
-        return true;
+        return cfg;
     },
     
-    reset : function()
+    update : function(aria_valuenow)
     {
-        this.date = this.viewDate = '';
+        this.aria_valuenow = aria_valuenow;
         
-        Roo.bootstrap.DateField.superclass.setValue.call(this, '');
+        this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
     }
    
 });
 
-Roo.apply(Roo.bootstrap.DateField,  {
-    
-    head : {
-        tag: 'thead',
-        cn: [
-        {
-            tag: 'tr',
-            cn: [
-            {
-                tag: 'th',
-                cls: 'prev',
-                html: '<i class="fa fa-arrow-left"/>'
-            },
-            {
-                tag: 'th',
-                cls: 'switch',
-                colspan: '5'
-            },
-            {
-                tag: 'th',
-                cls: 'next',
-                html: '<i class="fa fa-arrow-right"/>'
-            }
-
-            ]
-        }
-        ]
-    },
-    
-    content : {
-        tag: 'tbody',
-        cn: [
-        {
-            tag: 'tr',
-            cn: [
-            {
-                tag: 'td',
-                colspan: '7'
-            }
-            ]
-        }
-        ]
-    },
-    
-    footer : {
-        tag: 'tfoot',
-        cn: [
-        {
-            tag: 'tr',
-            cn: [
-            {
-                tag: 'th',
-                colspan: '7',
-                cls: 'today'
-            }
-                    
-            ]
-        }
-        ]
-    },
-    
-    dates:{
-        en: {
-            days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
-            daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
-            daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
-            months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
-            monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
-            today: "Today"
-        }
-    },
-    
-    modes: [
-    {
-        clsName: 'days',
-        navFnc: 'Month',
-        navStep: 1
-    },
-    {
-        clsName: 'months',
-        navFnc: 'FullYear',
-        navStep: 1
-    },
-    {
-        clsName: 'years',
-        navFnc: 'FullYear',
-        navStep: 10
-    }]
-});
-
-Roo.apply(Roo.bootstrap.DateField,  {
-  
-    template : {
-        tag: 'div',
-        cls: 'datepicker dropdown-menu roo-dynamic shadow',
-        cn: [
-        {
-            tag: 'div',
-            cls: 'datepicker-days',
-            cn: [
-            {
-                tag: 'table',
-                cls: 'table-condensed',
-                cn:[
-                Roo.bootstrap.DateField.head,
-                {
-                    tag: 'tbody'
-                },
-                Roo.bootstrap.DateField.footer
-                ]
-            }
-            ]
-        },
-        {
-            tag: 'div',
-            cls: 'datepicker-months',
-            cn: [
-            {
-                tag: 'table',
-                cls: 'table-condensed',
-                cn:[
-                Roo.bootstrap.DateField.head,
-                Roo.bootstrap.DateField.content,
-                Roo.bootstrap.DateField.footer
-                ]
-            }
-            ]
-        },
-        {
-            tag: 'div',
-            cls: 'datepicker-years',
-            cn: [
-            {
-                tag: 'table',
-                cls: 'table-condensed',
-                cn:[
-                Roo.bootstrap.DateField.head,
-                Roo.bootstrap.DateField.content,
-                Roo.bootstrap.DateField.footer
-                ]
-            }
-            ]
-        }
-        ]
-    }
-});
-
 
  /*
  * - LGPL
  *
- * TimeField
+ * column
  * 
  */
 
 /**
- * @class Roo.bootstrap.TimeField
- * @extends Roo.bootstrap.Input
- * Bootstrap DateField class
- * 
+ * @class Roo.bootstrap.TabGroup
+ * @extends Roo.bootstrap.Column
+ * Bootstrap Column class
+ * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
+ * @cfg {Boolean} carousel true to make the group behave like a carousel
+ * @cfg {Boolean} bullets show bullets for the panels
+ * @cfg {Boolean} autoslide (true|false) auto slide .. default false
+ * @cfg {Number} timer auto slide timer .. default 0 millisecond
+ * @cfg {Boolean} showarrow (true|false) show arrow default true
  * 
  * @constructor
- * Create a new TimeField
+ * Create a new TabGroup
  * @param {Object} config The config object
  */
 
-Roo.bootstrap.TimeField = function(config){
-    Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
-    this.addEvents({
-            /**
-             * @event show
-             * Fires when this field show.
-             * @param {Roo.bootstrap.DateField} thisthis
-             * @param {Mixed} date The date value
-             */
-            show : true,
-            /**
-             * @event show
-             * Fires when this field hide.
-             * @param {Roo.bootstrap.DateField} this
-             * @param {Mixed} date The date value
-             */
-            hide : true,
-            /**
-             * @event select
-             * Fires when select a date.
-             * @param {Roo.bootstrap.DateField} this
-             * @param {Mixed} date The date value
-             */
-            select : true
-        });
+Roo.bootstrap.TabGroup = function(config){
+    Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
+    if (!this.navId) {
+        this.navId = Roo.id();
+    }
+    this.tabs = [];
+    Roo.bootstrap.TabGroup.register(this);
+    
 };
 
-Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
+Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
+    
+    carousel : false,
+    transition : false,
+    bullets : 0,
+    timer : 0,
+    autoslide : false,
+    slideFn : false,
+    slideOnTouch : false,
+    showarrow : true,
     
-    /**
-     * @cfg {String} format
-     * The default time format string which can be overriden for localization support.  The format must be
-     * valid according to {@link Date#parseDate} (defaults to 'H:i').
-     */
-    format : "H:i",
-
     getAutoCreate : function()
     {
-        this.after = '<i class="fa far fa-clock"></i>';
-        return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
-        
-         
-    },
-    onRender: function(ct, position)
-    {
-        
-        Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
-                
-        this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
+        var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
         
-        this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        cfg.cls += ' tab-content';
         
-        this.pop = this.picker().select('>.datepicker-time',true).first();
-        this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        if (this.carousel) {
+            cfg.cls += ' carousel slide';
+            
+            cfg.cn = [{
+               cls : 'carousel-inner',
+               cn : []
+            }];
         
-        this.picker().on('mousedown', this.onMousedown, this);
-        this.picker().on('click', this.onClick, this);
+            if(this.bullets  && !Roo.isTouch){
+                
+                var bullets = {
+                    cls : 'carousel-bullets',
+                    cn : []
+                };
+               
+                if(this.bullets_cls){
+                    bullets.cls = bullets.cls + ' ' + this.bullets_cls;
+                }
+                
+                bullets.cn.push({
+                    cls : 'clear'
+                });
+                
+                cfg.cn[0].cn.push(bullets);
+            }
+            
+            if(this.showarrow){
+                cfg.cn[0].cn.push({
+                    tag : 'div',
+                    class : 'carousel-arrow',
+                    cn : [
+                        {
+                            tag : 'div',
+                            class : 'carousel-prev',
+                            cn : [
+                                {
+                                    tag : 'i',
+                                    class : 'fa fa-chevron-left'
+                                }
+                            ]
+                        },
+                        {
+                            tag : 'div',
+                            class : 'carousel-next',
+                            cn : [
+                                {
+                                    tag : 'i',
+                                    class : 'fa fa-chevron-right'
+                                }
+                            ]
+                        }
+                    ]
+                });
+            }
+            
+        }
         
-        this.picker().addClass('datepicker-dropdown');
+        return cfg;
+    },
     
-        this.fillTime();
-        this.update();
-            
-        this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
-        this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
-        this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
-        this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
-        this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
-        this.pop.select('button.ok', true).first().on('click', this.setTime, this);
-
-    },
-    
-    fireKey: function(e){
-        if (!this.picker().isVisible()){
-            if (e.keyCode == 27) { // allow escape to hide and re-show picker
-                this.show();
-            }
-            return;
+    initEvents:  function()
+    {
+//        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
+//            this.el.on("touchstart", this.onTouchStart, this);
+//        }
+        
+        if(this.autoslide){
+            var _this = this;
+            
+            this.slideFn = window.setInterval(function() {
+                _this.showPanelNext();
+            }, this.timer);
         }
-
-        e.preventDefault();
         
-        switch(e.keyCode){
-            case 27: // escape
-                this.hide();
-                break;
-            case 37: // left
-            case 39: // right
-                this.onTogglePeriod();
-                break;
-            case 38: // up
-                this.onIncrementMinutes();
-                break;
-            case 40: // down
-                this.onDecrementMinutes();
-                break;
-            case 13: // enter
-            case 9: // tab
-                this.setTime();
-                break;
+        if(this.showarrow){
+            this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
+            this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
         }
+        
+        
     },
     
-    onClick: function(e) {
-        e.stopPropagation();
-        e.preventDefault();
+//    onTouchStart : function(e, el, o)
+//    {
+//        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
+//            return;
+//        }
+//        
+//        this.showPanelNext();
+//    },
+    
+    
+    getChildContainer : function()
+    {
+        return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
     },
     
-    picker : function()
+    /**
+    * register a Navigation item
+    * @param {Roo.bootstrap.NavItem} the navitem to add
+    */
+    register : function(item)
     {
-        return this.pickerEl;
+        this.tabs.push( item);
+        item.navId = this.navId; // not really needed..
+        this.addBullet();
+    
     },
     
-    fillTime: function()
-    {    
-        var time = this.pop.select('tbody', true).first();
-        
-        time.dom.innerHTML = '';
-        
-        time.createChild({
-            tag: 'tr',
-            cn: [
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'a',
-                            href: '#',
-                            cls: 'btn',
-                            cn: [
-                                {
-                                    tag: 'i',
-                                    cls: 'hours-up fa fas fa-chevron-up'
-                                }
-                            ]
-                        } 
-                    ]
-                },
-                {
-                    tag: 'td',
-                    cls: 'separator'
-                },
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'a',
-                            href: '#',
-                            cls: 'btn',
-                            cn: [
-                                {
-                                    tag: 'i',
-                                    cls: 'minutes-up fa fas fa-chevron-up'
-                                }
-                            ]
-                        }
-                    ]
-                },
-                {
-                    tag: 'td',
-                    cls: 'separator'
-                }
-            ]
-        });
-        
-        time.createChild({
-            tag: 'tr',
-            cn: [
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'span',
-                            cls: 'timepicker-hour',
-                            html: '00'
-                        }  
-                    ]
-                },
-                {
-                    tag: 'td',
-                    cls: 'separator',
-                    html: ':'
-                },
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'span',
-                            cls: 'timepicker-minute',
-                            html: '00'
-                        }  
-                    ]
-                },
-                {
-                    tag: 'td',
-                    cls: 'separator'
-                },
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'button',
-                            type: 'button',
-                            cls: 'btn btn-primary period',
-                            html: 'AM'
-                            
-                        }
-                    ]
-                }
-            ]
-        });
-        
-        time.createChild({
-            tag: 'tr',
-            cn: [
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'a',
-                            href: '#',
-                            cls: 'btn',
-                            cn: [
-                                {
-                                    tag: 'span',
-                                    cls: 'hours-down fa fas fa-chevron-down'
-                                }
-                            ]
-                        }
-                    ]
-                },
-                {
-                    tag: 'td',
-                    cls: 'separator'
-                },
-                {
-                    tag: 'td',
-                    cn: [
-                        {
-                            tag: 'a',
-                            href: '#',
-                            cls: 'btn',
-                            cn: [
-                                {
-                                    tag: 'span',
-                                    cls: 'minutes-down fa fas fa-chevron-down'
-                                }
-                            ]
-                        }
-                    ]
-                },
-                {
-                    tag: 'td',
-                    cls: 'separator'
-                }
-            ]
+    getActivePanel : function()
+    {
+        var r = false;
+        Roo.each(this.tabs, function(t) {
+            if (t.active) {
+                r = t;
+                return false;
+            }
+            return null;
         });
+        return r;
         
     },
-    
-    update: function()
+    getPanelByName : function(n)
     {
-        
-        this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
-        
-        this.fill();
+        var r = false;
+        Roo.each(this.tabs, function(t) {
+            if (t.tabId == n) {
+                r = t;
+                return false;
+            }
+            return null;
+        });
+        return r;
     },
-    
-    fill: function() 
+    indexOfPanel : function(p)
     {
-        var hours = this.time.getHours();
-        var minutes = this.time.getMinutes();
-        var period = 'AM';
-        
-        if(hours > 11){
-            period = 'PM';
-        }
-        
-        if(hours == 0){
-            hours = 12;
-        }
-        
-        
-        if(hours > 12){
-            hours = hours - 12;
+        var r = false;
+        Roo.each(this.tabs, function(t,i) {
+            if (t.tabId == p.tabId) {
+                r = i;
+                return false;
+            }
+            return null;
+        });
+        return r;
+    },
+    /**
+     * show a specific panel
+     * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
+     * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
+     */
+    showPanel : function (pan)
+    {
+        if(this.transition || typeof(pan) == 'undefined'){
+            Roo.log("waiting for the transitionend");
+            return false;
         }
         
-        if(hours < 10){
-            hours = '0' + hours;
+        if (typeof(pan) == 'number') {
+            pan = this.tabs[pan];
         }
         
-        if(minutes < 10){
-            minutes = '0' + minutes;
+        if (typeof(pan) == 'string') {
+            pan = this.getPanelByName(pan);
         }
         
-        this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
-        this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
-        this.pop.select('button', true).first().dom.innerHTML = period;
+        var cur = this.getActivePanel();
         
-    },
-    
-    place: function()
-    {   
-        this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
+        if(!pan || !cur){
+            Roo.log('pan or acitve pan is undefined');
+            return false;
+        }
         
-        var cls = ['bottom'];
+        if (pan.tabId == this.getActivePanel().tabId) {
+            return true;
+        }
         
-        if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
-            cls.pop();
-            cls.push('top');
+        if (false === cur.fireEvent('beforedeactivate')) {
+            return false;
         }
         
-        cls.push('right');
+        if(this.bullets > 0 && !Roo.isTouch){
+            this.setActiveBullet(this.indexOfPanel(pan));
+        }
         
-        if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
-            cls.pop();
-            cls.push('left');
+        if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
+            
+            //class="carousel-item carousel-item-next carousel-item-left"
+            
+            this.transition = true;
+            var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
+            var lr = dir == 'next' ? 'left' : 'right';
+            pan.el.addClass(dir); // or prev
+            pan.el.addClass('carousel-item-' + dir); // or prev
+            pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
+            cur.el.addClass(lr); // or right
+            pan.el.addClass(lr);
+            cur.el.addClass('carousel-item-' +lr); // or right
+            pan.el.addClass('carousel-item-' +lr);
+            
+            
+            var _this = this;
+            cur.el.on('transitionend', function() {
+                Roo.log("trans end?");
+                
+                pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
+                pan.setActive(true);
+                
+                cur.el.removeClass([lr, 'carousel-item-' + lr]);
+                cur.setActive(false);
+                
+                _this.transition = false;
+                
+            }, this, { single:  true } );
+            
+            return true;
         }
-        //this.picker().setXY(20000,20000);
-        this.picker().addClass(cls.join('-'));
         
-        var _this = this;
+        cur.setActive(false);
+        pan.setActive(true);
         
-        Roo.each(cls, function(c){
-            if(c == 'bottom'){
-                (function() {
-                 //  
-                }).defer(200);
-                 _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
-                //_this.picker().setTop(_this.inputEl().getHeight());
-                return;
-            }
-            if(c == 'top'){
-                 _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
-                
-                //_this.picker().setTop(0 - _this.picker().getHeight());
-                return;
-            }
-            /*
-            if(c == 'left'){
-                _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
-                return;
-            }
-            if(c == 'right'){
-                _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
-                return;
-            }
-            */
-        });
+        return true;
         
     },
-  
-    onFocus : function()
-    {
-        Roo.bootstrap.TimeField.superclass.onFocus.call(this);
-        this.show();
-    },
-    
-    onBlur : function()
-    {
-        Roo.bootstrap.TimeField.superclass.onBlur.call(this);
-        this.hide();
-    },
-    
-    show : function()
+    showPanelNext : function()
     {
-        this.picker().show();
-        this.pop.show();
-        this.update();
-        this.place();
+        var i = this.indexOfPanel(this.getActivePanel());
         
-        this.fireEvent('show', this, this.date);
-    },
-    
-    hide : function()
-    {
-        this.picker().hide();
-        this.pop.hide();
+        if (i >= this.tabs.length - 1 && !this.autoslide) {
+            return;
+        }
         
-        this.fireEvent('hide', this, this.date);
+        if (i >= this.tabs.length - 1 && this.autoslide) {
+            i = -1;
+        }
+        
+        this.showPanel(this.tabs[i+1]);
     },
     
-    setTime : function()
+    showPanelPrev : function()
     {
-        this.hide();
-        this.setValue(this.time.format(this.format));
+        var i = this.indexOfPanel(this.getActivePanel());
         
-        this.fireEvent('select', this, this.date);
+        if (i  < 1 && !this.autoslide) {
+            return;
+        }
         
+        if (i < 1 && this.autoslide) {
+            i = this.tabs.length;
+        }
         
+        this.showPanel(this.tabs[i-1]);
     },
     
-    onMousedown: function(e){
-        e.stopPropagation();
-        e.preventDefault();
-    },
     
-    onIncrementHours: function()
+    addBullet: function()
     {
-        Roo.log('onIncrementHours');
-        this.time = this.time.add(Date.HOUR, 1);
-        this.update();
+        if(!this.bullets || Roo.isTouch){
+            return;
+        }
+        var ctr = this.el.select('.carousel-bullets',true).first();
+        var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
+        var bullet = ctr.createChild({
+            cls : 'bullet bullet-' + i
+        },ctr.dom.lastChild);
+        
+        
+        var _this = this;
+        
+        bullet.on('click', (function(e, el, o, ii, t){
+
+            e.preventDefault();
+
+            this.showPanel(ii);
+
+            if(this.autoslide && this.slideFn){
+                clearInterval(this.slideFn);
+                this.slideFn = window.setInterval(function() {
+                    _this.showPanelNext();
+                }, this.timer);
+            }
+
+        }).createDelegate(this, [i, bullet], true));
+                
         
     },
-    
-    onDecrementHours: function()
+     
+    setActiveBullet : function(i)
     {
-        Roo.log('onDecrementHours');
-        this.time = this.time.add(Date.HOUR, -1);
-        this.update();
-    },
+        if(Roo.isTouch){
+            return;
+        }
+        
+        Roo.each(this.el.select('.bullet', true).elements, function(el){
+            el.removeClass('selected');
+        });
+
+        var bullet = this.el.select('.bullet-' + i, true).first();
+        
+        if(!bullet){
+            return;
+        }
+        
+        bullet.addClass('selected');
+    }
     
-    onIncrementMinutes: function()
-    {
-        Roo.log('onIncrementMinutes');
-        this.time = this.time.add(Date.MINUTE, 1);
-        this.update();
-    },
     
-    onDecrementMinutes: function()
-    {
-        Roo.log('onDecrementMinutes');
-        this.time = this.time.add(Date.MINUTE, -1);
-        this.update();
-    },
+  
+});
+
+
+Roo.apply(Roo.bootstrap.TabGroup, {
     
-    onTogglePeriod: function()
+    groups: {},
+     /**
+    * register a Navigation Group
+    * @param {Roo.bootstrap.NavGroup} the navgroup to add
+    */
+    register : function(navgrp)
     {
-        Roo.log('onTogglePeriod');
-        this.time = this.time.add(Date.HOUR, 12);
-        this.update();
+        this.groups[navgrp.navId] = navgrp;
+       
+    },
+    /**
+    * fetch a Navigation Group based on the navigation ID
+    * if one does not exist , it will get created.
+    * @param {string} the navgroup to add
+    * @returns {Roo.bootstrap.NavGroup} the navgroup 
+    */
+    get: function(navId) {
+        if (typeof(this.groups[navId]) == 'undefined') {
+            this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
+        }
+        return this.groups[navId] ;
     }
     
-   
-});
-
-Roo.apply(Roo.bootstrap.TimeField,  {
-  
-    template : {
-        tag: 'div',
-        cls: 'datepicker dropdown-menu',
-        cn: [
-            {
-                tag: 'div',
-                cls: 'datepicker-time',
-                cn: [
-                {
-                    tag: 'table',
-                    cls: 'table-condensed',
-                    cn:[
-                        {
-                            tag: 'tbody',
-                            cn: [
-                                {
-                                    tag: 'tr',
-                                    cn: [
-                                    {
-                                        tag: 'td',
-                                        colspan: '7'
-                                    }
-                                    ]
-                                }
-                            ]
-                        },
-                        {
-                            tag: 'tfoot',
-                            cn: [
-                                {
-                                    tag: 'tr',
-                                    cn: [
-                                    {
-                                        tag: 'th',
-                                        colspan: '7',
-                                        cls: '',
-                                        cn: [
-                                            {
-                                                tag: 'button',
-                                                cls: 'btn btn-info ok',
-                                                html: 'OK'
-                                            }
-                                        ]
-                                    }
-                    
-                                    ]
-                                }
-                            ]
-                        }
-                    ]
-                }
-                ]
-            }
-        ]
-    }
+    
+    
 });
 
-
  /*
  * - LGPL
  *
- * MonthField
+ * TabPanel
  * 
  */
 
 /**
- * @class Roo.bootstrap.MonthField
- * @extends Roo.bootstrap.Input
- * Bootstrap MonthField class
+ * @class Roo.bootstrap.TabPanel
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TabPanel class
+ * @cfg {Boolean} active panel active
+ * @cfg {String} html panel content
+ * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
+ * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
+ * @cfg {String} href click to link..
+ * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
  * 
- * @cfg {String} language default en
  * 
  * @constructor
- * Create a new MonthField
+ * Create a new TabPanel
  * @param {Object} config The config object
  */
 
-Roo.bootstrap.MonthField = function(config){
-    Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
-    
+Roo.bootstrap.TabPanel = function(config){
+    Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
     this.addEvents({
         /**
-         * @event show
-         * Fires when this field show.
-         * @param {Roo.bootstrap.MonthField} this
-         * @param {Mixed} date The date value
-         */
-        show : true,
-        /**
-         * @event show
-         * Fires when this field hide.
-         * @param {Roo.bootstrap.MonthField} this
-         * @param {Mixed} date The date value
+            * @event changed
+            * Fires when the active status changes
+            * @param {Roo.bootstrap.TabPanel} this
+            * @param {Boolean} state the new state
+           
          */
-        hide : true,
+        'changed': true,
         /**
-         * @event select
-         * Fires when select a date.
-         * @param {Roo.bootstrap.MonthField} this
-         * @param {String} oldvalue The old value
-         * @param {String} newvalue The new value
+            * @event beforedeactivate
+            * Fires before a tab is de-activated - can be used to do validation on a form.
+            * @param {Roo.bootstrap.TabPanel} this
+            * @return {Boolean} false if there is an error
+           
          */
-        select : true
-    });
+        'beforedeactivate': true
+     });
+    
+    this.tabId = this.tabId || Roo.id();
+  
 };
 
-Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
+Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
     
-    onRender: function(ct, position)
-    {
-        
-        Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
-        
-        this.language = this.language || 'en';
-        this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
-        this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
+    active: false,
+    html: false,
+    tabId: false,
+    navId : false,
+    href : '',
+    touchSlide : false,
+    getAutoCreate : function(){
         
-        this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
-        this.isInline = false;
-        this.isInput = true;
-        this.component = this.el.select('.add-on', true).first() || false;
-        this.component = (this.component && this.component.length === 0) ? false : this.component;
-        this.hasInput = this.component && this.inputEL().length;
+       
+       var cfg = {
+            tag: 'div',
+            // item is needed for carousel - not sure if it has any effect otherwise
+            cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
+            html: this.html || ''
+        };
         
-        this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
+        if(this.active){
+            cfg.cls += ' active';
+        }
         
-        this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        if(this.tabId){
+            cfg.tabId = this.tabId;
+        }
         
-        this.picker().on('mousedown', this.onMousedown, this);
-        this.picker().on('click', this.onClick, this);
+       
         
-        this.picker().addClass('datepicker-dropdown');
+        return cfg;
+    },
+    
+    initEvents:  function()
+    {
+        var p = this.parent();
         
-        Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
-            v.setStyle('width', '189px');
-        });
+        this.navId = this.navId || p.navId;
         
-        this.fillMonths();
+        if (typeof(this.navId) != 'undefined') {
+            // not really needed.. but just in case.. parent should be a NavGroup.
+            var tg = Roo.bootstrap.TabGroup.get(this.navId);
+            
+            tg.register(this);
+            
+            var i = tg.tabs.length - 1;
+            
+            if(this.active && tg.bullets > 0 && i < tg.bullets){
+                tg.setActiveBullet(i);
+            }
+        }
         
-        this.update();
+        this.el.on('click', this.onClick, this);
         
-        if(this.isInline) {
-            this.show();
+        if(Roo.isTouch && this.touchSlide){
+            this.el.on("touchstart", this.onTouchStart, this);
+            this.el.on("touchmove", this.onTouchMove, this);
+            this.el.on("touchend", this.onTouchEnd, this);
         }
         
     },
     
-    setValue: function(v, suppressEvent)
-    {   
-        var o = this.getValue();
-        
-        Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
-        
-        this.update();
-
-        if(suppressEvent !== true){
-            this.fireEvent('select', this, o, v);
-        }
-        
+    onRender : function(ct, position)
+    {
+        Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
     },
     
-    getValue: function()
+    setActive : function(state)
     {
-        return this.value;
+        Roo.log("panel - set active " + this.tabId + "=" + state);
+        
+        this.active = state;
+        if (!state) {
+            this.el.removeClass('active');
+            
+        } else  if (!this.el.hasClass('active')) {
+            this.el.addClass('active');
+        }
+        
+        this.fireEvent('changed', this, state);
     },
     
-    onClick: function(e) 
+    onClick : function(e)
     {
-        e.stopPropagation();
         e.preventDefault();
         
-        var target = e.getTarget();
+        if(!this.href.length){
+            return;
+        }
         
-        if(target.nodeName.toLowerCase() === 'i'){
-            target = Roo.get(target).dom.parentNode;
-        }
-        
-        var nodeName = target.nodeName;
-        var className = target.className;
-        var html = target.innerHTML;
-        
-        if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
-            return;
-        }
-        
-        this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
-        
-        this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-        
-        this.hide();
-                        
+        window.location.href = this.href;
     },
     
-    picker : function()
-    {
-        return this.pickerEl;
-    },
+    startX : 0,
+    startY : 0,
+    endX : 0,
+    endY : 0,
+    swiping : false,
     
-    fillMonths: function()
-    {    
-        var i = 0;
-        var months = this.picker().select('>.datepicker-months td', true).first();
-        
-        months.dom.innerHTML = '';
-        
-        while (i < 12) {
-            var month = {
-                tag: 'span',
-                cls: 'month',
-                html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
-            };
-            
-            months.createChild(month);
-        }
+    onTouchStart : function(e)
+    {
+        this.swiping = false;
         
+        this.startX = e.browserEvent.touches[0].clientX;
+        this.startY = e.browserEvent.touches[0].clientY;
     },
     
-    update: function()
+    onTouchMove : function(e)
     {
-        var _this = this;
-        
-        if(typeof(this.vIndex) == 'undefined' && this.value.length){
-            this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
-        }
+        this.swiping = true;
         
-        Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
-            e.removeClass('active');
-            
-            if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
-                e.addClass('active');
-            }
-        })
+        this.endX = e.browserEvent.touches[0].clientX;
+        this.endY = e.browserEvent.touches[0].clientY;
     },
     
-    place: function()
+    onTouchEnd : function(e)
     {
-        if(this.isInline) {
+        if(!this.swiping){
+            this.onClick(e);
             return;
         }
         
-        this.picker().removeClass(['bottom', 'top']);
+        var tabGroup = this.parent();
         
-        if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
-            /*
-             * place to the top of element!
-             *
-             */
-            
-            this.picker().addClass('top');
-            this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
-            
+        if(this.endX > this.startX){ // swiping right
+            tabGroup.showPanelPrev();
             return;
         }
         
-        this.picker().addClass('bottom');
-        
-        this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
-    },
+        if(this.startX > this.endX){ // swiping left
+            tabGroup.showPanelNext();
+            return;
+        }
+    }
     
-    onFocus : function()
+    
+});
+
+
+ /*
+ * - LGPL
+ *
+ * DateField
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.DateField
+ * @extends Roo.bootstrap.Input
+ * Bootstrap DateField class
+ * @cfg {Number} weekStart default 0
+ * @cfg {String} viewMode default empty, (months|years)
+ * @cfg {String} minViewMode default empty, (months|years)
+ * @cfg {Number} startDate default -Infinity
+ * @cfg {Number} endDate default Infinity
+ * @cfg {Boolean} todayHighlight default false
+ * @cfg {Boolean} todayBtn default false
+ * @cfg {Boolean} calendarWeeks default false
+ * @cfg {Object} daysOfWeekDisabled default empty
+ * @cfg {Boolean} singleMode default false (true | false)
+ * 
+ * @cfg {Boolean} keyboardNavigation default true
+ * @cfg {String} language default en
+ * 
+ * @constructor
+ * Create a new DateField
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.DateField = function(config){
+    Roo.bootstrap.DateField.superclass.constructor.call(this, config);
+     this.addEvents({
+            /**
+             * @event show
+             * Fires when this field show.
+             * @param {Roo.bootstrap.DateField} this
+             * @param {Mixed} date The date value
+             */
+            show : true,
+            /**
+             * @event show
+             * Fires when this field hide.
+             * @param {Roo.bootstrap.DateField} this
+             * @param {Mixed} date The date value
+             */
+            hide : true,
+            /**
+             * @event select
+             * Fires when select a date.
+             * @param {Roo.bootstrap.DateField} this
+             * @param {Mixed} date The date value
+             */
+            select : true,
+            /**
+             * @event beforeselect
+             * Fires when before select a date.
+             * @param {Roo.bootstrap.DateField} this
+             * @param {Mixed} date The date value
+             */
+            beforeselect : true
+        });
+};
+
+Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
+    
+    /**
+     * @cfg {String} format
+     * The default date format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
+     */
+    format : "m/d/y",
+    /**
+     * @cfg {String} altFormats
+     * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+     * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
+     */
+    altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
+    
+    weekStart : 0,
+    
+    viewMode : '',
+    
+    minViewMode : '',
+    
+    todayHighlight : false,
+    
+    todayBtn: false,
+    
+    language: 'en',
+    
+    keyboardNavigation: true,
+    
+    calendarWeeks: false,
+    
+    startDate: -Infinity,
+    
+    endDate: Infinity,
+    
+    daysOfWeekDisabled: [],
+    
+    _events: [],
+    
+    singleMode : false,
+    
+    UTCDate: function()
     {
-        Roo.bootstrap.MonthField.superclass.onFocus.call(this);
-        this.show();
+        return new Date(Date.UTC.apply(Date, arguments));
     },
     
-    onBlur : function()
+    UTCToday: function()
     {
-        Roo.bootstrap.MonthField.superclass.onBlur.call(this);
-        
-        var d = this.inputEl().getValue();
-        
-        this.setValue(d);
-                
-        this.hide();
+        var today = new Date();
+        return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
     },
     
-    show : function()
-    {
-        this.picker().show();
-        this.picker().select('>.datepicker-months', true).first().show();
-        this.update();
-        this.place();
-        
-        this.fireEvent('show', this, this.date);
+    getDate: function() {
+            var d = this.getUTCDate();
+            return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
     },
     
-    hide : function()
-    {
-        if(this.isInline) {
-            return;
-        }
-        this.picker().hide();
-        this.fireEvent('hide', this, this.date);
-        
+    getUTCDate: function() {
+            return this.date;
     },
     
-    onMousedown: function(e)
-    {
-        e.stopPropagation();
-        e.preventDefault();
+    setDate: function(d) {
+            this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
     },
     
-    keyup: function(e)
-    {
-        Roo.bootstrap.MonthField.superclass.keyup.call(this);
-        this.update();
+    setUTCDate: function(d) {
+            this.date = d;
+            this.setValue(this.formatDate(this.date));
     },
-
-    fireKey: function(e)
-    {
-        if (!this.picker().isVisible()){
-            if (e.keyCode == 27)   {// allow escape to hide and re-show picker
-                this.show();
+        
+    onRender: function(ct, position)
+    {
+        
+        Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
+        
+        this.language = this.language || 'en';
+        this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
+        this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
+        
+        this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
+        this.format = this.format || 'm/d/y';
+        this.isInline = false;
+        this.isInput = true;
+        this.component = this.el.select('.add-on', true).first() || false;
+        this.component = (this.component && this.component.length === 0) ? false : this.component;
+        this.hasInput = this.component && this.inputEl().length;
+        
+        if (typeof(this.minViewMode === 'string')) {
+            switch (this.minViewMode) {
+                case 'months':
+                    this.minViewMode = 1;
+                    break;
+                case 'years':
+                    this.minViewMode = 2;
+                    break;
+                default:
+                    this.minViewMode = 0;
+                    break;
             }
-            return;
         }
         
-        var dir;
-        
-        switch(e.keyCode){
-            case 27: // escape
-                this.hide();
-                e.preventDefault();
-                break;
-            case 37: // left
-            case 39: // right
-                dir = e.keyCode == 37 ? -1 : 1;
-                
-                this.vIndex = this.vIndex + dir;
-                
-                if(this.vIndex < 0){
-                    this.vIndex = 0;
-                }
-                
-                if(this.vIndex > 11){
-                    this.vIndex = 11;
-                }
-                
-                if(isNaN(this.vIndex)){
-                    this.vIndex = 0;
-                }
-                
-                this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-                
-                break;
-            case 38: // up
-            case 40: // down
-                
-                dir = e.keyCode == 38 ? -1 : 1;
-                
-                this.vIndex = this.vIndex + dir * 4;
-                
-                if(this.vIndex < 0){
-                    this.vIndex = 0;
-                }
-                
-                if(this.vIndex > 11){
-                    this.vIndex = 11;
-                }
-                
-                if(isNaN(this.vIndex)){
-                    this.vIndex = 0;
-                }
-                
-                this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-                break;
-                
-            case 13: // enter
-                
-                if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
-                    this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-                }
-                
-                this.hide();
-                e.preventDefault();
-                break;
-            case 9: // tab
-                if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
-                    this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-                }
-                this.hide();
-                break;
-            case 16: // shift
-            case 17: // ctrl
-            case 18: // alt
-                break;
-            default :
-                this.hide();
+        if (typeof(this.viewMode === 'string')) {
+            switch (this.viewMode) {
+                case 'months':
+                    this.viewMode = 1;
+                    break;
+                case 'years':
+                    this.viewMode = 2;
+                    break;
+                default:
+                    this.viewMode = 0;
+                    break;
+            }
+        }
                 
+        this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
+        
+//        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
+        
+        this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+        
+        this.picker().on('mousedown', this.onMousedown, this);
+        this.picker().on('click', this.onClick, this);
+        
+        this.picker().addClass('datepicker-dropdown');
+        
+        this.startViewMode = this.viewMode;
+        
+        if(this.singleMode){
+            Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
+                v.setVisibilityMode(Roo.Element.DISPLAY);
+                v.hide();
+            });
+            
+            Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
+                v.setStyle('width', '189px');
+            });
+        }
+        
+        Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
+            if(!this.calendarWeeks){
+                v.remove();
+                return;
+            }
+            
+            v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
+            v.attr('colspan', function(i, val){
+                return parseInt(val) + 1;
+            });
+        });
+                       
+        
+        this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
+        
+        this.setStartDate(this.startDate);
+        this.setEndDate(this.endDate);
+        
+        this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+        
+        this.fillDow();
+        this.fillMonths();
+        this.update();
+        this.showMode();
+        
+        if(this.isInline) {
+            this.showPopup();
         }
     },
     
-    remove: function() 
+    picker : function()
     {
-        this.picker().remove();
-    }
-   
-});
-
-Roo.apply(Roo.bootstrap.MonthField,  {
+        return this.pickerEl;
+//        return this.el.select('.datepicker', true).first();
+    },
     
-    content : {
-        tag: 'tbody',
-        cn: [
-        {
+    fillDow: function()
+    {
+        var dowCnt = this.weekStart;
+        
+        var dow = {
             tag: 'tr',
             cn: [
-            {
-                tag: 'td',
-                colspan: '7'
-            }
+                
             ]
+        };
+        
+        if(this.calendarWeeks){
+            dow.cn.push({
+                tag: 'th',
+                cls: 'cw',
+                html: '&nbsp;'
+            })
         }
-        ]
+        
+        while (dowCnt < this.weekStart + 7) {
+            dow.cn.push({
+                tag: 'th',
+                cls: 'dow',
+                html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
+            });
+        }
+        
+        this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
     },
     
-    dates:{
-        en: {
-            months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
-            monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+    fillMonths: function()
+    {    
+        var i = 0;
+        var months = this.picker().select('>.datepicker-months td', true).first();
+        
+        months.dom.innerHTML = '';
+        
+        while (i < 12) {
+            var month = {
+                tag: 'span',
+                cls: 'month',
+                html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
+            };
+            
+            months.createChild(month);
         }
-    }
-});
-
-Roo.apply(Roo.bootstrap.MonthField,  {
-  
-    template : {
-        tag: 'div',
-        cls: 'datepicker dropdown-menu roo-dynamic',
-        cn: [
-            {
-                tag: 'div',
-                cls: 'datepicker-months',
-                cn: [
-                {
-                    tag: 'table',
-                    cls: 'table-condensed',
-                    cn:[
-                        Roo.bootstrap.DateField.content
-                    ]
-                }
-                ]
-            }
-        ]
-    }
-});
-
-
- /*
- * - LGPL
- *
- * CheckBox
- * 
- */
-
-/**
- * @class Roo.bootstrap.CheckBox
- * @extends Roo.bootstrap.Input
- * Bootstrap CheckBox class
- * 
- * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
- * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
- * @cfg {String} boxLabel The text that appears beside the checkbox
- * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
- * @cfg {Boolean} checked initnal the element
- * @cfg {Boolean} inline inline the element (default false)
- * @cfg {String} groupId the checkbox group id // normal just use for checkbox
- * @cfg {String} tooltip label tooltip
- * 
- * @constructor
- * Create a new CheckBox
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.CheckBox = function(config){
-    Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
-   
-    this.addEvents({
-        /**
-        * @event check
-        * Fires when the element is checked or unchecked.
-        * @param {Roo.bootstrap.CheckBox} this This input
-        * @param {Boolean} checked The new checked value
-        */
-       check : true,
-       /**
-        * @event click
-        * Fires when the element is click.
-        * @param {Roo.bootstrap.CheckBox} this This input
-        */
-       click : true
-    });
-    
-};
-
-Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
-  
-    inputType: 'checkbox',
-    inputValue: 1,
-    valueOff: 0,
-    boxLabel: false,
-    checked: false,
-    weight : false,
-    inline: false,
-    tooltip : '',
-    
-    // checkbox success does not make any sense really.. 
-    invalidClass : "",
-    validClass : "",
+        
+    },
     
+    update: function()
+    {
+        this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
+        
+        if (this.date < this.startDate) {
+            this.viewDate = new Date(this.startDate);
+        } else if (this.date > this.endDate) {
+            this.viewDate = new Date(this.endDate);
+        } else {
+            this.viewDate = new Date(this.date);
+        }
+        
+        this.fill();
+    },
     
-    getAutoCreate : function()
+    fill: function() 
     {
-        var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+        var d = new Date(this.viewDate),
+                year = d.getUTCFullYear(),
+                month = d.getUTCMonth(),
+                startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
+                startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
+                endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
+                endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
+                currentDate = this.date && this.date.valueOf(),
+                today = this.UTCToday();
         
-        var id = Roo.id();
+        this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
         
-        var cfg = {};
+//        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
         
-        cfg.cls = 'form-group form-check ' + this.inputType; //input-group
+//        this.picker.select('>tfoot th.today').
+//                                             .text(dates[this.language].today)
+//                                             .toggle(this.todayBtn !== false);
+    
+        this.updateNavArrows();
+        this.fillMonths();
+                                                
+        var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
         
-        if(this.inline){
-            cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
-        }
+        day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+         
+        prevMonth.setUTCDate(day);
         
-        var input =  {
-            tag: 'input',
-            id : id,
-            type : this.inputType,
-            value : this.inputValue,
-            cls : 'roo-' + this.inputType, //'form-box',
-            placeholder : this.placeholder || ''
-            
-        };
+        prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
         
-        if(this.inputType != 'radio'){
-            var hidden =  {
-                tag: 'input',
-                type : 'hidden',
-                cls : 'roo-hidden-value',
-                value : this.checked ? this.inputValue : this.valueOff
-            };
-        }
+        var nextMonth = new Date(prevMonth);
         
-            
-        if (this.weight) { // Validity check?
-            cfg.cls += " " + this.inputType + "-" + this.weight;
-        }
+        nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
         
-        if (this.disabled) {
-            input.disabled=true;
-        }
+        nextMonth = nextMonth.valueOf();
         
-        if(this.checked){
-            input.checked = this.checked;
-        }
+        var fillMonths = false;
         
-        if (this.name) {
+        this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
+        
+        while(prevMonth.valueOf() <= nextMonth) {
+            var clsName = '';
             
-            input.name = this.name;
+            if (prevMonth.getUTCDay() === this.weekStart) {
+                if(fillMonths){
+                    this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
+                }
+                    
+                fillMonths = {
+                    tag: 'tr',
+                    cn: []
+                };
+                
+                if(this.calendarWeeks){
+                    // ISO 8601: First week contains first thursday.
+                    // ISO also states week starts on Monday, but we can be more abstract here.
+                    var
+                    // Start of current week: based on weekstart/current date
+                    ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
+                    // Thursday of this week
+                    th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
+                    // First Thursday of year, year from thursday
+                    yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
+                    // Calendar week: ms between thursdays, div ms per day, div 7 days
+                    calWeek =  (th - yth) / 864e5 / 7 + 1;
+                    
+                    fillMonths.cn.push({
+                        tag: 'td',
+                        cls: 'cw',
+                        html: calWeek
+                    });
+                }
+            }
             
-            if(this.inputType != 'radio'){
-                hidden.name = this.name;
-                input.name = '_hidden_' + this.name;
+            if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
+                clsName += ' old';
+            } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
+                clsName += ' new';
             }
-        }
-        
-        if (this.size) {
-            input.cls += ' input-' + this.size;
-        }
-        
-        var settings=this;
-        
-        ['xs','sm','md','lg'].map(function(size){
-            if (settings[size]) {
-                cfg.cls += ' col-' + size + '-' + settings[size];
+            if (this.todayHighlight &&
+                prevMonth.getUTCFullYear() == today.getFullYear() &&
+                prevMonth.getUTCMonth() == today.getMonth() &&
+                prevMonth.getUTCDate() == today.getDate()) {
+                clsName += ' today';
             }
-        });
-        
-        var inputblock = input;
-         
-        if (this.before || this.after) {
             
-            inputblock = {
-                cls : 'input-group',
-                cn :  [] 
-            };
+            if (currentDate && prevMonth.valueOf() === currentDate) {
+                clsName += ' active';
+            }
             
-            if (this.before) {
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'input-group-addon',
-                    html : this.before
-                });
+            if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
+                    this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
+                    clsName += ' disabled';
             }
             
-            inputblock.cn.push(input);
+            fillMonths.cn.push({
+                tag: 'td',
+                cls: 'day ' + clsName,
+                html: prevMonth.getDate()
+            });
             
-            if(this.inputType != 'radio'){
-                inputblock.cn.push(hidden);
+            prevMonth.setDate(prevMonth.getDate()+1);
+        }
+          
+        var currentYear = this.date && this.date.getUTCFullYear();
+        var currentMonth = this.date && this.date.getUTCMonth();
+        
+        this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
+        
+        Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
+            v.removeClass('active');
+            
+            if(currentYear === year && k === currentMonth){
+                v.addClass('active');
             }
             
-            if (this.after) {
-                inputblock.cn.push({
-                    tag :'span',
-                    cls : 'input-group-addon',
-                    html : this.after
-                });
+            if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
+                v.addClass('disabled');
             }
             
-        }
-        var boxLabelCfg = false;
+        });
         
-        if(this.boxLabel){
-           
-            boxLabelCfg = {
-                tag: 'label',
-                //'for': id, // box label is handled by onclick - so no for...
-                cls: 'box-label',
-                html: this.boxLabel
-            };
-            if(this.tooltip){
-                boxLabelCfg.tooltip = this.tooltip;
-            }
-             
-        }
         
+        year = parseInt(year/10, 10) * 10;
         
-        if (align ==='left' && this.fieldLabel.length) {
-//                Roo.log("left and has label");
-            cfg.cn = [
-                {
-                    tag: 'label',
-                    'for' :  id,
-                    cls : 'control-label',
-                    html : this.fieldLabel
-                },
-                {
-                    cls : "", 
-                    cn: [
-                        inputblock
-                    ]
-                }
-            ];
-            
-            if (boxLabelCfg) {
-                cfg.cn[1].cn.push(boxLabelCfg);
-            }
-            
-            if(this.labelWidth > 12){
-                cfg.cn[0].style = "width: " + this.labelWidth + 'px';
-            }
-            
-            if(this.labelWidth < 13 && this.labelmd == 0){
-                this.labelmd = this.labelWidth;
-            }
-            
-            if(this.labellg > 0){
-                cfg.cn[0].cls += ' col-lg-' + this.labellg;
-                cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
-            }
-            
-            if(this.labelmd > 0){
-                cfg.cn[0].cls += ' col-md-' + this.labelmd;
-                cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
-            }
-            
-            if(this.labelsm > 0){
-                cfg.cn[0].cls += ' col-sm-' + this.labelsm;
-                cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
-            }
-            
-            if(this.labelxs > 0){
-                cfg.cn[0].cls += ' col-xs-' + this.labelxs;
-                cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
-            }
-            
-        } else if ( this.fieldLabel.length) {
-//                Roo.log(" label");
-                cfg.cn = [
-                   
-                    {
-                        tag: this.boxLabel ? 'span' : 'label',
-                        'for': id,
-                        cls: 'control-label box-input-label',
-                        //cls : 'input-group-addon',
-                        html : this.fieldLabel
-                    },
-                    
-                    inputblock
-                    
-                ];
-                if (boxLabelCfg) {
-                    cfg.cn.push(boxLabelCfg);
-                }
-
-        } else {
-            
-//                Roo.log(" no label && no align");
-                cfg.cn = [  inputblock ] ;
-                if (boxLabelCfg) {
-                    cfg.cn.push(boxLabelCfg);
-                }
-
-                
-        }
+        this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
         
-       
+        this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
         
-        if(this.inputType != 'radio'){
-            cfg.cn.push(hidden);
+        year -= 1;
+        for (var i = -1; i < 11; i++) {
+            this.picker().select('>.datepicker-years tbody td',true).first().createChild({
+                tag: 'span',
+                cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
+                html: year
+            });
+            
+            year += 1;
         }
-        
-        return cfg;
-        
     },
     
-    /**
-     * return the real input element.
-     */
-    inputEl: function ()
+    showMode: function(dir) 
     {
-        return this.el.select('input.roo-' + this.inputType,true).first();
+        if (dir) {
+            this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
+        }
+        
+        Roo.each(this.picker().select('>div',true).elements, function(v){
+            v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+            v.hide();
+        });
+        this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
     },
-    hiddenEl: function ()
+    
+    place: function()
     {
-        return this.el.select('input.roo-hidden-value',true).first();
+        if(this.isInline) {
+            return;
+        }
+        
+        this.picker().removeClass(['bottom', 'top']);
+        
+        if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
+            /*
+             * place to the top of element!
+             *
+             */
+            
+            this.picker().addClass('top');
+            this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
+            
+            return;
+        }
+        
+        this.picker().addClass('bottom');
+        
+        this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
     },
     
-    labelEl: function()
+    parseDate : function(value)
     {
-        return this.el.select('label.control-label',true).first();
+        if(!value || value instanceof Date){
+            return value;
+        }
+        var v = Date.parseDate(value, this.format);
+        if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
+            v = Date.parseDate(value, 'Y-m-d');
+        }
+        if(!v && this.altFormats){
+            if(!this.altFormatsArray){
+                this.altFormatsArray = this.altFormats.split("|");
+            }
+            for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+                v = Date.parseDate(value, this.altFormatsArray[i]);
+            }
+        }
+        return v;
     },
-    /* depricated... */
     
-    label: function()
-    {
-        return this.labelEl();
+    formatDate : function(date, fmt)
+    {   
+        return (!date || !(date instanceof Date)) ?
+        date : date.dateFormat(fmt || this.format);
     },
     
-    boxLabelEl: function()
+    onFocus : function()
     {
-        return this.el.select('label.box-label',true).first();
+        Roo.bootstrap.DateField.superclass.onFocus.call(this);
+        this.showPopup();
     },
     
-    initEvents : function()
+    onBlur : function()
     {
-//        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
-        
-        this.inputEl().on('click', this.onClick,  this);
-        
-        if (this.boxLabel) { 
-            this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
-        }
+        Roo.bootstrap.DateField.superclass.onBlur.call(this);
         
-        this.startValue = this.getValue();
+        var d = this.inputEl().getValue();
         
-        if(this.groupId){
-            Roo.bootstrap.CheckBox.register(this);
-        }
+        this.setValue(d);
+                
+        this.hidePopup();
     },
     
-    onClick : function(e)
-    {   
-        if(this.fireEvent('click', this, e) !== false){
-            this.setChecked(!this.checked);
-        }
+    showPopup : function()
+    {
+        this.picker().show();
+        this.update();
+        this.place();
         
+        this.fireEvent('showpopup', this, this.date);
     },
     
-    setChecked : function(state,suppressEvent)
+    hidePopup : function()
     {
-        this.startValue = this.getValue();
-
-        if(this.inputType == 'radio'){
-            
-            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-                e.dom.checked = false;
-            });
-            
-            this.inputEl().dom.checked = true;
-            
-            this.inputEl().dom.value = this.inputValue;
-            
-            if(suppressEvent !== true){
-                this.fireEvent('check', this, true);
-            }
-            
-            this.validate();
-            
+        if(this.isInline) {
             return;
         }
+        this.picker().hide();
+        this.viewMode = this.startViewMode;
+        this.showMode();
         
-        this.checked = state;
-        
-        this.inputEl().dom.checked = state;
-        
-        
-        this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
-        
-        if(suppressEvent !== true){
-            this.fireEvent('check', this, state);
-        }
+        this.fireEvent('hidepopup', this, this.date);
         
-        this.validate();
     },
     
-    getValue : function()
+    onMousedown: function(e)
     {
-        if(this.inputType == 'radio'){
-            return this.getGroupValue();
-        }
-        
-        return this.hiddenEl().dom.value;
-        
+        e.stopPropagation();
+        e.preventDefault();
     },
     
-    getGroupValue : function()
-    {
-        if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
-            return '';
-        }
-        
-        return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
-    },
-    
-    setValue : function(v,suppressEvent)
+    keyup: function(e)
     {
-        if(this.inputType == 'radio'){
-            this.setGroupValue(v, suppressEvent);
-            return;
-        }
-        
-        this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
-        
-        this.validate();
+        Roo.bootstrap.DateField.superclass.keyup.call(this);
+        this.update();
     },
-    
-    setGroupValue : function(v, suppressEvent)
+
+    setValue: function(v)
     {
-        this.startValue = this.getValue();
+        if(this.fireEvent('beforeselect', this, v) !== false){
+            var d = new Date(this.parseDate(v) ).clearTime();
         
-        Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-            e.dom.checked = false;
-            
-            if(e.dom.value == v){
-                e.dom.checked = true;
+            if(isNaN(d.getTime())){
+                this.date = this.viewDate = '';
+                Roo.bootstrap.DateField.superclass.setValue.call(this, '');
+                return;
             }
-        });
-        
-        if(suppressEvent !== true){
-            this.fireEvent('check', this, true);
-        }
 
-        this.validate();
-        
-        return;
-    },
-    
-    validate : function()
-    {
-        if(this.getVisibilityEl().hasClass('hidden')){
-            return true;
-        }
-        
-        if(
-                this.disabled || 
-                (this.inputType == 'radio' && this.validateRadio()) ||
-                (this.inputType == 'checkbox' && this.validateCheckbox())
-        ){
-            this.markValid();
-            return true;
+            v = this.formatDate(d);
+
+            Roo.bootstrap.DateField.superclass.setValue.call(this, v);
+
+            this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
+
+            this.update();
+
+            this.fireEvent('select', this, this.date);
         }
-        
-        this.markInvalid();
-        return false;
     },
     
-    validateRadio : function()
+    getValue: function()
     {
-        if(this.getVisibilityEl().hasClass('hidden')){
-            return true;
-        }
-        
-        if(this.allowBlank){
-            return true;
-        }
-        
-        var valid = false;
-        
-        Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-            if(!e.dom.checked){
-                return;
-            }
-            
-            valid = true;
-            
-            return false;
-        });
-        
-        return valid;
+        return this.formatDate(this.date);
     },
     
-    validateCheckbox : function()
+    fireKey: function(e)
     {
-        if(!this.groupId){
-            return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
-            //return (this.getValue() == this.inputValue) ? true : false;
-        }
-        
-        var group = Roo.bootstrap.CheckBox.get(this.groupId);
-        
-        if(!group){
-            return false;
+        if (!this.picker().isVisible()){
+            if (e.keyCode == 27) { // allow escape to hide and re-show picker
+                this.showPopup();
+            }
+            return;
         }
         
-        var r = false;
+        var dateChanged = false,
+        dir, day, month,
+        newDate, newViewDate;
         
-        for(var i in group){
-            if(group[i].el.isVisible(true)){
-                r = false;
+        switch(e.keyCode){
+            case 27: // escape
+                this.hidePopup();
+                e.preventDefault();
                 break;
-            }
-            
-            r = true;
-        }
-        
-        for(var i in group){
-            if(r){
+            case 37: // left
+            case 39: // right
+                if (!this.keyboardNavigation) {
+                    break;
+                }
+                dir = e.keyCode == 37 ? -1 : 1;
+                
+                if (e.ctrlKey){
+                    newDate = this.moveYear(this.date, dir);
+                    newViewDate = this.moveYear(this.viewDate, dir);
+                } else if (e.shiftKey){
+                    newDate = this.moveMonth(this.date, dir);
+                    newViewDate = this.moveMonth(this.viewDate, dir);
+                } else {
+                    newDate = new Date(this.date);
+                    newDate.setUTCDate(this.date.getUTCDate() + dir);
+                    newViewDate = new Date(this.viewDate);
+                    newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+                }
+                if (this.dateWithinRange(newDate)){
+                    this.date = newDate;
+                    this.viewDate = newViewDate;
+                    this.setValue(this.formatDate(this.date));
+//                    this.update();
+                    e.preventDefault();
+                    dateChanged = true;
+                }
                 break;
-            }
-            
-            r = (group[i].getValue() == group[i].inputValue) ? true : false;
+            case 38: // up
+            case 40: // down
+                if (!this.keyboardNavigation) {
+                    break;
+                }
+                dir = e.keyCode == 38 ? -1 : 1;
+                if (e.ctrlKey){
+                    newDate = this.moveYear(this.date, dir);
+                    newViewDate = this.moveYear(this.viewDate, dir);
+                } else if (e.shiftKey){
+                    newDate = this.moveMonth(this.date, dir);
+                    newViewDate = this.moveMonth(this.viewDate, dir);
+                } else {
+                    newDate = new Date(this.date);
+                    newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+                    newViewDate = new Date(this.viewDate);
+                    newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+                }
+                if (this.dateWithinRange(newDate)){
+                    this.date = newDate;
+                    this.viewDate = newViewDate;
+                    this.setValue(this.formatDate(this.date));
+//                    this.update();
+                    e.preventDefault();
+                    dateChanged = true;
+                }
+                break;
+            case 13: // enter
+                this.setValue(this.formatDate(this.date));
+                this.hidePopup();
+                e.preventDefault();
+                break;
+            case 9: // tab
+                this.setValue(this.formatDate(this.date));
+                this.hidePopup();
+                break;
+            case 16: // shift
+            case 17: // ctrl
+            case 18: // alt
+                break;
+            default :
+                this.hidePopup();
+                
         }
-        
-        return r;
     },
     
-    /**
-     * Mark this field as valid
-     */
-    markValid : function()
+    
+    onClick: function(e) 
     {
-        var _this = this;
-        
-        this.fireEvent('valid', this);
+        e.stopPropagation();
+        e.preventDefault();
         
-        var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+        var target = e.getTarget();
         
-        if(this.groupId){
-            label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
+        if(target.nodeName.toLowerCase() === 'i'){
+            target = Roo.get(target).dom.parentNode;
         }
         
-        if(label){
-            label.markValid();
-        }
-
-        if(this.inputType == 'radio'){
-            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-                var fg = e.findParent('.form-group', false, true);
-                if (Roo.bootstrap.version == 3) {
-                    fg.removeClass([_this.invalidClass, _this.validClass]);
-                    fg.addClass(_this.validClass);
-                } else {
-                    fg.removeClass(['is-valid', 'is-invalid']);
-                    fg.addClass('is-valid');
-                }
-            });
-            
-            return;
-        }
-
-        if(!this.groupId){
-            var fg = this.el.findParent('.form-group', false, true);
-            if (Roo.bootstrap.version == 3) {
-                fg.removeClass([this.invalidClass, this.validClass]);
-                fg.addClass(this.validClass);
-            } else {
-                fg.removeClass(['is-valid', 'is-invalid']);
-                fg.addClass('is-valid');
-            }
-            return;
-        }
+        var nodeName = target.nodeName;
+        var className = target.className;
+        var html = target.innerHTML;
+        //Roo.log(nodeName);
         
-        var group = Roo.bootstrap.CheckBox.get(this.groupId);
-        
-        if(!group){
-            return;
-        }
-        
-        for(var i in group){
-            var fg = group[i].el.findParent('.form-group', false, true);
-            if (Roo.bootstrap.version == 3) {
-                fg.removeClass([this.invalidClass, this.validClass]);
-                fg.addClass(this.validClass);
-            } else {
-                fg.removeClass(['is-valid', 'is-invalid']);
-                fg.addClass('is-valid');
-            }
+        switch(nodeName.toLowerCase()) {
+            case 'th':
+                switch(className) {
+                    case 'switch':
+                        this.showMode(1);
+                        break;
+                    case 'prev':
+                    case 'next':
+                        var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
+                        switch(this.viewMode){
+                                case 0:
+                                        this.viewDate = this.moveMonth(this.viewDate, dir);
+                                        break;
+                                case 1:
+                                case 2:
+                                        this.viewDate = this.moveYear(this.viewDate, dir);
+                                        break;
+                        }
+                        this.fill();
+                        break;
+                    case 'today':
+                        var date = new Date();
+                        this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+//                        this.fill()
+                        this.setValue(this.formatDate(this.date));
+                        
+                        this.hidePopup();
+                        break;
+                }
+                break;
+            case 'span':
+                if (className.indexOf('disabled') < 0) {
+                if (!this.viewDate) {
+                    this.viewDate = new Date();
+                }
+                this.viewDate.setUTCDate(1);
+                    if (className.indexOf('month') > -1) {
+                        this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
+                    } else {
+                        var year = parseInt(html, 10) || 0;
+                        this.viewDate.setUTCFullYear(year);
+                        
+                    }
+                    
+                    if(this.singleMode){
+                        this.setValue(this.formatDate(this.viewDate));
+                        this.hidePopup();
+                        return;
+                    }
+                    
+                    this.showMode(-1);
+                    this.fill();
+                }
+                break;
+                
+            case 'td':
+                //Roo.log(className);
+                if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
+                    var day = parseInt(html, 10) || 1;
+                    var year =  (this.viewDate || new Date()).getUTCFullYear(),
+                        month = (this.viewDate || new Date()).getUTCMonth();
+
+                    if (className.indexOf('old') > -1) {
+                        if(month === 0 ){
+                            month = 11;
+                            year -= 1;
+                        }else{
+                            month -= 1;
+                        }
+                    } else if (className.indexOf('new') > -1) {
+                        if (month == 11) {
+                            month = 0;
+                            year += 1;
+                        } else {
+                            month += 1;
+                        }
+                    }
+                    //Roo.log([year,month,day]);
+                    this.date = this.UTCDate(year, month, day,0,0,0,0);
+                    this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
+//                    this.fill();
+                    //Roo.log(this.formatDate(this.date));
+                    this.setValue(this.formatDate(this.date));
+                    this.hidePopup();
+                }
+                break;
         }
     },
     
-     /**
-     * Mark this field as invalid
-     * @param {String} msg The validation message
-     */
-    markInvalid : function(msg)
+    setStartDate: function(startDate)
     {
-        if(this.allowBlank){
-            return;
-        }
-        
-        var _this = this;
-        
-        this.fireEvent('invalid', this, msg);
-        
-        var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
-        
-        if(this.groupId){
-            label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
-        }
-        
-        if(label){
-            label.markInvalid();
-        }
-            
-        if(this.inputType == 'radio'){
-            
-            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-                var fg = e.findParent('.form-group', false, true);
-                if (Roo.bootstrap.version == 3) {
-                    fg.removeClass([_this.invalidClass, _this.validClass]);
-                    fg.addClass(_this.invalidClass);
-                } else {
-                    fg.removeClass(['is-invalid', 'is-valid']);
-                    fg.addClass('is-invalid');
-                }
-            });
-            
-            return;
-        }
-        
-        if(!this.groupId){
-            var fg = this.el.findParent('.form-group', false, true);
-            if (Roo.bootstrap.version == 3) {
-                fg.removeClass([_this.invalidClass, _this.validClass]);
-                fg.addClass(_this.invalidClass);
-            } else {
-                fg.removeClass(['is-invalid', 'is-valid']);
-                fg.addClass('is-invalid');
-            }
-            return;
-        }
-        
-        var group = Roo.bootstrap.CheckBox.get(this.groupId);
-        
-        if(!group){
-            return;
-        }
-        
-        for(var i in group){
-            var fg = group[i].el.findParent('.form-group', false, true);
-            if (Roo.bootstrap.version == 3) {
-                fg.removeClass([_this.invalidClass, _this.validClass]);
-                fg.addClass(_this.invalidClass);
-            } else {
-                fg.removeClass(['is-invalid', 'is-valid']);
-                fg.addClass('is-invalid');
-            }
+        this.startDate = startDate || -Infinity;
+        if (this.startDate !== -Infinity) {
+            this.startDate = this.parseDate(this.startDate);
         }
-        
+        this.update();
+        this.updateNavArrows();
     },
-    
-    clearInvalid : function()
+
+    setEndDate: function(endDate)
     {
-        Roo.bootstrap.Input.prototype.clearInvalid.call(this);
-        
-        // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
-        
-        var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
-        
-        if (label && label.iconEl) {
-            label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
-            label.iconEl.removeClass(['is-invalid', 'is-valid']);
+        this.endDate = endDate || Infinity;
+        if (this.endDate !== Infinity) {
+            this.endDate = this.parseDate(this.endDate);
         }
+        this.update();
+        this.updateNavArrows();
     },
     
-    disable : function()
+    setDaysOfWeekDisabled: function(daysOfWeekDisabled)
     {
-        if(this.inputType != 'radio'){
-            Roo.bootstrap.CheckBox.superclass.disable.call(this);
-            return;
-        }
-        
-        var _this = this;
-        
-        if(this.rendered){
-            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-                _this.getActionEl().addClass(this.disabledClass);
-                e.dom.disabled = true;
-            });
+        this.daysOfWeekDisabled = daysOfWeekDisabled || [];
+        if (typeof(this.daysOfWeekDisabled) !== 'object') {
+            this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
         }
-        
-        this.disabled = true;
-        this.fireEvent("disable", this);
-        return this;
+        this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
+            return parseInt(d, 10);
+        });
+        this.update();
+        this.updateNavArrows();
     },
-
-    enable : function()
+    
+    updateNavArrows: function() 
     {
-        if(this.inputType != 'radio'){
-            Roo.bootstrap.CheckBox.superclass.enable.call(this);
+        if(this.singleMode){
             return;
         }
         
-        var _this = this;
-        
-        if(this.rendered){
-            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
-                _this.getActionEl().removeClass(this.disabledClass);
-                e.dom.disabled = false;
-            });
-        }
-        
-        this.disabled = false;
-        this.fireEvent("enable", this);
-        return this;
-    },
-    
-    setBoxLabel : function(v)
-    {
-        this.boxLabel = v;
+        var d = new Date(this.viewDate),
+        year = d.getUTCFullYear(),
+        month = d.getUTCMonth();
         
-        if(this.rendered){
-            this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
-        }
-    }
-
-});
+        Roo.each(this.picker().select('.prev', true).elements, function(v){
+            v.show();
+            switch (this.viewMode) {
+                case 0:
 
-Roo.apply(Roo.bootstrap.CheckBox, {
-    
-    groups: {},
+                    if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
+                        v.hide();
+                    }
+                    break;
+                case 1:
+                case 2:
+                    if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
+                        v.hide();
+                    }
+                    break;
+            }
+        });
+        
+        Roo.each(this.picker().select('.next', true).elements, function(v){
+            v.show();
+            switch (this.viewMode) {
+                case 0:
+
+                    if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
+                        v.hide();
+                    }
+                    break;
+                case 1:
+                case 2:
+                    if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
+                        v.hide();
+                    }
+                    break;
+            }
+        })
+    },
     
-     /**
-    * register a CheckBox Group
-    * @param {Roo.bootstrap.CheckBox} the CheckBox to add
-    */
-    register : function(checkbox)
+    moveMonth: function(date, dir)
     {
-        if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
-            this.groups[checkbox.groupId] = {};
+        if (!dir) {
+            return date;
         }
-        
-        if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
-            return;
+        var new_date = new Date(date.valueOf()),
+        day = new_date.getUTCDate(),
+        month = new_date.getUTCMonth(),
+        mag = Math.abs(dir),
+        new_month, test;
+        dir = dir > 0 ? 1 : -1;
+        if (mag == 1){
+            test = dir == -1
+            // If going back one month, make sure month is not current month
+            // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+            ? function(){
+                return new_date.getUTCMonth() == month;
+            }
+            // If going forward one month, make sure month is as expected
+            // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+            : function(){
+                return new_date.getUTCMonth() != new_month;
+            };
+            new_month = month + dir;
+            new_date.setUTCMonth(new_month);
+            // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+            if (new_month < 0 || new_month > 11) {
+                new_month = (new_month + 12) % 12;
+            }
+        } else {
+            // For magnitudes >1, move one month at a time...
+            for (var i=0; i<mag; i++) {
+                // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
+                new_date = this.moveMonth(new_date, dir);
+            }
+            // ...then reset the day, keeping it in the new month
+            new_month = new_date.getUTCMonth();
+            new_date.setUTCDate(day);
+            test = function(){
+                return new_month != new_date.getUTCMonth();
+            };
         }
-        
-        this.groups[checkbox.groupId][checkbox.name] = checkbox;
-       
-    },
-    /**
-    * fetch a CheckBox Group based on the group ID
-    * @param {string} the group ID
-    * @returns {Roo.bootstrap.CheckBox} the CheckBox group
-    */
-    get: function(groupId) {
-        if (typeof(this.groups[groupId]) == 'undefined') {
-            return false;
+        // Common date-resetting loop -- if date is beyond end of month, make it
+        // end of month
+        while (test()){
+            new_date.setUTCDate(--day);
+            new_date.setUTCMonth(new_month);
         }
-        
-        return this.groups[groupId] ;
-    }
-    
-    
-});
-/*
- * - LGPL
- *
- * RadioItem
- * 
- */
-
-/**
- * @class Roo.bootstrap.Radio
- * @extends Roo.bootstrap.Component
- * Bootstrap Radio class
- * @cfg {String} boxLabel - the label associated
- * @cfg {String} value - the value of radio
- * 
- * @constructor
- * Create a new Radio
- * @param {Object} config The config object
- */
-Roo.bootstrap.Radio = function(config){
-    Roo.bootstrap.Radio.superclass.constructor.call(this, config);
-    
-};
+        return new_date;
+    },
 
-Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
-    
-    boxLabel : '',
-    
-    value : '',
-    
-    getAutoCreate : function()
+    moveYear: function(date, dir)
     {
-        var cfg = {
-            tag : 'div',
-            cls : 'form-group radio',
-            cn : [
-                {
-                    tag : 'label',
-                    cls : 'box-label',
-                    html : this.boxLabel
-                }
-            ]
-        };
-        
-        return cfg;
+        return this.moveMonth(date, dir*12);
     },
-    
-    initEvents : function() 
+
+    dateWithinRange: function(date)
     {
-        this.parent().register(this);
-        
-        this.el.on('click', this.onClick, this);
-        
+        return date >= this.startDate && date <= this.endDate;
     },
+
     
-    onClick : function(e)
+    remove: function() 
     {
-        if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
-            this.setChecked(true);
-        }
+        this.picker().remove();
     },
     
-    setChecked : function(state, suppressEvent)
+    validateValue : function(value)
     {
-        this.parent().setValue(this.value, suppressEvent);
+        if(this.getVisibilityEl().hasClass('hidden')){
+            return true;
+        }
+        
+        if(value.length < 1)  {
+            if(this.allowBlank){
+                return true;
+            }
+            return false;
+        }
+        
+        if(value.length < this.minLength){
+            return false;
+        }
+        if(value.length > this.maxLength){
+            return false;
+        }
+        if(this.vtype){
+            var vt = Roo.form.VTypes;
+            if(!vt[this.vtype](value, this)){
+                return false;
+            }
+        }
+        if(typeof this.validator == "function"){
+            var msg = this.validator(value);
+            if(msg !== true){
+                return false;
+            }
+        }
+        
+        if(this.regex && !this.regex.test(value)){
+            return false;
+        }
+        
+        if(typeof(this.parseDate(value)) == 'undefined'){
+            return false;
+        }
+        
+        if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
+            return false;
+        }      
+        
+        if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
+            return false;
+        } 
+        
         
+        return true;
     },
     
-    setBoxLabel : function(v)
+    reset : function()
     {
-        this.boxLabel = v;
+        this.date = this.viewDate = '';
         
-        if(this.rendered){
-            this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
-        }
+        Roo.bootstrap.DateField.superclass.setValue.call(this, '');
     }
-    
+   
 });
-
- /*
- * - LGPL
- *
- * Input
- * 
- */
 
-/**
- * @class Roo.bootstrap.SecurePass
- * @extends Roo.bootstrap.Input
- * Bootstrap SecurePass class
- *
- * 
- * @constructor
- * Create a new SecurePass
- * @param {Object} config The config object
- */
-Roo.bootstrap.SecurePass = function (config) {
-    // these go here, so the translation tool can replace them..
-    this.errors = {
-        PwdEmpty: "Please type a password, and then retype it to confirm.",
-        PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
-        PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
-        PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
-        IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
-        FNInPwd: "Your password can't contain your first name. Please type a different password.",
-        LNInPwd: "Your password can't contain your last name. Please type a different password.",
-        TooWeak: "Your password is Too Weak."
-    },
-    this.meterLabel = "Password strength:";
-    this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
-    this.meterClass = [
-        "roo-password-meter-tooweak", 
-        "roo-password-meter-weak", 
-        "roo-password-meter-medium", 
-        "roo-password-meter-strong", 
-        "roo-password-meter-grey"
-    ];
-    
-    this.errors = {};
-    
-    Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
-}
-
-Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
-    /**
-     * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
-     * {
-     *  PwdEmpty: "Please type a password, and then retype it to confirm.",
-     *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
-     *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
-     *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
-     *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
-     *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
-     *  LNInPwd: "Your password can't contain your last name. Please type a different password."
-     * })
-     */
-    // private
-    
-    meterWidth: 300,
-    errorMsg :'',    
-    errors: false,
-    imageRoot: '/',
-    /**
-     * @cfg {String/Object} Label for the strength meter (defaults to
-     * 'Password strength:')
-     */
-    // private
-    meterLabel: '',
-    /**
-     * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
-     * ['Weak', 'Medium', 'Strong'])
-     */
-    // private    
-    pwdStrengths: false,    
-    // private
-    strength: 0,
-    // private
-    _lastPwd: null,
-    // private
-    kCapitalLetter: 0,
-    kSmallLetter: 1,
-    kDigit: 2,
-    kPunctuation: 3,
+Roo.apply(Roo.bootstrap.DateField,  {
     
-    insecure: false,
-    // private
-    initEvents: function ()
-    {
-        Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
+    head : {
+        tag: 'thead',
+        cn: [
+        {
+            tag: 'tr',
+            cn: [
+            {
+                tag: 'th',
+                cls: 'prev',
+                html: '<i class="fa fa-arrow-left"/>'
+            },
+            {
+                tag: 'th',
+                cls: 'switch',
+                colspan: '5'
+            },
+            {
+                tag: 'th',
+                cls: 'next',
+                html: '<i class="fa fa-arrow-right"/>'
+            }
 
-        if (this.el.is('input[type=password]') && Roo.isSafari) {
-            this.el.on('keydown', this.SafariOnKeyDown, this);
+            ]
         }
-
-        this.el.on('keyup', this.checkStrength, this, {buffer: 50});
+        ]
     },
-    // private
-    onRender: function (ct, position)
-    {
-        Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
-        this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
-        this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
-
-        this.trigger.createChild({
-                  cn: [
-                    {
-                    //id: 'PwdMeter',
-                    tag: 'div',
-                    cls: 'roo-password-meter-grey col-xs-12',
-                    style: {
-                        //width: 0,
-                        //width: this.meterWidth + 'px'                                                
-                        }
-                    },
-                    {                           
-                        cls: 'roo-password-meter-text'                          
-                    }
-                ]            
-        });
-
-         
-        if (this.hideTrigger) {
-            this.trigger.setDisplayed(false);
+    
+    content : {
+        tag: 'tbody',
+        cn: [
+        {
+            tag: 'tr',
+            cn: [
+            {
+                tag: 'td',
+                colspan: '7'
+            }
+            ]
         }
-        this.setSize(this.width || '', this.height || '');
+        ]
     },
-    // private
-    onDestroy: function ()
-    {
-        if (this.trigger) {
-            this.trigger.removeAllListeners();
-            this.trigger.remove();
+    
+    footer : {
+        tag: 'tfoot',
+        cn: [
+        {
+            tag: 'tr',
+            cn: [
+            {
+                tag: 'th',
+                colspan: '7',
+                cls: 'today'
+            }
+                    
+            ]
         }
-        if (this.wrap) {
-            this.wrap.remove();
+        ]
+    },
+    
+    dates:{
+        en: {
+            days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+            daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+            daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+            months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+            monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+            today: "Today"
         }
-        Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
     },
-    // private
-    checkStrength: function ()
+    
+    modes: [
     {
-        var pwd = this.inputEl().getValue();
-        if (pwd == this._lastPwd) {
-            return;
-        }
-
-        var strength;
-        if (this.ClientSideStrongPassword(pwd)) {
-            strength = 3;
-        } else if (this.ClientSideMediumPassword(pwd)) {
-            strength = 2;
-        } else if (this.ClientSideWeakPassword(pwd)) {
-            strength = 1;
-        } else {
-            strength = 0;
-        }
-        
-        Roo.log('strength1: ' + strength);
-        
-        //var pm = this.trigger.child('div/div/div').dom;
-        var pm = this.trigger.child('div/div');
-        pm.removeClass(this.meterClass);
-        pm.addClass(this.meterClass[strength]);
-                
-        
-        var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
-                
-        pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
-        
-        this._lastPwd = pwd;
+        clsName: 'days',
+        navFnc: 'Month',
+        navStep: 1
     },
-    reset: function ()
     {
-        Roo.bootstrap.SecurePass.superclass.reset.call(this);
-        
-        this._lastPwd = '';
-        
-        var pm = this.trigger.child('div/div');
-        pm.removeClass(this.meterClass);
-        pm.addClass('roo-password-meter-grey');        
-        
-        
-        var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
-        
-        pt.innerHTML = '';
-        this.inputEl().dom.type='password';
+        clsName: 'months',
+        navFnc: 'FullYear',
+        navStep: 1
     },
-    // private
-    validateValue: function (value)
     {
-        if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
-            return false;
-        }
-        if (value.length == 0) {
-            if (this.allowBlank) {
-                this.clearInvalid();
-                return true;
-            }
+        clsName: 'years',
+        navFnc: 'FullYear',
+        navStep: 10
+    }]
+});
 
-            this.markInvalid(this.errors.PwdEmpty);
-            this.errorMsg = this.errors.PwdEmpty;
-            return false;
-        }
-        
-        if(this.insecure){
-            return true;
-        }
-        
-        if (!value.match(/[\x21-\x7e]+/)) {
-            this.markInvalid(this.errors.PwdBadChar);
-            this.errorMsg = this.errors.PwdBadChar;
-            return false;
-        }
-        if (value.length < 6) {
-            this.markInvalid(this.errors.PwdShort);
-            this.errorMsg = this.errors.PwdShort;
-            return false;
-        }
-        if (value.length > 16) {
-            this.markInvalid(this.errors.PwdLong);
-            this.errorMsg = this.errors.PwdLong;
-            return false;
-        }
-        var strength;
-        if (this.ClientSideStrongPassword(value)) {
-            strength = 3;
-        } else if (this.ClientSideMediumPassword(value)) {
-            strength = 2;
-        } else if (this.ClientSideWeakPassword(value)) {
-            strength = 1;
-        } else {
-            strength = 0;
+Roo.apply(Roo.bootstrap.DateField,  {
+  
+    template : {
+        tag: 'div',
+        cls: 'datepicker dropdown-menu roo-dynamic shadow',
+        cn: [
+        {
+            tag: 'div',
+            cls: 'datepicker-days',
+            cn: [
+            {
+                tag: 'table',
+                cls: 'table-condensed',
+                cn:[
+                Roo.bootstrap.DateField.head,
+                {
+                    tag: 'tbody'
+                },
+                Roo.bootstrap.DateField.footer
+                ]
+            }
+            ]
+        },
+        {
+            tag: 'div',
+            cls: 'datepicker-months',
+            cn: [
+            {
+                tag: 'table',
+                cls: 'table-condensed',
+                cn:[
+                Roo.bootstrap.DateField.head,
+                Roo.bootstrap.DateField.content,
+                Roo.bootstrap.DateField.footer
+                ]
+            }
+            ]
+        },
+        {
+            tag: 'div',
+            cls: 'datepicker-years',
+            cn: [
+            {
+                tag: 'table',
+                cls: 'table-condensed',
+                cn:[
+                Roo.bootstrap.DateField.head,
+                Roo.bootstrap.DateField.content,
+                Roo.bootstrap.DateField.footer
+                ]
+            }
+            ]
         }
+        ]
+    }
+});
+
+
+ /*
+ * - LGPL
+ *
+ * TimeField
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.TimeField
+ * @extends Roo.bootstrap.Input
+ * Bootstrap DateField class
+ * 
+ * 
+ * @constructor
+ * Create a new TimeField
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TimeField = function(config){
+    Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
+    this.addEvents({
+            /**
+             * @event show
+             * Fires when this field show.
+             * @param {Roo.bootstrap.DateField} thisthis
+             * @param {Mixed} date The date value
+             */
+            show : true,
+            /**
+             * @event show
+             * Fires when this field hide.
+             * @param {Roo.bootstrap.DateField} this
+             * @param {Mixed} date The date value
+             */
+            hide : true,
+            /**
+             * @event select
+             * Fires when select a date.
+             * @param {Roo.bootstrap.DateField} this
+             * @param {Mixed} date The date value
+             */
+            select : true
+        });
+};
 
+Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
+    
+    /**
+     * @cfg {String} format
+     * The default time format string which can be overriden for localization support.  The format must be
+     * valid according to {@link Date#parseDate} (defaults to 'H:i').
+     */
+    format : "H:i",
+
+    getAutoCreate : function()
+    {
+        this.after = '<i class="fa far fa-clock"></i>';
+        return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
         
-        if (strength < 2) {
-            //this.markInvalid(this.errors.TooWeak);
-            this.errorMsg = this.errors.TooWeak;
-            //return false;
-        }
+         
+    },
+    onRender: function(ct, position)
+    {
         
+        Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
+                
+        this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
         
-        console.log('strength2: ' + strength);
+        this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
         
-        //var pm = this.trigger.child('div/div/div').dom;
+        this.pop = this.picker().select('>.datepicker-time',true).first();
+        this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
         
-        var pm = this.trigger.child('div/div');
-        pm.removeClass(this.meterClass);
-        pm.addClass(this.meterClass[strength]);
-                
-        var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
-                
-        pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
+        this.picker().on('mousedown', this.onMousedown, this);
+        this.picker().on('click', this.onClick, this);
         
-        this.errorMsg = ''; 
-        return true;
-    },
-    // private
-    CharacterSetChecks: function (type)
-    {
-        this.type = type;
-        this.fResult = false;
+        this.picker().addClass('datepicker-dropdown');
+    
+        this.fillTime();
+        this.update();
+            
+        this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
+        this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
+        this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
+        this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
+        this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
+        this.pop.select('button.ok', true).first().on('click', this.setTime, this);
+
     },
-    // private
-    isctype: function (character, type)
-    {
-        switch (type) {  
-            case this.kCapitalLetter:
-                if (character >= 'A' && character <= 'Z') {
-                    return true;
-                }
+    
+    fireKey: function(e){
+        if (!this.picker().isVisible()){
+            if (e.keyCode == 27) { // allow escape to hide and re-show picker
+                this.show();
+            }
+            return;
+        }
+
+        e.preventDefault();
+        
+        switch(e.keyCode){
+            case 27: // escape
+                this.hide();
                 break;
-            
-            case this.kSmallLetter:
-                if (character >= 'a' && character <= 'z') {
-                    return true;
-                }
+            case 37: // left
+            case 39: // right
+                this.onTogglePeriod();
                 break;
-            
-            case this.kDigit:
-                if (character >= '0' && character <= '9') {
-                    return true;
-                }
+            case 38: // up
+                this.onIncrementMinutes();
                 break;
-            
-            case this.kPunctuation:
-                if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
-                    return true;
-                }
+            case 40: // down
+                this.onDecrementMinutes();
+                break;
+            case 13: // enter
+            case 9: // tab
+                this.setTime();
                 break;
-            
-            default:
-                return false;
         }
-
     },
-    // private
-    IsLongEnough: function (pwd, size)
-    {
-        return !(pwd == null || isNaN(size) || pwd.length < size);
+    
+    onClick: function(e) {
+        e.stopPropagation();
+        e.preventDefault();
     },
-    // private
-    SpansEnoughCharacterSets: function (word, nb)
+    
+    picker : function()
     {
-        if (!this.IsLongEnough(word, nb))
-        {
-            return false;
-        }
-
-        var characterSetChecks = new Array(
-            new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
-            new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
-        );
+        return this.pickerEl;
+    },
+    
+    fillTime: function()
+    {    
+        var time = this.pop.select('tbody', true).first();
         
-        for (var index = 0; index < word.length; ++index) {
-            for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
-                if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
-                    characterSetChecks[nCharSet].fResult = true;
-                    break;
-                }
-            }
+        time.dom.innerHTML = '';
+        
+        time.createChild({
+            tag: 'tr',
+            cn: [
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'a',
+                            href: '#',
+                            cls: 'btn',
+                            cn: [
+                                {
+                                    tag: 'i',
+                                    cls: 'hours-up fa fas fa-chevron-up'
+                                }
+                            ]
+                        } 
+                    ]
+                },
+                {
+                    tag: 'td',
+                    cls: 'separator'
+                },
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'a',
+                            href: '#',
+                            cls: 'btn',
+                            cn: [
+                                {
+                                    tag: 'i',
+                                    cls: 'minutes-up fa fas fa-chevron-up'
+                                }
+                            ]
+                        }
+                    ]
+                },
+                {
+                    tag: 'td',
+                    cls: 'separator'
+                }
+            ]
+        });
+        
+        time.createChild({
+            tag: 'tr',
+            cn: [
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'span',
+                            cls: 'timepicker-hour',
+                            html: '00'
+                        }  
+                    ]
+                },
+                {
+                    tag: 'td',
+                    cls: 'separator',
+                    html: ':'
+                },
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'span',
+                            cls: 'timepicker-minute',
+                            html: '00'
+                        }  
+                    ]
+                },
+                {
+                    tag: 'td',
+                    cls: 'separator'
+                },
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'button',
+                            type: 'button',
+                            cls: 'btn btn-primary period',
+                            html: 'AM'
+                            
+                        }
+                    ]
+                }
+            ]
+        });
+        
+        time.createChild({
+            tag: 'tr',
+            cn: [
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'a',
+                            href: '#',
+                            cls: 'btn',
+                            cn: [
+                                {
+                                    tag: 'span',
+                                    cls: 'hours-down fa fas fa-chevron-down'
+                                }
+                            ]
+                        }
+                    ]
+                },
+                {
+                    tag: 'td',
+                    cls: 'separator'
+                },
+                {
+                    tag: 'td',
+                    cn: [
+                        {
+                            tag: 'a',
+                            href: '#',
+                            cls: 'btn',
+                            cn: [
+                                {
+                                    tag: 'span',
+                                    cls: 'minutes-down fa fas fa-chevron-down'
+                                }
+                            ]
+                        }
+                    ]
+                },
+                {
+                    tag: 'td',
+                    cls: 'separator'
+                }
+            ]
+        });
+        
+    },
+    
+    update: function()
+    {
+        
+        this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
+        
+        this.fill();
+    },
+    
+    fill: function() 
+    {
+        var hours = this.time.getHours();
+        var minutes = this.time.getMinutes();
+        var period = 'AM';
+        
+        if(hours > 11){
+            period = 'PM';
         }
-
-        var nCharSets = 0;
-        for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
-            if (characterSetChecks[nCharSet].fResult) {
-                ++nCharSets;
-            }
+        
+        if(hours == 0){
+            hours = 12;
         }
-
-        if (nCharSets < nb) {
-            return false;
+        
+        
+        if(hours > 12){
+            hours = hours - 12;
         }
-        return true;
+        
+        if(hours < 10){
+            hours = '0' + hours;
+        }
+        
+        if(minutes < 10){
+            minutes = '0' + minutes;
+        }
+        
+        this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
+        this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
+        this.pop.select('button', true).first().dom.innerHTML = period;
+        
     },
-    // private
-    ClientSideStrongPassword: function (pwd)
-    {
-        return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
+    
+    place: function()
+    {   
+        this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
+        
+        var cls = ['bottom'];
+        
+        if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
+            cls.pop();
+            cls.push('top');
+        }
+        
+        cls.push('right');
+        
+        if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
+            cls.pop();
+            cls.push('left');
+        }
+        //this.picker().setXY(20000,20000);
+        this.picker().addClass(cls.join('-'));
+        
+        var _this = this;
+        
+        Roo.each(cls, function(c){
+            if(c == 'bottom'){
+                (function() {
+                 //  
+                }).defer(200);
+                 _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
+                //_this.picker().setTop(_this.inputEl().getHeight());
+                return;
+            }
+            if(c == 'top'){
+                 _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
+                
+                //_this.picker().setTop(0 - _this.picker().getHeight());
+                return;
+            }
+            /*
+            if(c == 'left'){
+                _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
+                return;
+            }
+            if(c == 'right'){
+                _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
+                return;
+            }
+            */
+        });
+        
     },
-    // private
-    ClientSideMediumPassword: function (pwd)
+  
+    onFocus : function()
     {
-        return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
+        Roo.bootstrap.TimeField.superclass.onFocus.call(this);
+        this.show();
     },
-    // private
-    ClientSideWeakPassword: function (pwd)
+    
+    onBlur : function()
     {
-        return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
+        Roo.bootstrap.TimeField.superclass.onBlur.call(this);
+        this.hide();
+    },
+    
+    show : function()
+    {
+        this.picker().show();
+        this.pop.show();
+        this.update();
+        this.place();
+        
+        this.fireEvent('show', this, this.date);
+    },
+    
+    hide : function()
+    {
+        this.picker().hide();
+        this.pop.hide();
+        
+        this.fireEvent('hide', this, this.date);
+    },
+    
+    setTime : function()
+    {
+        this.hide();
+        this.setValue(this.time.format(this.format));
+        
+        this.fireEvent('select', this, this.date);
+        
+        
+    },
+    
+    onMousedown: function(e){
+        e.stopPropagation();
+        e.preventDefault();
+    },
+    
+    onIncrementHours: function()
+    {
+        Roo.log('onIncrementHours');
+        this.time = this.time.add(Date.HOUR, 1);
+        this.update();
+        
+    },
+    
+    onDecrementHours: function()
+    {
+        Roo.log('onDecrementHours');
+        this.time = this.time.add(Date.HOUR, -1);
+        this.update();
+    },
+    
+    onIncrementMinutes: function()
+    {
+        Roo.log('onIncrementMinutes');
+        this.time = this.time.add(Date.MINUTE, 1);
+        this.update();
+    },
+    
+    onDecrementMinutes: function()
+    {
+        Roo.log('onDecrementMinutes');
+        this.time = this.time.add(Date.MINUTE, -1);
+        this.update();
+    },
+    
+    onTogglePeriod: function()
+    {
+        Roo.log('onTogglePeriod');
+        this.time = this.time.add(Date.HOUR, 12);
+        this.update();
     }
-          
-})//<script type="text/javascript">
+    
+   
+});
 
-/*
- * Based  Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- * LGPL
+Roo.apply(Roo.bootstrap.TimeField,  {
+  
+    template : {
+        tag: 'div',
+        cls: 'datepicker dropdown-menu',
+        cn: [
+            {
+                tag: 'div',
+                cls: 'datepicker-time',
+                cn: [
+                {
+                    tag: 'table',
+                    cls: 'table-condensed',
+                    cn:[
+                        {
+                            tag: 'tbody',
+                            cn: [
+                                {
+                                    tag: 'tr',
+                                    cn: [
+                                    {
+                                        tag: 'td',
+                                        colspan: '7'
+                                    }
+                                    ]
+                                }
+                            ]
+                        },
+                        {
+                            tag: 'tfoot',
+                            cn: [
+                                {
+                                    tag: 'tr',
+                                    cn: [
+                                    {
+                                        tag: 'th',
+                                        colspan: '7',
+                                        cls: '',
+                                        cn: [
+                                            {
+                                                tag: 'button',
+                                                cls: 'btn btn-info ok',
+                                                html: 'OK'
+                                            }
+                                        ]
+                                    }
+                    
+                                    ]
+                                }
+                            ]
+                        }
+                    ]
+                }
+                ]
+            }
+        ]
+    }
+});
+
+
+ /*
+ * - LGPL
  *
+ * MonthField
+ * 
  */
+
 /**
- * @class Roo.HtmlEditorCore
- * @extends Roo.Component
- * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
- *
- * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
+ * @class Roo.bootstrap.MonthField
+ * @extends Roo.bootstrap.Input
+ * Bootstrap MonthField class
+ * 
+ * @cfg {String} language default en
+ * 
+ * @constructor
+ * Create a new MonthField
+ * @param {Object} config The config object
  */
 
-Roo.HtmlEditorCore = function(config){
-    
-    
-    Roo.HtmlEditorCore.superclass.constructor.call(this, config);
-    
+Roo.bootstrap.MonthField = function(config){
+    Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
     
     this.addEvents({
         /**
-         * @event initialize
-         * Fires when the editor is fully initialized (including the iframe)
-         * @param {Roo.HtmlEditorCore} this
+         * @event show
+         * Fires when this field show.
+         * @param {Roo.bootstrap.MonthField} this
+         * @param {Mixed} date The date value
          */
-        initialize: true,
+        show : true,
         /**
-         * @event activate
-         * Fires when the editor is first receives the focus. Any insertion must wait
-         * until after this event.
-         * @param {Roo.HtmlEditorCore} this
-         */
-        activate: true,
-         /**
-         * @event beforesync
-         * Fires before the textarea is updated with content from the editor iframe. Return false
-         * to cancel the sync.
-         * @param {Roo.HtmlEditorCore} this
-         * @param {String} html
-         */
-        beforesync: true,
-         /**
-         * @event beforepush
-         * Fires before the iframe editor is updated with content from the textarea. Return false
-         * to cancel the push.
-         * @param {Roo.HtmlEditorCore} this
-         * @param {String} html
-         */
-        beforepush: true,
-         /**
-         * @event sync
-         * Fires when the textarea is updated with content from the editor iframe.
-         * @param {Roo.HtmlEditorCore} this
-         * @param {String} html
-         */
-        sync: true,
-         /**
-         * @event push
-         * Fires when the iframe editor is updated with content from the textarea.
-         * @param {Roo.HtmlEditorCore} this
-         * @param {String} html
+         * @event show
+         * Fires when this field hide.
+         * @param {Roo.bootstrap.MonthField} this
+         * @param {Mixed} date The date value
          */
-        push: true,
-        
+        hide : true,
         /**
-         * @event editorevent
-         * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
-         * @param {Roo.HtmlEditorCore} this
+         * @event select
+         * Fires when select a date.
+         * @param {Roo.bootstrap.MonthField} this
+         * @param {String} oldvalue The old value
+         * @param {String} newvalue The new value
          */
-        editorevent: true
-        
+        select : true
     });
-    
-    // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
-    
-    // defaults : white / black...
-    this.applyBlacklists();
-    
-    
-    
 };
 
-
-Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
-
-
-     /**
-     * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
-     */
-    
-    owner : false,
+Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
     
-     /**
-     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
-     *                        Roo.resizable.
-     */
-    resizable : false,
-     /**
-     * @cfg {Number} height (in pixels)
-     */   
-    height: 300,
-   /**
-     * @cfg {Number} width (in pixels)
-     */   
-    width: 500,
-    
-    /**
-     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
-     * 
-     */
-    stylesheets: false,
-    
-    // id of frame..
-    frameId: false,
-    
-    // private properties
-    validationEvent : false,
-    deferHeight: true,
-    initialized : false,
-    activated : false,
-    sourceEditMode : false,
-    onFocus : Roo.emptyFn,
-    iframePad:3,
-    hideMode:'offsets',
-    
-    clearUp: true,
-    
-    // blacklist + whitelisted elements..
-    black: false,
-    white: false,
-     
-    bodyCls : '',
-
-    /**
-     * Protected method that will not generally be called directly. It
-     * is called when the editor initializes the iframe with HTML contents. Override this method if you
-     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
-     */
-    getDocMarkup : function(){
-        // body styles..
-        var st = '';
-        
-        // inherit styels from page...?? 
-        if (this.stylesheets === false) {
-            
-            Roo.get(document.head).select('style').each(function(node) {
-                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
-            });
-            
-            Roo.get(document.head).select('link').each(function(node) { 
-                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
-            });
-            
-        } else if (!this.stylesheets.length) {
-                // simple..
-                st = '<style type="text/css">' +
-                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
-                   '</style>';
-        } else {
-            for (var i in this.stylesheets) { 
-                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
-            }
-            
-        }
+    onRender: function(ct, position)
+    {
         
-        st +=  '<style type="text/css">' +
-            'IMG { cursor: pointer } ' +
-        '</style>';
-
-        var cls = 'roo-htmleditor-body';
+        Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
         
-        if(this.bodyCls.length){
-            cls += ' ' + this.bodyCls;
-        }
+        this.language = this.language || 'en';
+        this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
+        this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
         
-        return '<html><head>' + st  +
-            //<style type="text/css">' +
-            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
-            //'</style>' +
-            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
-    },
-
-    // private
-    onRender : function(ct, position)
-    {
-        var _t = this;
-        //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
-        this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
+        this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
+        this.isInline = false;
+        this.isInput = true;
+        this.component = this.el.select('.add-on', true).first() || false;
+        this.component = (this.component && this.component.length === 0) ? false : this.component;
+        this.hasInput = this.component && this.inputEL().length;
         
+        this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
         
-        this.el.dom.style.border = '0 none';
-        this.el.dom.setAttribute('tabIndex', -1);
-        this.el.addClass('x-hidden hide');
+        this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
         
+        this.picker().on('mousedown', this.onMousedown, this);
+        this.picker().on('click', this.onClick, this);
         
+        this.picker().addClass('datepicker-dropdown');
         
-        if(Roo.isIE){ // fix IE 1px bogus margin
-            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
-        }
-       
+        Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
+            v.setStyle('width', '189px');
+        });
         
-        this.frameId = Roo.id();
+        this.fillMonths();
         
-         
+        this.update();
         
-        var iframe = this.owner.wrap.createChild({
-            tag: 'iframe',
-            cls: 'form-control', // bootstrap..
-            id: this.frameId,
-            name: this.frameId,
-            frameBorder : 'no',
-            'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
-        }, this.el
-        );
+        if(this.isInline) {
+            this.show();
+        }
         
+    },
+    
+    setValue: function(v, suppressEvent)
+    {   
+        var o = this.getValue();
         
-        this.iframe = iframe.dom;
-
-         this.assignDocWin();
+        Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
         
-        this.doc.designMode = 'on';
-       
-        this.doc.open();
-        this.doc.write(this.getDocMarkup());
-        this.doc.close();
+        this.update();
 
+        if(suppressEvent !== true){
+            this.fireEvent('select', this, o, v);
+        }
         
-        var task = { // must defer to wait for browser to be ready
-            run : function(){
-                //console.log("run task?" + this.doc.readyState);
-                this.assignDocWin();
-                if(this.doc.body || this.doc.readyState == 'complete'){
-                    try {
-                        this.doc.designMode="on";
-                    } catch (e) {
-                        return;
-                    }
-                    Roo.TaskMgr.stop(task);
-                    this.initEditor.defer(10, this);
-                }
-            },
-            interval : 10,
-            duration: 10000,
-            scope: this
-        };
-        Roo.TaskMgr.start(task);
-
     },
-
-    // private
-    onResize : function(w, h)
+    
+    getValue: function()
     {
-         Roo.log('resize: ' +w + ',' + h );
-        //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
-        if(!this.iframe){
-            return;
-        }
-        if(typeof w == 'number'){
-            
-            this.iframe.style.width = w + 'px';
+        return this.value;
+    },
+    
+    onClick: function(e) 
+    {
+        e.stopPropagation();
+        e.preventDefault();
+        
+        var target = e.getTarget();
+        
+        if(target.nodeName.toLowerCase() === 'i'){
+            target = Roo.get(target).dom.parentNode;
         }
-        if(typeof h == 'number'){
-            
-            this.iframe.style.height = h + 'px';
-            if(this.doc){
-                (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
-            }
+        
+        var nodeName = target.nodeName;
+        var className = target.className;
+        var html = target.innerHTML;
+        
+        if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
+            return;
         }
         
+        this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
+        
+        this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
+        
+        this.hide();
+                        
     },
-
-    /**
-     * Toggles the editor between standard and source edit mode.
-     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
-     */
-    toggleSourceEdit : function(sourceEditMode){
+    
+    picker : function()
+    {
+        return this.pickerEl;
+    },
+    
+    fillMonths: function()
+    {    
+        var i = 0;
+        var months = this.picker().select('>.datepicker-months td', true).first();
         
-        this.sourceEditMode = sourceEditMode === true;
+        months.dom.innerHTML = '';
         
-        if(this.sourceEditMode){
-            Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
+        while (i < 12) {
+            var month = {
+                tag: 'span',
+                cls: 'month',
+                html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
+            };
             
-        }else{
-            Roo.get(this.iframe).removeClass(['x-hidden','hide']);
-            //this.iframe.className = '';
-            this.deferFocus();
+            months.createChild(month);
         }
-        //this.setSize(this.owner.wrap.getSize());
-        //this.fireEvent('editmodechange', this, this.sourceEditMode);
+        
     },
-
     
-  
-
-    /**
-     * Protected method that will not generally be called directly. If you need/want
-     * custom HTML cleanup, this is the method you should override.
-     * @param {String} html The HTML to be cleaned
-     * return {String} The cleaned HTML
-     */
-    cleanHtml : function(html){
-        html = String(html);
-        if(html.length > 5){
-            if(Roo.isSafari){ // strip safari nonsense
-                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
-            }
-        }
-        if(html == '&nbsp;'){
-            html = '';
-        }
-        return html;
-    },
-
-    /**
-     * HTML Editor -> Textarea
-     * Protected method that will not generally be called directly. Syncs the contents
-     * of the editor iframe with the textarea.
-     */
-    syncValue : function(){
-        if(this.initialized){
-            var bd = (this.doc.body || this.doc.documentElement);
-            //this.cleanUpPaste(); -- this is done else where and causes havoc..
-            var html = bd.innerHTML;
-            if(Roo.isSafari){
-                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
-                var m = bs ? bs.match(/text-align:(.*?);/i) : false;
-                if(m && m[1]){
-                    html = '<div style="'+m[0]+'">' + html + '</div>';
-                }
-            }
-            html = this.cleanHtml(html);
-            // fix up the special chars.. normaly like back quotes in word...
-            // however we do not want to do this with chinese..
-            html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
-                
-                var cc = match.charCodeAt();
-
-                // Get the character value, handling surrogate pairs
-                if (match.length == 2) {
-                    // It's a surrogate pair, calculate the Unicode code point
-                    var high = match.charCodeAt(0) - 0xD800;
-                    var low  = match.charCodeAt(1) - 0xDC00;
-                    cc = (high * 0x400) + low + 0x10000;
-                }  else if (
-                    (cc >= 0x4E00 && cc < 0xA000 ) ||
-                    (cc >= 0x3400 && cc < 0x4E00 ) ||
-                    (cc >= 0xf900 && cc < 0xfb00 )
-                ) {
-                        return match;
-                }  
-         
-                // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
-                return "&#" + cc + ";";
-                
-                
-            });
-            
-            
-             
-            if(this.owner.fireEvent('beforesync', this, html) !== false){
-                this.el.dom.value = html;
-                this.owner.fireEvent('sync', this, html);
-            }
+    update: function()
+    {
+        var _this = this;
+        
+        if(typeof(this.vIndex) == 'undefined' && this.value.length){
+            this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
         }
-    },
-
-    /**
-     * Protected method that will not generally be called directly. Pushes the value of the textarea
-     * into the iframe editor.
-     */
-    pushValue : function(){
-        if(this.initialized){
-            var v = this.el.dom.value.trim();
-            
-//            if(v.length < 1){
-//                v = '&#160;';
-//            }
+        
+        Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
+            e.removeClass('active');
             
-            if(this.owner.fireEvent('beforepush', this, v) !== false){
-                var d = (this.doc.body || this.doc.documentElement);
-                d.innerHTML = v;
-                this.cleanUpPaste();
-                this.el.dom.value = d.innerHTML;
-                this.owner.fireEvent('push', this, v);
+            if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
+                e.addClass('active');
             }
-        }
-    },
-
-    // private
-    deferFocus : function(){
-        this.focus.defer(10, this);
-    },
-
-    // doc'ed in Field
-    focus : function(){
-        if(this.win && !this.sourceEditMode){
-            this.win.focus();
-        }else{
-            this.el.focus();
-        }
+        })
     },
     
-    assignDocWin: function()
+    place: function()
     {
-        var iframe = this.iframe;
+        if(this.isInline) {
+            return;
+        }
         
-         if(Roo.isIE){
-            this.doc = iframe.contentWindow.document;
-            this.win = iframe.contentWindow;
-        } else {
-//            if (!Roo.get(this.frameId)) {
-//                return;
-//            }
-//            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
-//            this.win = Roo.get(this.frameId).dom.contentWindow;
+        this.picker().removeClass(['bottom', 'top']);
+        
+        if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
+            /*
+             * place to the top of element!
+             *
+             */
             
-            if (!Roo.get(this.frameId) && !iframe.contentDocument) {
-                return;
-            }
+            this.picker().addClass('top');
+            this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
             
-            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
-            this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
+            return;
         }
-    },
-    
-    // private
-    initEditor : function(){
-        //console.log("INIT EDITOR");
-        this.assignDocWin();
-        
         
+        this.picker().addClass('bottom');
         
-        this.doc.designMode="on";
-        this.doc.open();
-        this.doc.write(this.getDocMarkup());
-        this.doc.close();
-        
-        var dbody = (this.doc.body || this.doc.documentElement);
-        //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
-        // this copies styles from the containing element into thsi one..
-        // not sure why we need all of this..
-        //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
-        
-        //var ss = this.el.getStyles( 'background-image', 'background-repeat');
-        //ss['background-attachment'] = 'fixed'; // w3c
-        dbody.bgProperties = 'fixed'; // ie
-        //Roo.DomHelper.applyStyles(dbody, ss);
-        Roo.EventManager.on(this.doc, {
-            //'mousedown': this.onEditorEvent,
-            'mouseup': this.onEditorEvent,
-            'dblclick': this.onEditorEvent,
-            'click': this.onEditorEvent,
-            'keyup': this.onEditorEvent,
-            buffer:100,
-            scope: this
-        });
-        if(Roo.isGecko){
-            Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
-        }
-        if(Roo.isIE || Roo.isSafari || Roo.isOpera){
-            Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
-        }
-        this.initialized = true;
-
-        this.owner.fireEvent('initialize', this);
-        this.pushValue();
+        this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
     },
-
-    // private
-    onDestroy : function(){
+    
+    onFocus : function()
+    {
+        Roo.bootstrap.MonthField.superclass.onFocus.call(this);
+        this.show();
+    },
+    
+    onBlur : function()
+    {
+        Roo.bootstrap.MonthField.superclass.onBlur.call(this);
         
+        var d = this.inputEl().getValue();
         
+        this.setValue(d);
+                
+        this.hide();
+    },
+    
+    show : function()
+    {
+        this.picker().show();
+        this.picker().select('>.datepicker-months', true).first().show();
+        this.update();
+        this.place();
         
-        if(this.rendered){
-            
-            //for (var i =0; i < this.toolbars.length;i++) {
-            //    // fixme - ask toolbars for heights?
-            //    this.toolbars[i].onDestroy();
-           // }
-            
-            //this.wrap.dom.innerHTML = '';
-            //this.wrap.remove();
+        this.fireEvent('show', this, this.date);
+    },
+    
+    hide : function()
+    {
+        if(this.isInline) {
+            return;
         }
+        this.picker().hide();
+        this.fireEvent('hide', this, this.date);
+        
+    },
+    
+    onMousedown: function(e)
+    {
+        e.stopPropagation();
+        e.preventDefault();
+    },
+    
+    keyup: function(e)
+    {
+        Roo.bootstrap.MonthField.superclass.keyup.call(this);
+        this.update();
     },
 
-    // private
-    onFirstFocus : function(){
-        
-        this.assignDocWin();
+    fireKey: function(e)
+    {
+        if (!this.picker().isVisible()){
+            if (e.keyCode == 27)   {// allow escape to hide and re-show picker
+                this.show();
+            }
+            return;
+        }
         
+        var dir;
         
-        this.activated = true;
-         
-    
-        if(Roo.isGecko){ // prevent silly gecko errors
-            this.win.focus();
-            var s = this.win.getSelection();
-            if(!s.focusNode || s.focusNode.nodeType != 3){
-                var r = s.getRangeAt(0);
-                r.selectNodeContents((this.doc.body || this.doc.documentElement));
-                r.collapse(true);
-                this.deferFocus();
-            }
-            try{
-                this.execCmd('useCSS', true);
-                this.execCmd('styleWithCSS', false);
-            }catch(e){}
-        }
-        this.owner.fireEvent('activate', this);
-    },
-
-    // private
-    adjustFont: function(btn){
-        var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
-        //if(Roo.isSafari){ // safari
-        //    adjust *= 2;
-       // }
-        var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
-        if(Roo.isSafari){ // safari
-            var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
-            v =  (v < 10) ? 10 : v;
-            v =  (v > 48) ? 48 : v;
-            v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
-            
+        switch(e.keyCode){
+            case 27: // escape
+                this.hide();
+                e.preventDefault();
+                break;
+            case 37: // left
+            case 39: // right
+                dir = e.keyCode == 37 ? -1 : 1;
+                
+                this.vIndex = this.vIndex + dir;
+                
+                if(this.vIndex < 0){
+                    this.vIndex = 0;
+                }
+                
+                if(this.vIndex > 11){
+                    this.vIndex = 11;
+                }
+                
+                if(isNaN(this.vIndex)){
+                    this.vIndex = 0;
+                }
+                
+                this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
+                
+                break;
+            case 38: // up
+            case 40: // down
+                
+                dir = e.keyCode == 38 ? -1 : 1;
+                
+                this.vIndex = this.vIndex + dir * 4;
+                
+                if(this.vIndex < 0){
+                    this.vIndex = 0;
+                }
+                
+                if(this.vIndex > 11){
+                    this.vIndex = 11;
+                }
+                
+                if(isNaN(this.vIndex)){
+                    this.vIndex = 0;
+                }
+                
+                this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
+                break;
+                
+            case 13: // enter
+                
+                if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
+                    this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
+                }
+                
+                this.hide();
+                e.preventDefault();
+                break;
+            case 9: // tab
+                if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
+                    this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
+                }
+                this.hide();
+                break;
+            case 16: // shift
+            case 17: // ctrl
+            case 18: // alt
+                break;
+            default :
+                this.hide();
+                
         }
-        
-        
-        v = Math.max(1, v+adjust);
-        
-        this.execCmd('FontSize', v  );
-    },
-
-    onEditorEvent : function(e)
-    {
-        this.owner.fireEvent('editorevent', this, e);
-      //  this.updateToolbar();
-        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
     },
-
-    insertTag : function(tg)
+    
+    remove: function() 
     {
-        // could be a bit smarter... -> wrap the current selected tRoo..
-        if (tg.toLowerCase() == 'span' ||
-            tg.toLowerCase() == 'code' ||
-            tg.toLowerCase() == 'sup' ||
-            tg.toLowerCase() == 'sub' 
-            ) {
-            
-            range = this.createRange(this.getSelection());
-            var wrappingNode = this.doc.createElement(tg.toLowerCase());
-            wrappingNode.appendChild(range.extractContents());
-            range.insertNode(wrappingNode);
+        this.picker().remove();
+    }
+   
+});
 
-            return;
-            
-            
-            
+Roo.apply(Roo.bootstrap.MonthField,  {
+    
+    content : {
+        tag: 'tbody',
+        cn: [
+        {
+            tag: 'tr',
+            cn: [
+            {
+                tag: 'td',
+                colspan: '7'
+            }
+            ]
         }
-        this.execCmd("formatblock",   tg);
-        
+        ]
     },
     
-    insertText : function(txt)
-    {
-        
-        
-        var range = this.createRange();
-        range.deleteContents();
-               //alert(Sender.getAttribute('label'));
-               
-        range.insertNode(this.doc.createTextNode(txt));
-    } ,
-    
-     
+    dates:{
+        en: {
+            months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+            monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+        }
+    }
+});
 
-    /**
-     * Executes a Midas editor command on the editor document and performs necessary focus and
-     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
-     * @param {String} cmd The Midas command
-     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
-     */
-    relayCmd : function(cmd, value){
-        this.win.focus();
-        this.execCmd(cmd, value);
-        this.owner.fireEvent('editorevent', this);
-        //this.updateToolbar();
-        this.owner.deferFocus();
-    },
+Roo.apply(Roo.bootstrap.MonthField,  {
+  
+    template : {
+        tag: 'div',
+        cls: 'datepicker dropdown-menu roo-dynamic',
+        cn: [
+            {
+                tag: 'div',
+                cls: 'datepicker-months',
+                cn: [
+                {
+                    tag: 'table',
+                    cls: 'table-condensed',
+                    cn:[
+                        Roo.bootstrap.DateField.content
+                    ]
+                }
+                ]
+            }
+        ]
+    }
+});
 
-    /**
-     * Executes a Midas editor command directly on the editor document.
-     * For visual commands, you should use {@link #relayCmd} instead.
-     * <b>This should only be called after the editor is initialized.</b>
-     * @param {String} cmd The Midas command
-     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
-     */
-    execCmd : function(cmd, value){
-        this.doc.execCommand(cmd, false, value === undefined ? null : value);
-        this.syncValue();
-    },
  
+
  
+ /*
+ * - LGPL
+ *
+ * CheckBox
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.CheckBox
+ * @extends Roo.bootstrap.Input
+ * Bootstrap CheckBox class
+ * 
+ * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
+ * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
+ * @cfg {String} boxLabel The text that appears beside the checkbox
+ * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
+ * @cfg {Boolean} checked initnal the element
+ * @cfg {Boolean} inline inline the element (default false)
+ * @cfg {String} groupId the checkbox group id // normal just use for checkbox
+ * @cfg {String} tooltip label tooltip
+ * 
+ * @constructor
+ * Create a new CheckBox
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.CheckBox = function(config){
+    Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
    
-    /**
-     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
-     * to insert tRoo.
-     * @param {String} text | dom node.. 
-     */
-    insertAtCursor : function(text)
-    {
-        
-        if(!this.activated){
-            return;
-        }
-        /*
-        if(Roo.isIE){
-            this.win.focus();
-            var r = this.doc.selection.createRange();
-            if(r){
-                r.collapse(true);
-                r.pasteHTML(text);
-                this.syncValue();
-                this.deferFocus();
-            
-            }
-            return;
-        }
+    this.addEvents({
+        /**
+        * @event check
+        * Fires when the element is checked or unchecked.
+        * @param {Roo.bootstrap.CheckBox} this This input
+        * @param {Boolean} checked The new checked value
         */
-        if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
-            this.win.focus();
-            
-            
-            // from jquery ui (MIT licenced)
-            var range, node;
-            var win = this.win;
-            
-            if (win.getSelection && win.getSelection().getRangeAt) {
-                range = win.getSelection().getRangeAt(0);
-                node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
-                range.insertNode(node);
-            } else if (win.document.selection && win.document.selection.createRange) {
-                // no firefox support
-                var txt = typeof(text) == 'string' ? text : text.outerHTML;
-                win.document.selection.createRange().pasteHTML(txt);
-            } else {
-                // no firefox support
-                var txt = typeof(text) == 'string' ? text : text.outerHTML;
-                this.execCmd('InsertHTML', txt);
-            } 
-            
-            this.syncValue();
-            
-            this.deferFocus();
-        }
-    },
- // private
-    mozKeyPress : function(e){
-        if(e.ctrlKey){
-            var c = e.getCharCode(), cmd;
-          
-            if(c > 0){
-                c = String.fromCharCode(c).toLowerCase();
-                switch(c){
-                    case 'b':
-                        cmd = 'bold';
-                        break;
-                    case 'i':
-                        cmd = 'italic';
-                        break;
-                    
-                    case 'u':
-                        cmd = 'underline';
-                        break;
-                    
-                    case 'v':
-                        this.cleanUpPaste.defer(100, this);
-                        return;
-                        
-                }
-                if(cmd){
-                    this.win.focus();
-                    this.execCmd(cmd);
-                    this.deferFocus();
-                    e.preventDefault();
-                }
-                
-            }
-        }
-    },
-
-    // private
-    fixKeys : function(){ // load time branching for fastest keydown performance
-        if(Roo.isIE){
-            return function(e){
-                var k = e.getKey(), r;
-                if(k == e.TAB){
-                    e.stopEvent();
-                    r = this.doc.selection.createRange();
-                    if(r){
-                        r.collapse(true);
-                        r.pasteHTML('&#160;&#160;&#160;&#160;');
-                        this.deferFocus();
-                    }
-                    return;
-                }
-                
-                if(k == e.ENTER){
-                    r = this.doc.selection.createRange();
-                    if(r){
-                        var target = r.parentElement();
-                        if(!target || target.tagName.toLowerCase() != 'li'){
-                            e.stopEvent();
-                            r.pasteHTML('<br />');
-                            r.collapse(false);
-                            r.select();
-                        }
-                    }
-                }
-                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
-                    this.cleanUpPaste.defer(100, this);
-                    return;
-                }
-                
-                
-            };
-        }else if(Roo.isOpera){
-            return function(e){
-                var k = e.getKey();
-                if(k == e.TAB){
-                    e.stopEvent();
-                    this.win.focus();
-                    this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
-                    this.deferFocus();
-                }
-                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
-                    this.cleanUpPaste.defer(100, this);
-                    return;
-                }
-                
-            };
-        }else if(Roo.isSafari){
-            return function(e){
-                var k = e.getKey();
-                
-                if(k == e.TAB){
-                    e.stopEvent();
-                    this.execCmd('InsertText','\t');
-                    this.deferFocus();
-                    return;
-                }
-               if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
-                    this.cleanUpPaste.defer(100, this);
-                    return;
-                }
-                
-             };
-        }
-    }(),
+       check : true,
+       /**
+        * @event click
+        * Fires when the element is click.
+        * @param {Roo.bootstrap.CheckBox} this This input
+        */
+       click : true
+    });
     
-    getAllAncestors: function()
-    {
-        var p = this.getSelectedNode();
-        var a = [];
-        if (!p) {
-            a.push(p); // push blank onto stack..
-            p = this.getParentElement();
-        }
-        
-        
-        while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
-            a.push(p);
-            p = p.parentNode;
-        }
-        a.push(this.doc.body);
-        return a;
-    },
-    lastSel : false,
-    lastSelNode : false,
+};
+
+Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
+  
+    inputType: 'checkbox',
+    inputValue: 1,
+    valueOff: 0,
+    boxLabel: false,
+    checked: false,
+    weight : false,
+    inline: false,
+    tooltip : '',
     
+    // checkbox success does not make any sense really.. 
+    invalidClass : "",
+    validClass : "",
     
-    getSelection : function() 
-    {
-        this.assignDocWin();
-        return Roo.isIE ? this.doc.selection : this.win.getSelection();
-    },
     
-    getSelectedNode: function() 
+    getAutoCreate : function()
     {
-        // this may only work on Gecko!!!
-        
-        // should we cache this!!!!
+        var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
         
+        var id = Roo.id();
         
+        var cfg = {};
         
-         
-        var range = this.createRange(this.getSelection()).cloneRange();
+        cfg.cls = 'form-group form-check ' + this.inputType; //input-group
         
-        if (Roo.isIE) {
-            var parent = range.parentElement();
-            while (true) {
-                var testRange = range.duplicate();
-                testRange.moveToElementText(parent);
-                if (testRange.inRange(range)) {
-                    break;
-                }
-                if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
-                    break;
-                }
-                parent = parent.parentElement;
-            }
-            return parent;
+        if(this.inline){
+            cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
         }
         
-        // is ancestor a text element.
-        var ac =  range.commonAncestorContainer;
-        if (ac.nodeType == 3) {
-            ac = ac.parentNode;
+        var input =  {
+            tag: 'input',
+            id : id,
+            type : this.inputType,
+            value : this.inputValue,
+            cls : 'roo-' + this.inputType, //'form-box',
+            placeholder : this.placeholder || ''
+            
+        };
+        
+        if(this.inputType != 'radio'){
+            var hidden =  {
+                tag: 'input',
+                type : 'hidden',
+                cls : 'roo-hidden-value',
+                value : this.checked ? this.inputValue : this.valueOff
+            };
         }
         
-        var ar = ac.childNodes;
-         
-        var nodes = [];
-        var other_nodes = [];
-        var has_other_nodes = false;
-        for (var i=0;i<ar.length;i++) {
-            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
-                continue;
-            }
-            // fullly contained node.
-            
-            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
-                nodes.push(ar[i]);
-                continue;
-            }
             
-            // probably selected..
-            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
-                other_nodes.push(ar[i]);
-                continue;
+        if (this.weight) { // Validity check?
+            cfg.cls += " " + this.inputType + "-" + this.weight;
+        }
+        
+        if (this.disabled) {
+            input.disabled=true;
+        }
+        
+        if(this.checked){
+            input.checked = this.checked;
+        }
+        
+        if (this.name) {
+            
+            input.name = this.name;
+            
+            if(this.inputType != 'radio'){
+                hidden.name = this.name;
+                input.name = '_hidden_' + this.name;
             }
-            // outer..
-            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
-                continue;
+        }
+        
+        if (this.size) {
+            input.cls += ' input-' + this.size;
+        }
+        
+        var settings=this;
+        
+        ['xs','sm','md','lg'].map(function(size){
+            if (settings[size]) {
+                cfg.cls += ' col-' + size + '-' + settings[size];
             }
+        });
+        
+        var inputblock = input;
+         
+        if (this.before || this.after) {
             
+            inputblock = {
+                cls : 'input-group',
+                cn :  [] 
+            };
+            
+            if (this.before) {
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'input-group-addon',
+                    html : this.before
+                });
+            }
+            
+            inputblock.cn.push(input);
+            
+            if(this.inputType != 'radio'){
+                inputblock.cn.push(hidden);
+            }
+            
+            if (this.after) {
+                inputblock.cn.push({
+                    tag :'span',
+                    cls : 'input-group-addon',
+                    html : this.after
+                });
+            }
             
-            has_other_nodes = true;
-        }
-        if (!nodes.length && other_nodes.length) {
-            nodes= other_nodes;
         }
-        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
-            return false;
+        var boxLabelCfg = false;
+        
+        if(this.boxLabel){
+           
+            boxLabelCfg = {
+                tag: 'label',
+                //'for': id, // box label is handled by onclick - so no for...
+                cls: 'box-label',
+                html: this.boxLabel
+            };
+            if(this.tooltip){
+                boxLabelCfg.tooltip = this.tooltip;
+            }
+             
         }
         
-        return nodes[0];
-    },
-    createRange: function(sel)
-    {
-        // this has strange effects when using with 
-        // top toolbar - not sure if it's a great idea.
-        //this.editor.contentWindow.focus();
-        if (typeof sel != "undefined") {
-            try {
-                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
-            } catch(e) {
-                return this.doc.createRange();
+        
+        if (align ==='left' && this.fieldLabel.length) {
+//                Roo.log("left and has label");
+            cfg.cn = [
+                {
+                    tag: 'label',
+                    'for' :  id,
+                    cls : 'control-label',
+                    html : this.fieldLabel
+                },
+                {
+                    cls : "", 
+                    cn: [
+                        inputblock
+                    ]
+                }
+            ];
+            
+            if (boxLabelCfg) {
+                cfg.cn[1].cn.push(boxLabelCfg);
+            }
+            
+            if(this.labelWidth > 12){
+                cfg.cn[0].style = "width: " + this.labelWidth + 'px';
+            }
+            
+            if(this.labelWidth < 13 && this.labelmd == 0){
+                this.labelmd = this.labelWidth;
+            }
+            
+            if(this.labellg > 0){
+                cfg.cn[0].cls += ' col-lg-' + this.labellg;
+                cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
+            }
+            
+            if(this.labelmd > 0){
+                cfg.cn[0].cls += ' col-md-' + this.labelmd;
+                cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
+            }
+            
+            if(this.labelsm > 0){
+                cfg.cn[0].cls += ' col-sm-' + this.labelsm;
+                cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
+            }
+            
+            if(this.labelxs > 0){
+                cfg.cn[0].cls += ' col-xs-' + this.labelxs;
+                cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
             }
+            
+        } else if ( this.fieldLabel.length) {
+//                Roo.log(" label");
+                cfg.cn = [
+                   
+                    {
+                        tag: this.boxLabel ? 'span' : 'label',
+                        'for': id,
+                        cls: 'control-label box-input-label',
+                        //cls : 'input-group-addon',
+                        html : this.fieldLabel
+                    },
+                    
+                    inputblock
+                    
+                ];
+                if (boxLabelCfg) {
+                    cfg.cn.push(boxLabelCfg);
+                }
+
         } else {
-            return this.doc.createRange();
+            
+//                Roo.log(" no label && no align");
+                cfg.cn = [  inputblock ] ;
+                if (boxLabelCfg) {
+                    cfg.cn.push(boxLabelCfg);
+                }
+
+                
         }
-    },
-    getParentElement: function()
-    {
         
-        this.assignDocWin();
-        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
+       
         
-        var range = this.createRange(sel);
-         
-        try {
-            var p = range.commonAncestorContainer;
-            while (p.nodeType == 3) { // text node
-                p = p.parentNode;
-            }
-            return p;
-        } catch (e) {
-            return null;
+        if(this.inputType != 'radio'){
+            cfg.cn.push(hidden);
         }
-    
+        
+        return cfg;
+        
     },
-    /***
-     *
-     * Range intersection.. the hard stuff...
-     *  '-1' = before
-     *  '0' = hits..
-     *  '1' = after.
-     *         [ -- selected range --- ]
-     *   [fail]                        [fail]
-     *
-     *    basically..
-     *      if end is before start or  hits it. fail.
-     *      if start is after end or hits it fail.
-     *
-     *   if either hits (but other is outside. - then it's not 
-     *   
-     *    
-     **/
-    
     
-    // @see http://www.thismuchiknow.co.uk/?p=64.
-    rangeIntersectsNode : function(range, node)
+    /**
+     * return the real input element.
+     */
+    inputEl: function ()
     {
-        var nodeRange = node.ownerDocument.createRange();
-        try {
-            nodeRange.selectNode(node);
-        } catch (e) {
-            nodeRange.selectNodeContents(node);
-        }
-    
-        var rangeStartRange = range.cloneRange();
-        rangeStartRange.collapse(true);
+        return this.el.select('input.roo-' + this.inputType,true).first();
+    },
+    hiddenEl: function ()
+    {
+        return this.el.select('input.roo-hidden-value',true).first();
+    },
     
-        var rangeEndRange = range.cloneRange();
-        rangeEndRange.collapse(false);
+    labelEl: function()
+    {
+        return this.el.select('label.control-label',true).first();
+    },
+    /* depricated... */
     
-        var nodeStartRange = nodeRange.cloneRange();
-        nodeStartRange.collapse(true);
+    label: function()
+    {
+        return this.labelEl();
+    },
     
-        var nodeEndRange = nodeRange.cloneRange();
-        nodeEndRange.collapse(false);
+    boxLabelEl: function()
+    {
+        return this.el.select('label.box-label',true).first();
+    },
     
-        return rangeStartRange.compareBoundaryPoints(
-                 Range.START_TO_START, nodeEndRange) == -1 &&
-               rangeEndRange.compareBoundaryPoints(
-                 Range.START_TO_START, nodeStartRange) == 1;
+    initEvents : function()
+    {
+//        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
         
-         
+        this.inputEl().on('click', this.onClick,  this);
+        
+        if (this.boxLabel) { 
+            this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
+        }
+        
+        this.startValue = this.getValue();
+        
+        if(this.groupId){
+            Roo.bootstrap.CheckBox.register(this);
+        }
     },
-    rangeCompareNode : function(range, node)
+    
+    onClick : function(e)
+    {   
+        if(this.fireEvent('click', this, e) !== false){
+            this.setChecked(!this.checked);
+        }
+        
+    },
+    
+    setChecked : function(state,suppressEvent)
     {
-        var nodeRange = node.ownerDocument.createRange();
-        try {
-            nodeRange.selectNode(node);
-        } catch (e) {
-            nodeRange.selectNodeContents(node);
+        this.startValue = this.getValue();
+
+        if(this.inputType == 'radio'){
+            
+            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+                e.dom.checked = false;
+            });
+            
+            this.inputEl().dom.checked = true;
+            
+            this.inputEl().dom.value = this.inputValue;
+            
+            if(suppressEvent !== true){
+                this.fireEvent('check', this, true);
+            }
+            
+            this.validate();
+            
+            return;
         }
         
+        this.checked = state;
         
-        range.collapse(true);
-    
-        nodeRange.collapse(true);
-     
-        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
-        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
-         
-        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
+        this.inputEl().dom.checked = state;
         
-        var nodeIsBefore   =  ss == 1;
-        var nodeIsAfter    = ee == -1;
         
-        if (nodeIsBefore && nodeIsAfter) {
-            return 0; // outer
-        }
-        if (!nodeIsBefore && nodeIsAfter) {
-            return 1; //right trailed.
+        this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
+        
+        if(suppressEvent !== true){
+            this.fireEvent('check', this, state);
         }
         
-        if (nodeIsBefore && !nodeIsAfter) {
-            return 2;  // left trailed.
+        this.validate();
+    },
+    
+    getValue : function()
+    {
+        if(this.inputType == 'radio'){
+            return this.getGroupValue();
         }
-        // fully contined.
-        return 3;
+        
+        return this.hiddenEl().dom.value;
+        
     },
-
-    // private? - in a new class?
-    cleanUpPaste :  function()
+    
+    getGroupValue : function()
     {
-        // cleans up the whole document..
-        Roo.log('cleanuppaste');
+        if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
+            return '';
+        }
         
-        this.cleanUpChildren(this.doc.body);
-        var clean = this.cleanWordChars(this.doc.body.innerHTML);
-        if (clean != this.doc.body.innerHTML) {
-            this.doc.body.innerHTML = clean;
+        return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
+    },
+    
+    setValue : function(v,suppressEvent)
+    {
+        if(this.inputType == 'radio'){
+            this.setGroupValue(v, suppressEvent);
+            return;
         }
         
+        this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
+        
+        this.validate();
     },
     
-    cleanWordChars : function(input) {// change the chars to hex code
-        var he = Roo.HtmlEditorCore;
+    setGroupValue : function(v, suppressEvent)
+    {
+        this.startValue = this.getValue();
         
-        var output = input;
-        Roo.each(he.swapCodes, function(sw) { 
-            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
+        Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+            e.dom.checked = false;
             
-            output = output.replace(swapper, sw[1]);
+            if(e.dom.value == v){
+                e.dom.checked = true;
+            }
         });
         
-        return output;
+        if(suppressEvent !== true){
+            this.fireEvent('check', this, true);
+        }
+
+        this.validate();
+        
+        return;
     },
     
-    
-    cleanUpChildren : function (n)
+    validate : function()
     {
-        if (!n.childNodes.length) {
-            return;
+        if(this.getVisibilityEl().hasClass('hidden')){
+            return true;
         }
-        for (var i = n.childNodes.length-1; i > -1 ; i--) {
-           this.cleanUpChild(n.childNodes[i]);
+        
+        if(
+                this.disabled || 
+                (this.inputType == 'radio' && this.validateRadio()) ||
+                (this.inputType == 'checkbox' && this.validateCheckbox())
+        ){
+            this.markValid();
+            return true;
         }
-    },
-    
-    
         
+        this.markInvalid();
+        return false;
+    },
     
-    cleanUpChild : function (node)
+    validateRadio : function()
     {
-        var ed = this;
-        //console.log(node);
-        if (node.nodeName == "#text") {
-            // clean up silly Windows -- stuff?
-            return; 
+        if(this.getVisibilityEl().hasClass('hidden')){
+            return true;
         }
-        if (node.nodeName == "#comment") {
-            node.parentNode.removeChild(node);
-            // clean up silly Windows -- stuff?
-            return; 
+        
+        if(this.allowBlank){
+            return true;
         }
-        var lcname = node.tagName.toLowerCase();
-        // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
-        // whitelist of tags..
         
-        if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
-            // remove node.
-            node.parentNode.removeChild(node);
-            return;
+        var valid = false;
+        
+        Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+            if(!e.dom.checked){
+                return;
+            }
             
+            valid = true;
+            
+            return false;
+        });
+        
+        return valid;
+    },
+    
+    validateCheckbox : function()
+    {
+        if(!this.groupId){
+            return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
+            //return (this.getValue() == this.inputValue) ? true : false;
         }
         
-        var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
+        var group = Roo.bootstrap.CheckBox.get(this.groupId);
         
-        // spans with no attributes - just remove them..
-        if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
-            remove_keep_children = true;
+        if(!group){
+            return false;
         }
         
-        // remove <a name=....> as rendering on yahoo mailer is borked with this.
-        // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
-        
-        //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
-        //    remove_keep_children = true;
-        //}
+        var r = false;
         
-        if (remove_keep_children) {
-            this.cleanUpChildren(node);
-            // inserts everything just before this node...
-            while (node.childNodes.length) {
-                var cn = node.childNodes[0];
-                node.removeChild(cn);
-                node.parentNode.insertBefore(cn, node);
+        for(var i in group){
+            if(group[i].el.isVisible(true)){
+                r = false;
+                break;
             }
-            node.parentNode.removeChild(node);
-            return;
-        }
-        
-        if (!node.attributes || !node.attributes.length) {
-            
-          
-            
             
-            this.cleanUpChildren(node);
-            return;
+            r = true;
         }
         
-        function cleanAttr(n,v)
-        {
-            
-            if (v.match(/^\./) || v.match(/^\//)) {
-                return;
-            }
-            if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
-                return;
-            }
-            if (v.match(/^#/)) {
-                return;
-            }
-            if (v.match(/^\{/)) { // allow template editing.
-                return;
+        for(var i in group){
+            if(r){
+                break;
             }
-//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
-            node.removeAttribute(n);
             
+            r = (group[i].getValue() == group[i].inputValue) ? true : false;
         }
         
-        var cwhite = this.cwhite;
-        var cblack = this.cblack;
-            
-        function cleanStyle(n,v)
-        {
-            if (v.match(/expression/)) { //XSS?? should we even bother..
-                node.removeAttribute(n);
-                return;
-            }
-            
-            var parts = v.split(/;/);
-            var clean = [];
-            
-            Roo.each(parts, function(p) {
-                p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
-                if (!p.length) {
-                    return true;
-                }
-                var l = p.split(':').shift().replace(/\s+/g,'');
-                l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
-                
-                if ( cwhite.length && cblack.indexOf(l) > -1) {
-//                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
-                    //node.removeAttribute(n);
-                    return true;
-                }
-                //Roo.log()
-                // only allow 'c whitelisted system attributes'
-                if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
-//                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
-                    //node.removeAttribute(n);
-                    return true;
+        return r;
+    },
+    
+    /**
+     * Mark this field as valid
+     */
+    markValid : function()
+    {
+        var _this = this;
+        
+        this.fireEvent('valid', this);
+        
+        var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+        
+        if(this.groupId){
+            label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
+        }
+        
+        if(label){
+            label.markValid();
+        }
+
+        if(this.inputType == 'radio'){
+            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+                var fg = e.findParent('.form-group', false, true);
+                if (Roo.bootstrap.version == 3) {
+                    fg.removeClass([_this.invalidClass, _this.validClass]);
+                    fg.addClass(_this.validClass);
+                } else {
+                    fg.removeClass(['is-valid', 'is-invalid']);
+                    fg.addClass('is-valid');
                 }
-                
-                
-                 
-                
-                clean.push(p);
-                return true;
             });
-            if (clean.length) { 
-                node.setAttribute(n, clean.join(';'));
-            } else {
-                node.removeAttribute(n);
-            }
             
+            return;
         }
-        
-        
-        for (var i = node.attributes.length-1; i > -1 ; i--) {
-            var a = node.attributes[i];
-            //console.log(a);
-            
-            if (a.name.toLowerCase().substr(0,2)=='on')  {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
-                cleanAttr(a.name,a.value); // fixme..
-                continue;
-            }
-            if (a.name == 'style') {
-                cleanStyle(a.name,a.value);
-                continue;
-            }
-            /// clean up MS crap..
-            // tecnically this should be a list of valid class'es..
-            
-            
-            if (a.name == 'class') {
-                if (a.value.match(/^Mso/)) {
-                    node.removeAttribute('class');
-                }
-                
-                if (a.value.match(/^body$/)) {
-                    node.removeAttribute('class');
-                }
-                continue;
+
+        if(!this.groupId){
+            var fg = this.el.findParent('.form-group', false, true);
+            if (Roo.bootstrap.version == 3) {
+                fg.removeClass([this.invalidClass, this.validClass]);
+                fg.addClass(this.validClass);
+            } else {
+                fg.removeClass(['is-valid', 'is-invalid']);
+                fg.addClass('is-valid');
             }
-            
-            // style cleanup!?
-            // class cleanup?
-            
+            return;
         }
         
+        var group = Roo.bootstrap.CheckBox.get(this.groupId);
         
-        this.cleanUpChildren(node);
-        
+        if(!group){
+            return;
+        }
         
+        for(var i in group){
+            var fg = group[i].el.findParent('.form-group', false, true);
+            if (Roo.bootstrap.version == 3) {
+                fg.removeClass([this.invalidClass, this.validClass]);
+                fg.addClass(this.validClass);
+            } else {
+                fg.removeClass(['is-valid', 'is-invalid']);
+                fg.addClass('is-valid');
+            }
+        }
     },
     
-    /**
-     * Clean up MS wordisms...
+     /**
+     * Mark this field as invalid
+     * @param {String} msg The validation message
      */
-    cleanWord : function(node)
+    markInvalid : function(msg)
     {
-        if (!node) {
-            this.cleanWord(this.doc.body);
+        if(this.allowBlank){
             return;
         }
         
-        if(
-                node.nodeName == 'SPAN' &&
-                !node.hasAttributes() &&
-                node.childNodes.length == 1 &&
-                node.firstChild.nodeName == "#text"  
-        ) {
-            var textNode = node.firstChild;
-            node.removeChild(textNode);
-            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
-                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
-            }
-            node.parentNode.insertBefore(textNode, node);
-            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
-                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
-            }
-            node.parentNode.removeChild(node);
-        }
+        var _this = this;
         
-        if (node.nodeName == "#text") {
-            // clean up silly Windows -- stuff?
-            return; 
-        }
-        if (node.nodeName == "#comment") {
-            node.parentNode.removeChild(node);
-            // clean up silly Windows -- stuff?
-            return; 
-        }
+        this.fireEvent('invalid', this, msg);
         
-        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
-            node.parentNode.removeChild(node);
-            return;
+        var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+        
+        if(this.groupId){
+            label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
         }
-        //Roo.log(node.tagName);
-        // remove - but keep children..
-        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
-            //Roo.log('-- removed');
-            while (node.childNodes.length) {
-                var cn = node.childNodes[0];
-                node.removeChild(cn);
-                node.parentNode.insertBefore(cn, node);
-                // move node to parent - and clean it..
-                this.cleanWord(cn);
-            }
-            node.parentNode.removeChild(node);
-            /// no need to iterate chidlren = it's got none..
-            //this.iterateChildren(node, this.cleanWord);
-            return;
+        
+        if(label){
+            label.markInvalid();
         }
-        // clean styles
-        if (node.className.length) {
             
-            var cn = node.className.split(/\W+/);
-            var cna = [];
-            Roo.each(cn, function(cls) {
-                if (cls.match(/Mso[a-zA-Z]+/)) {
-                    return;
+        if(this.inputType == 'radio'){
+            
+            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+                var fg = e.findParent('.form-group', false, true);
+                if (Roo.bootstrap.version == 3) {
+                    fg.removeClass([_this.invalidClass, _this.validClass]);
+                    fg.addClass(_this.invalidClass);
+                } else {
+                    fg.removeClass(['is-invalid', 'is-valid']);
+                    fg.addClass('is-invalid');
                 }
-                cna.push(cls);
             });
-            node.className = cna.length ? cna.join(' ') : '';
-            if (!cna.length) {
-                node.removeAttribute("class");
+            
+            return;
+        }
+        
+        if(!this.groupId){
+            var fg = this.el.findParent('.form-group', false, true);
+            if (Roo.bootstrap.version == 3) {
+                fg.removeClass([_this.invalidClass, _this.validClass]);
+                fg.addClass(_this.invalidClass);
+            } else {
+                fg.removeClass(['is-invalid', 'is-valid']);
+                fg.addClass('is-invalid');
             }
+            return;
         }
         
-        if (node.hasAttribute("lang")) {
-            node.removeAttribute("lang");
+        var group = Roo.bootstrap.CheckBox.get(this.groupId);
+        
+        if(!group){
+            return;
         }
         
-        if (node.hasAttribute("style")) {
-            
-            var styles = node.getAttribute("style").split(";");
-            var nstyle = [];
-            Roo.each(styles, function(s) {
-                if (!s.match(/:/)) {
-                    return;
-                }
-                var kv = s.split(":");
-                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
-                    return;
-                }
-                // what ever is left... we allow.
-                nstyle.push(s);
-            });
-            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
-            if (!nstyle.length) {
-                node.removeAttribute('style');
+        for(var i in group){
+            var fg = group[i].el.findParent('.form-group', false, true);
+            if (Roo.bootstrap.version == 3) {
+                fg.removeClass([_this.invalidClass, _this.validClass]);
+                fg.addClass(_this.invalidClass);
+            } else {
+                fg.removeClass(['is-invalid', 'is-valid']);
+                fg.addClass('is-invalid');
             }
         }
-        this.iterateChildren(node, this.cleanWord);
-        
-        
         
     },
-    /**
-     * iterateChildren of a Node, calling fn each time, using this as the scole..
-     * @param {DomNode} node node to iterate children of.
-     * @param {Function} fn method of this class to call on each item.
-     */
-    iterateChildren : function(node, fn)
+    
+    clearInvalid : function()
     {
-        if (!node.childNodes.length) {
-                return;
-        }
-        for (var i = node.childNodes.length-1; i > -1 ; i--) {
-           fn.call(this, node.childNodes[i])
+        Roo.bootstrap.Input.prototype.clearInvalid.call(this);
+        
+        // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
+        
+        var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+        
+        if (label && label.iconEl) {
+            label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
+            label.iconEl.removeClass(['is-invalid', 'is-valid']);
         }
     },
     
-    
-    /**
-     * cleanTableWidths.
-     *
-     * Quite often pasting from word etc.. results in tables with column and widths.
-     * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
-     *
-     */
-    cleanTableWidths : function(node)
+    disable : function()
     {
-         
-         
-        if (!node) {
-            this.cleanTableWidths(this.doc.body);
+        if(this.inputType != 'radio'){
+            Roo.bootstrap.CheckBox.superclass.disable.call(this);
             return;
         }
         
-        // ignore list...
-        if (node.nodeName == "#text" || node.nodeName == "#comment") {
-            return; 
+        var _this = this;
+        
+        if(this.rendered){
+            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+                _this.getActionEl().addClass(this.disabledClass);
+                e.dom.disabled = true;
+            });
         }
-        Roo.log(node.tagName);
-        if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
-            this.iterateChildren(node, this.cleanTableWidths);
+        
+        this.disabled = true;
+        this.fireEvent("disable", this);
+        return this;
+    },
+
+    enable : function()
+    {
+        if(this.inputType != 'radio'){
+            Roo.bootstrap.CheckBox.superclass.enable.call(this);
             return;
         }
-        if (node.hasAttribute('width')) {
-            node.removeAttribute('width');
-        }
         
-         
-        if (node.hasAttribute("style")) {
-            // pretty basic...
-            
-            var styles = node.getAttribute("style").split(";");
-            var nstyle = [];
-            Roo.each(styles, function(s) {
-                if (!s.match(/:/)) {
-                    return;
-                }
-                var kv = s.split(":");
-                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
-                    return;
-                }
-                // what ever is left... we allow.
-                nstyle.push(s);
+        var _this = this;
+        
+        if(this.rendered){
+            Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+                _this.getActionEl().removeClass(this.disabledClass);
+                e.dom.disabled = false;
             });
-            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
-            if (!nstyle.length) {
-                node.removeAttribute('style');
-            }
         }
         
-        this.iterateChildren(node, this.cleanTableWidths);
+        this.disabled = false;
+        this.fireEvent("enable", this);
+        return this;
+    },
+    
+    setBoxLabel : function(v)
+    {
+        this.boxLabel = v;
+        
+        if(this.rendered){
+            this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+        }
+    }
+
+});
+
+Roo.apply(Roo.bootstrap.CheckBox, {
+    
+    groups: {},
+    
+     /**
+    * register a CheckBox Group
+    * @param {Roo.bootstrap.CheckBox} the CheckBox to add
+    */
+    register : function(checkbox)
+    {
+        if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
+            this.groups[checkbox.groupId] = {};
+        }
         
+        if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
+            return;
+        }
         
+        this.groups[checkbox.groupId][checkbox.name] = checkbox;
+       
     },
+    /**
+    * fetch a CheckBox Group based on the group ID
+    * @param {string} the group ID
+    * @returns {Roo.bootstrap.CheckBox} the CheckBox group
+    */
+    get: function(groupId) {
+        if (typeof(this.groups[groupId]) == 'undefined') {
+            return false;
+        }
+        
+        return this.groups[groupId] ;
+    }
+    
+    
+});
+/*
+ * - LGPL
+ *
+ * RadioItem
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.Radio
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Radio class
+ * @cfg {String} boxLabel - the label associated
+ * @cfg {String} value - the value of radio
+ * 
+ * @constructor
+ * Create a new Radio
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.Radio = function(config){
+    Roo.bootstrap.Radio.superclass.constructor.call(this, config);
     
+};
+
+Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
     
+    boxLabel : '',
     
+    value : '',
     
-    domToHTML : function(currentElement, depth, nopadtext) {
+    getAutoCreate : function()
+    {
+        var cfg = {
+            tag : 'div',
+            cls : 'form-group radio',
+            cn : [
+                {
+                    tag : 'label',
+                    cls : 'box-label',
+                    html : this.boxLabel
+                }
+            ]
+        };
         
-        depth = depth || 0;
-        nopadtext = nopadtext || false;
+        return cfg;
+    },
     
-        if (!currentElement) {
-            return this.domToHTML(this.doc.body);
-        }
+    initEvents : function() 
+    {
+        this.parent().register(this);
         
-        //Roo.log(currentElement);
-        var j;
-        var allText = false;
-        var nodeName = currentElement.nodeName;
-        var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
+        this.el.on('click', this.onClick, this);
         
-        if  (nodeName == '#text') {
-            
-            return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
+    },
+    
+    onClick : function(e)
+    {
+        if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
+            this.setChecked(true);
         }
+    },
+    
+    setChecked : function(state, suppressEvent)
+    {
+        this.parent().setValue(this.value, suppressEvent);
         
+    },
+    
+    setBoxLabel : function(v)
+    {
+        this.boxLabel = v;
         
-        var ret = '';
-        if (nodeName != 'BODY') {
-             
-            var i = 0;
-            // Prints the node tagName, such as <A>, <IMG>, etc
-            if (tagName) {
-                var attr = [];
-                for(i = 0; i < currentElement.attributes.length;i++) {
-                    // quoting?
-                    var aname = currentElement.attributes.item(i).name;
-                    if (!currentElement.attributes.item(i).value.length) {
-                        continue;
-                    }
-                    attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
-                }
-                
-                ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
-            } 
-            else {
-                
-                // eack
-            }
-        } else {
-            tagName = false;
-        }
-        if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
-            return ret;
+        if(this.rendered){
+            this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
         }
-        if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
-            nopadtext = true;
+    }
+    
+});
+
+ /*
+ * - LGPL
+ *
+ * Input
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.SecurePass
+ * @extends Roo.bootstrap.Input
+ * Bootstrap SecurePass class
+ *
+ * 
+ * @constructor
+ * Create a new SecurePass
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.SecurePass = function (config) {
+    // these go here, so the translation tool can replace them..
+    this.errors = {
+        PwdEmpty: "Please type a password, and then retype it to confirm.",
+        PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
+        PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
+        PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
+        IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
+        FNInPwd: "Your password can't contain your first name. Please type a different password.",
+        LNInPwd: "Your password can't contain your last name. Please type a different password.",
+        TooWeak: "Your password is Too Weak."
+    },
+    this.meterLabel = "Password strength:";
+    this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
+    this.meterClass = [
+        "roo-password-meter-tooweak", 
+        "roo-password-meter-weak", 
+        "roo-password-meter-medium", 
+        "roo-password-meter-strong", 
+        "roo-password-meter-grey"
+    ];
+    
+    this.errors = {};
+    
+    Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
+}
+
+Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
+    /**
+     * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
+     * {
+     *  PwdEmpty: "Please type a password, and then retype it to confirm.",
+     *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
+     *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
+     *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
+     *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
+     *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
+     *  LNInPwd: "Your password can't contain your last name. Please type a different password."
+     * })
+     */
+    // private
+    
+    meterWidth: 300,
+    errorMsg :'',    
+    errors: false,
+    imageRoot: '/',
+    /**
+     * @cfg {String/Object} Label for the strength meter (defaults to
+     * 'Password strength:')
+     */
+    // private
+    meterLabel: '',
+    /**
+     * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
+     * ['Weak', 'Medium', 'Strong'])
+     */
+    // private    
+    pwdStrengths: false,    
+    // private
+    strength: 0,
+    // private
+    _lastPwd: null,
+    // private
+    kCapitalLetter: 0,
+    kSmallLetter: 1,
+    kDigit: 2,
+    kPunctuation: 3,
+    
+    insecure: false,
+    // private
+    initEvents: function ()
+    {
+        Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
+
+        if (this.el.is('input[type=password]') && Roo.isSafari) {
+            this.el.on('keydown', this.SafariOnKeyDown, this);
+        }
+
+        this.el.on('keyup', this.checkStrength, this, {buffer: 50});
+    },
+    // private
+    onRender: function (ct, position)
+    {
+        Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
+        this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
+        this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
+
+        this.trigger.createChild({
+                  cn: [
+                    {
+                    //id: 'PwdMeter',
+                    tag: 'div',
+                    cls: 'roo-password-meter-grey col-xs-12',
+                    style: {
+                        //width: 0,
+                        //width: this.meterWidth + 'px'                                                
+                        }
+                    },
+                    {                           
+                        cls: 'roo-password-meter-text'                          
+                    }
+                ]            
+        });
+
+         
+        if (this.hideTrigger) {
+            this.trigger.setDisplayed(false);
+        }
+        this.setSize(this.width || '', this.height || '');
+    },
+    // private
+    onDestroy: function ()
+    {
+        if (this.trigger) {
+            this.trigger.removeAllListeners();
+            this.trigger.remove();
+        }
+        if (this.wrap) {
+            this.wrap.remove();
+        }
+        Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
+    },
+    // private
+    checkStrength: function ()
+    {
+        var pwd = this.inputEl().getValue();
+        if (pwd == this._lastPwd) {
+            return;
+        }
+
+        var strength;
+        if (this.ClientSideStrongPassword(pwd)) {
+            strength = 3;
+        } else if (this.ClientSideMediumPassword(pwd)) {
+            strength = 2;
+        } else if (this.ClientSideWeakPassword(pwd)) {
+            strength = 1;
+        } else {
+            strength = 0;
         }
         
+        Roo.log('strength1: ' + strength);
         
-        // Traverse the tree
-        i = 0;
-        var currentElementChild = currentElement.childNodes.item(i);
-        var allText = true;
-        var innerHTML  = '';
-        lastnode = '';
-        while (currentElementChild) {
-            // Formatting code (indent the tree so it looks nice on the screen)
-            var nopad = nopadtext;
-            if (lastnode == 'SPAN') {
-                nopad  = true;
-            }
-            // text
-            if  (currentElementChild.nodeName == '#text') {
-                var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
-                toadd = nopadtext ? toadd : toadd.trim();
-                if (!nopad && toadd.length > 80) {
-                    innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
-                }
-                innerHTML  += toadd;
+        //var pm = this.trigger.child('div/div/div').dom;
+        var pm = this.trigger.child('div/div');
+        pm.removeClass(this.meterClass);
+        pm.addClass(this.meterClass[strength]);
                 
-                i++;
-                currentElementChild = currentElement.childNodes.item(i);
-                lastNode = '';
-                continue;
-            }
-            allText = false;
-            
-            innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
+        
+        var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
                 
-            // Recursively traverse the tree structure of the child node
-            innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
-            lastnode = currentElementChild.nodeName;
-            i++;
-            currentElementChild=currentElement.childNodes.item(i);
-        }
+        pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
         
-        ret += innerHTML;
+        this._lastPwd = pwd;
+    },
+    reset: function ()
+    {
+        Roo.bootstrap.SecurePass.superclass.reset.call(this);
         
-        if (!allText) {
-                // The remaining code is mostly for formatting the tree
-            ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
-        }
+        this._lastPwd = '';
+        
+        var pm = this.trigger.child('div/div');
+        pm.removeClass(this.meterClass);
+        pm.addClass('roo-password-meter-grey');        
         
         
-        if (tagName) {
-            ret+= "</"+tagName+">";
-        }
-        return ret;
+        var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
         
+        pt.innerHTML = '';
+        this.inputEl().dom.type='password';
     },
-        
-    applyBlacklists : function()
+    // private
+    validateValue: function (value)
     {
-        var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
-        var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
-        
-        this.white = [];
-        this.black = [];
-        Roo.each(Roo.HtmlEditorCore.white, function(tag) {
-            if (b.indexOf(tag) > -1) {
-                return;
+        if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
+            return false;
+        }
+        if (value.length == 0) {
+            if (this.allowBlank) {
+                this.clearInvalid();
+                return true;
             }
-            this.white.push(tag);
-            
-        }, this);
-        
-        Roo.each(w, function(tag) {
-            if (b.indexOf(tag) > -1) {
-                return;
-            }
-            if (this.white.indexOf(tag) > -1) {
-                return;
-            }
-            this.white.push(tag);
-            
-        }, this);
-        
+
+            this.markInvalid(this.errors.PwdEmpty);
+            this.errorMsg = this.errors.PwdEmpty;
+            return false;
+        }
         
-        Roo.each(Roo.HtmlEditorCore.black, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            this.black.push(tag);
-            
-        }, this);
+        if(this.insecure){
+            return true;
+        }
         
-        Roo.each(b, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            if (this.black.indexOf(tag) > -1) {
-                return;
-            }
-            this.black.push(tag);
-            
-        }, this);
+        if (!value.match(/[\x21-\x7e]+/)) {
+            this.markInvalid(this.errors.PwdBadChar);
+            this.errorMsg = this.errors.PwdBadChar;
+            return false;
+        }
+        if (value.length < 6) {
+            this.markInvalid(this.errors.PwdShort);
+            this.errorMsg = this.errors.PwdShort;
+            return false;
+        }
+        if (value.length > 16) {
+            this.markInvalid(this.errors.PwdLong);
+            this.errorMsg = this.errors.PwdLong;
+            return false;
+        }
+        var strength;
+        if (this.ClientSideStrongPassword(value)) {
+            strength = 3;
+        } else if (this.ClientSideMediumPassword(value)) {
+            strength = 2;
+        } else if (this.ClientSideWeakPassword(value)) {
+            strength = 1;
+        } else {
+            strength = 0;
+        }
+
         
+        if (strength < 2) {
+            //this.markInvalid(this.errors.TooWeak);
+            this.errorMsg = this.errors.TooWeak;
+            //return false;
+        }
         
-        w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
-        b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
         
-        this.cwhite = [];
-        this.cblack = [];
-        Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
-            if (b.indexOf(tag) > -1) {
-                return;
-            }
-            this.cwhite.push(tag);
-            
-        }, this);
+        console.log('strength2: ' + strength);
         
-        Roo.each(w, function(tag) {
-            if (b.indexOf(tag) > -1) {
-                return;
-            }
-            if (this.cwhite.indexOf(tag) > -1) {
-                return;
-            }
-            this.cwhite.push(tag);
-            
-        }, this);
+        //var pm = this.trigger.child('div/div/div').dom;
         
+        var pm = this.trigger.child('div/div');
+        pm.removeClass(this.meterClass);
+        pm.addClass(this.meterClass[strength]);
+                
+        var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
+                
+        pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
         
-        Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            this.cblack.push(tag);
+        this.errorMsg = ''; 
+        return true;
+    },
+    // private
+    CharacterSetChecks: function (type)
+    {
+        this.type = type;
+        this.fResult = false;
+    },
+    // private
+    isctype: function (character, type)
+    {
+        switch (type) {  
+            case this.kCapitalLetter:
+                if (character >= 'A' && character <= 'Z') {
+                    return true;
+                }
+                break;
             
-        }, this);
-        
-        Roo.each(b, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            if (this.cblack.indexOf(tag) > -1) {
-                return;
-            }
-            this.cblack.push(tag);
+            case this.kSmallLetter:
+                if (character >= 'a' && character <= 'z') {
+                    return true;
+                }
+                break;
             
-        }, this);
+            case this.kDigit:
+                if (character >= '0' && character <= '9') {
+                    return true;
+                }
+                break;
+            
+            case this.kPunctuation:
+                if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
+                    return true;
+                }
+                break;
+            
+            default:
+                return false;
+        }
+
     },
-    
-    setStylesheets : function(stylesheets)
+    // private
+    IsLongEnough: function (pwd, size)
     {
-        if(typeof(stylesheets) == 'string'){
-            Roo.get(this.iframe.contentDocument.head).createChild({
-                tag : 'link',
-                rel : 'stylesheet',
-                type : 'text/css',
-                href : stylesheets
-            });
-            
-            return;
+        return !(pwd == null || isNaN(size) || pwd.length < size);
+    },
+    // private
+    SpansEnoughCharacterSets: function (word, nb)
+    {
+        if (!this.IsLongEnough(word, nb))
+        {
+            return false;
         }
-        var _this = this;
-     
-        Roo.each(stylesheets, function(s) {
-            if(!s.length){
-                return;
-            }
-            
-            Roo.get(_this.iframe.contentDocument.head).createChild({
-                tag : 'link',
-                rel : 'stylesheet',
-                type : 'text/css',
-                href : s
-            });
-        });
 
+        var characterSetChecks = new Array(
+            new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
+            new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
+        );
         
+        for (var index = 0; index < word.length; ++index) {
+            for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
+                if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
+                    characterSetChecks[nCharSet].fResult = true;
+                    break;
+                }
+            }
+        }
+
+        var nCharSets = 0;
+        for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
+            if (characterSetChecks[nCharSet].fResult) {
+                ++nCharSets;
+            }
+        }
+
+        if (nCharSets < nb) {
+            return false;
+        }
+        return true;
     },
-    
-    removeStylesheets : function()
+    // private
+    ClientSideStrongPassword: function (pwd)
     {
-        var _this = this;
-        
-        Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
-            s.remove();
-        });
+        return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
     },
-    
-    setStyle : function(style)
+    // private
+    ClientSideMediumPassword: function (pwd)
     {
-        Roo.get(this.iframe.contentDocument.head).createChild({
-            tag : 'style',
-            type : 'text/css',
-            html : style
-        });
-
-        return;
+        return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
+    },
+    // private
+    ClientSideWeakPassword: function (pwd)
+    {
+        return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
     }
+          
+})//<script type="text/javascript">
+
+/*
+ * Based  Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ * LGPL
+ *
+ */
+/**
+ * @class Roo.HtmlEditorCore
+ * @extends Roo.Component
+ * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
+ *
+ * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
+ */
+
+Roo.HtmlEditorCore = function(config){
     
-    // hide stuff that is not compatible
-    /**
-     * @event blur
-     * @hide
-     */
-    /**
-     * @event change
-     * @hide
-     */
-    /**
-     * @event focus
-     * @hide
-     */
-    /**
-     * @event specialkey
-     * @hide
-     */
-    /**
-     * @cfg {String} fieldClass @hide
-     */
-    /**
-     * @cfg {String} focusClass @hide
-     */
-    /**
-     * @cfg {String} autoCreate @hide
-     */
-    /**
-     * @cfg {String} inputType @hide
-     */
-    /**
-     * @cfg {String} invalidClass @hide
+    
+    Roo.HtmlEditorCore.superclass.constructor.call(this, config);
+    
+    
+    this.addEvents({
+        /**
+         * @event initialize
+         * Fires when the editor is fully initialized (including the iframe)
+         * @param {Roo.HtmlEditorCore} this
+         */
+        initialize: true,
+        /**
+         * @event activate
+         * Fires when the editor is first receives the focus. Any insertion must wait
+         * until after this event.
+         * @param {Roo.HtmlEditorCore} this
+         */
+        activate: true,
+         /**
+         * @event beforesync
+         * Fires before the textarea is updated with content from the editor iframe. Return false
+         * to cancel the sync.
+         * @param {Roo.HtmlEditorCore} this
+         * @param {String} html
+         */
+        beforesync: true,
+         /**
+         * @event beforepush
+         * Fires before the iframe editor is updated with content from the textarea. Return false
+         * to cancel the push.
+         * @param {Roo.HtmlEditorCore} this
+         * @param {String} html
+         */
+        beforepush: true,
+         /**
+         * @event sync
+         * Fires when the textarea is updated with content from the editor iframe.
+         * @param {Roo.HtmlEditorCore} this
+         * @param {String} html
+         */
+        sync: true,
+         /**
+         * @event push
+         * Fires when the iframe editor is updated with content from the textarea.
+         * @param {Roo.HtmlEditorCore} this
+         * @param {String} html
+         */
+        push: true,
+        
+        /**
+         * @event editorevent
+         * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+         * @param {Roo.HtmlEditorCore} this
+         */
+        editorevent: true
+        
+    });
+    
+    // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
+    
+    // defaults : white / black...
+    this.applyBlacklists();
+    
+    
+    
+};
+
+
+Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
+
+
+     /**
+     * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
      */
-    /**
-     * @cfg {String} invalidText @hide
+    
+    owner : false,
+    
+     /**
+     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
+     *                        Roo.resizable.
      */
+    resizable : false,
+     /**
+     * @cfg {Number} height (in pixels)
+     */   
+    height: 300,
+   /**
+     * @cfg {Number} width (in pixels)
+     */   
+    width: 500,
+    
     /**
-     * @cfg {String} msgFx @hide
+     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+     * 
      */
+    stylesheets: false,
+    
+    // id of frame..
+    frameId: false,
+    
+    // private properties
+    validationEvent : false,
+    deferHeight: true,
+    initialized : false,
+    activated : false,
+    sourceEditMode : false,
+    onFocus : Roo.emptyFn,
+    iframePad:3,
+    hideMode:'offsets',
+    
+    clearUp: true,
+    
+    // blacklist + whitelisted elements..
+    black: false,
+    white: false,
+     
+    bodyCls : '',
+
     /**
-     * @cfg {String} validateOnBlur @hide
+     * Protected method that will not generally be called directly. It
+     * is called when the editor initializes the iframe with HTML contents. Override this method if you
+     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
      */
-});
+    getDocMarkup : function(){
+        // body styles..
+        var st = '';
+        
+        // inherit styels from page...?? 
+        if (this.stylesheets === false) {
+            
+            Roo.get(document.head).select('style').each(function(node) {
+                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+            });
+            
+            Roo.get(document.head).select('link').each(function(node) { 
+                st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+            });
+            
+        } else if (!this.stylesheets.length) {
+                // simple..
+                st = '<style type="text/css">' +
+                    'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+                   '</style>';
+        } else {
+            for (var i in this.stylesheets) { 
+                st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
+            }
+            
+        }
+        
+        st +=  '<style type="text/css">' +
+            'IMG { cursor: pointer } ' +
+        '</style>';
 
-Roo.HtmlEditorCore.white = [
-        'area', 'br', 'img', 'input', 'hr', 'wbr',
+        var cls = 'roo-htmleditor-body';
         
-       'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
-       'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
-       'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
-       'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
-       'table',   'ul',         'xmp', 
-       
-       'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
-      'thead',   'tr', 
-     
-      'dir', 'menu', 'ol', 'ul', 'dl',
+        if(this.bodyCls.length){
+            cls += ' ' + this.bodyCls;
+        }
+        
+        return '<html><head>' + st  +
+            //<style type="text/css">' +
+            //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+            //'</style>' +
+            ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
+    },
+
+    // private
+    onRender : function(ct, position)
+    {
+        var _t = this;
+        //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
+        this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
+        
+        
+        this.el.dom.style.border = '0 none';
+        this.el.dom.setAttribute('tabIndex', -1);
+        this.el.addClass('x-hidden hide');
+        
+        
+        
+        if(Roo.isIE){ // fix IE 1px bogus margin
+            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
+        }
        
-      'embed',  'object'
-];
+        
+        this.frameId = Roo.id();
+        
+         
+        
+        var iframe = this.owner.wrap.createChild({
+            tag: 'iframe',
+            cls: 'form-control', // bootstrap..
+            id: this.frameId,
+            name: this.frameId,
+            frameBorder : 'no',
+            'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
+        }, this.el
+        );
+        
+        
+        this.iframe = iframe.dom;
 
+         this.assignDocWin();
+        
+        this.doc.designMode = 'on';
+       
+        this.doc.open();
+        this.doc.write(this.getDocMarkup());
+        this.doc.close();
 
-Roo.HtmlEditorCore.black = [
-    //    'embed',  'object', // enable - backend responsiblity to clean thiese
-        'applet', // 
-        'base',   'basefont', 'bgsound', 'blink',  'body', 
-        'frame',  'frameset', 'head',    'html',   'ilayer', 
-        'iframe', 'layer',  'link',     'meta',    'object',   
-        'script', 'style' ,'title',  'xml' // clean later..
-];
-Roo.HtmlEditorCore.clean = [
-    'script', 'style', 'title', 'xml'
-];
-Roo.HtmlEditorCore.remove = [
-    'font'
-];
-// attributes..
+        
+        var task = { // must defer to wait for browser to be ready
+            run : function(){
+                //console.log("run task?" + this.doc.readyState);
+                this.assignDocWin();
+                if(this.doc.body || this.doc.readyState == 'complete'){
+                    try {
+                        this.doc.designMode="on";
+                    } catch (e) {
+                        return;
+                    }
+                    Roo.TaskMgr.stop(task);
+                    this.initEditor.defer(10, this);
+                }
+            },
+            interval : 10,
+            duration: 10000,
+            scope: this
+        };
+        Roo.TaskMgr.start(task);
 
-Roo.HtmlEditorCore.ablack = [
-    'on'
-];
-    
-Roo.HtmlEditorCore.aclean = [ 
-    'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
-];
+    },
 
-// protocols..
-Roo.HtmlEditorCore.pwhite= [
-        'http',  'https',  'mailto'
-];
+    // private
+    onResize : function(w, h)
+    {
+         Roo.log('resize: ' +w + ',' + h );
+        //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
+        if(!this.iframe){
+            return;
+        }
+        if(typeof w == 'number'){
+            
+            this.iframe.style.width = w + 'px';
+        }
+        if(typeof h == 'number'){
+            
+            this.iframe.style.height = h + 'px';
+            if(this.doc){
+                (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
+            }
+        }
+        
+    },
 
-// white listed style attributes.
-Roo.HtmlEditorCore.cwhite= [
-      //  'text-align', /// default is to allow most things..
-      
-         
-//        'font-size'//??
-];
+    /**
+     * Toggles the editor between standard and source edit mode.
+     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+     */
+    toggleSourceEdit : function(sourceEditMode){
+        
+        this.sourceEditMode = sourceEditMode === true;
+        
+        if(this.sourceEditMode){
+            Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
+            
+        }else{
+            Roo.get(this.iframe).removeClass(['x-hidden','hide']);
+            //this.iframe.className = '';
+            this.deferFocus();
+        }
+        //this.setSize(this.owner.wrap.getSize());
+        //this.fireEvent('editmodechange', this, this.sourceEditMode);
+    },
 
-// black listed style attributes.
-Roo.HtmlEditorCore.cblack= [
-      //  'font-size' -- this can be set by the project 
-];
+    
+  
 
+    /**
+     * Protected method that will not generally be called directly. If you need/want
+     * custom HTML cleanup, this is the method you should override.
+     * @param {String} html The HTML to be cleaned
+     * return {String} The cleaned HTML
+     */
+    cleanHtml : function(html){
+        html = String(html);
+        if(html.length > 5){
+            if(Roo.isSafari){ // strip safari nonsense
+                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
+            }
+        }
+        if(html == '&nbsp;'){
+            html = '';
+        }
+        return html;
+    },
 
-Roo.HtmlEditorCore.swapCodes   =[ 
-    [    8211, "&#8211;" ], 
-    [    8212, "&#8212;" ], 
-    [    8216,  "'" ],  
-    [    8217, "'" ],  
-    [    8220, '"' ],  
-    [    8221, '"' ],  
-    [    8226, "*" ],  
-    [    8230, "..." ]
-]; 
+    /**
+     * HTML Editor -> Textarea
+     * Protected method that will not generally be called directly. Syncs the contents
+     * of the editor iframe with the textarea.
+     */
+    syncValue : function(){
+        if(this.initialized){
+            var bd = (this.doc.body || this.doc.documentElement);
+            //this.cleanUpPaste(); -- this is done else where and causes havoc..
+            var html = bd.innerHTML;
+            if(Roo.isSafari){
+                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
+                var m = bs ? bs.match(/text-align:(.*?);/i) : false;
+                if(m && m[1]){
+                    html = '<div style="'+m[0]+'">' + html + '</div>';
+                }
+            }
+            html = this.cleanHtml(html);
+            // fix up the special chars.. normaly like back quotes in word...
+            // however we do not want to do this with chinese..
+            html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
+                
+                var cc = match.charCodeAt();
 
-    /*
- * - LGPL
- *
- * HtmlEditor
- * 
- */
+                // Get the character value, handling surrogate pairs
+                if (match.length == 2) {
+                    // It's a surrogate pair, calculate the Unicode code point
+                    var high = match.charCodeAt(0) - 0xD800;
+                    var low  = match.charCodeAt(1) - 0xDC00;
+                    cc = (high * 0x400) + low + 0x10000;
+                }  else if (
+                    (cc >= 0x4E00 && cc < 0xA000 ) ||
+                    (cc >= 0x3400 && cc < 0x4E00 ) ||
+                    (cc >= 0xf900 && cc < 0xfb00 )
+                ) {
+                        return match;
+                }  
+         
+                // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
+                return "&#" + cc + ";";
+                
+                
+            });
+            
+            
+             
+            if(this.owner.fireEvent('beforesync', this, html) !== false){
+                this.el.dom.value = html;
+                this.owner.fireEvent('sync', this, html);
+            }
+        }
+    },
 
-/**
- * @class Roo.bootstrap.HtmlEditor
- * @extends Roo.bootstrap.TextArea
- * Bootstrap HtmlEditor class
+    /**
+     * Protected method that will not generally be called directly. Pushes the value of the textarea
+     * into the iframe editor.
+     */
+    pushValue : function(){
+        if(this.initialized){
+            var v = this.el.dom.value.trim();
+            
+//            if(v.length < 1){
+//                v = '&#160;';
+//            }
+            
+            if(this.owner.fireEvent('beforepush', this, v) !== false){
+                var d = (this.doc.body || this.doc.documentElement);
+                d.innerHTML = v;
+                this.cleanUpPaste();
+                this.el.dom.value = d.innerHTML;
+                this.owner.fireEvent('push', this, v);
+            }
+        }
+    },
 
- * @constructor
- * Create a new HtmlEditor
- * @param {Object} config The config object
- */
+    // private
+    deferFocus : function(){
+        this.focus.defer(10, this);
+    },
 
-Roo.bootstrap.HtmlEditor = function(config){
-    Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
-    if (!this.toolbars) {
-        this.toolbars = [];
-    }
+    // doc'ed in Field
+    focus : function(){
+        if(this.win && !this.sourceEditMode){
+            this.win.focus();
+        }else{
+            this.el.focus();
+        }
+    },
     
-    this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
-    this.addEvents({
-            /**
-             * @event initialize
-             * Fires when the editor is fully initialized (including the iframe)
-             * @param {HtmlEditor} this
-             */
-            initialize: true,
-            /**
-             * @event activate
-             * Fires when the editor is first receives the focus. Any insertion must wait
-             * until after this event.
-             * @param {HtmlEditor} this
-             */
-            activate: true,
-             /**
-             * @event beforesync
-             * Fires before the textarea is updated with content from the editor iframe. Return false
-             * to cancel the sync.
-             * @param {HtmlEditor} this
-             * @param {String} html
-             */
-            beforesync: true,
-             /**
-             * @event beforepush
-             * Fires before the iframe editor is updated with content from the textarea. Return false
-             * to cancel the push.
-             * @param {HtmlEditor} this
-             * @param {String} html
-             */
-            beforepush: true,
-             /**
-             * @event sync
-             * Fires when the textarea is updated with content from the editor iframe.
-             * @param {HtmlEditor} this
-             * @param {String} html
-             */
-            sync: true,
-             /**
-             * @event push
-             * Fires when the iframe editor is updated with content from the textarea.
-             * @param {HtmlEditor} this
-             * @param {String} html
-             */
-            push: true,
-             /**
-             * @event editmodechange
-             * Fires when the editor switches edit modes
-             * @param {HtmlEditor} this
-             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
-             */
-            editmodechange: true,
-            /**
-             * @event editorevent
-             * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
-             * @param {HtmlEditor} this
-             */
-            editorevent: true,
-            /**
-             * @event firstfocus
-             * Fires when on first focus - needed by toolbars..
-             * @param {HtmlEditor} this
-             */
-            firstfocus: true,
-            /**
-             * @event autosave
-             * Auto save the htmlEditor value as a file into Events
-             * @param {HtmlEditor} this
-             */
-            autosave: true,
-            /**
-             * @event savedpreview
-             * preview the saved version of htmlEditor
-             * @param {HtmlEditor} this
-             */
-            savedpreview: true
+    assignDocWin: function()
+    {
+        var iframe = this.iframe;
+        
+         if(Roo.isIE){
+            this.doc = iframe.contentWindow.document;
+            this.win = iframe.contentWindow;
+        } else {
+//            if (!Roo.get(this.frameId)) {
+//                return;
+//            }
+//            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
+//            this.win = Roo.get(this.frameId).dom.contentWindow;
+            
+            if (!Roo.get(this.frameId) && !iframe.contentDocument) {
+                return;
+            }
+            
+            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
+            this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
+        }
+    },
+    
+    // private
+    initEditor : function(){
+        //console.log("INIT EDITOR");
+        this.assignDocWin();
+        
+        
+        
+        this.doc.designMode="on";
+        this.doc.open();
+        this.doc.write(this.getDocMarkup());
+        this.doc.close();
+        
+        var dbody = (this.doc.body || this.doc.documentElement);
+        //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
+        // this copies styles from the containing element into thsi one..
+        // not sure why we need all of this..
+        //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
+        
+        //var ss = this.el.getStyles( 'background-image', 'background-repeat');
+        //ss['background-attachment'] = 'fixed'; // w3c
+        dbody.bgProperties = 'fixed'; // ie
+        //Roo.DomHelper.applyStyles(dbody, ss);
+        Roo.EventManager.on(this.doc, {
+            //'mousedown': this.onEditorEvent,
+            'mouseup': this.onEditorEvent,
+            'dblclick': this.onEditorEvent,
+            'click': this.onEditorEvent,
+            'keyup': this.onEditorEvent,
+            buffer:100,
+            scope: this
         });
-};
+        if(Roo.isGecko){
+            Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
+        }
+        if(Roo.isIE || Roo.isSafari || Roo.isOpera){
+            Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
+        }
+        this.initialized = true;
 
+        this.owner.fireEvent('initialize', this);
+        this.pushValue();
+    },
 
-Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
+    // private
+    onDestroy : function(){
+        
+        
+        
+        if(this.rendered){
+            
+            //for (var i =0; i < this.toolbars.length;i++) {
+            //    // fixme - ask toolbars for heights?
+            //    this.toolbars[i].onDestroy();
+           // }
+            
+            //this.wrap.dom.innerHTML = '';
+            //this.wrap.remove();
+        }
+    },
+
+    // private
+    onFirstFocus : function(){
+        
+        this.assignDocWin();
+        
+        
+        this.activated = true;
+         
+    
+        if(Roo.isGecko){ // prevent silly gecko errors
+            this.win.focus();
+            var s = this.win.getSelection();
+            if(!s.focusNode || s.focusNode.nodeType != 3){
+                var r = s.getRangeAt(0);
+                r.selectNodeContents((this.doc.body || this.doc.documentElement));
+                r.collapse(true);
+                this.deferFocus();
+            }
+            try{
+                this.execCmd('useCSS', true);
+                this.execCmd('styleWithCSS', false);
+            }catch(e){}
+        }
+        this.owner.fireEvent('activate', this);
+    },
+
+    // private
+    adjustFont: function(btn){
+        var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
+        //if(Roo.isSafari){ // safari
+        //    adjust *= 2;
+       // }
+        var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
+        if(Roo.isSafari){ // safari
+            var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
+            v =  (v < 10) ? 10 : v;
+            v =  (v > 48) ? 48 : v;
+            v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
+            
+        }
+        
+        
+        v = Math.max(1, v+adjust);
+        
+        this.execCmd('FontSize', v  );
+    },
+
+    onEditorEvent : function(e)
+    {
+        this.owner.fireEvent('editorevent', this, e);
+      //  this.updateToolbar();
+        this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
+    },
+
+    insertTag : function(tg)
+    {
+        // could be a bit smarter... -> wrap the current selected tRoo..
+        if (tg.toLowerCase() == 'span' ||
+            tg.toLowerCase() == 'code' ||
+            tg.toLowerCase() == 'sup' ||
+            tg.toLowerCase() == 'sub' 
+            ) {
+            
+            range = this.createRange(this.getSelection());
+            var wrappingNode = this.doc.createElement(tg.toLowerCase());
+            wrappingNode.appendChild(range.extractContents());
+            range.insertNode(wrappingNode);
+
+            return;
+            
+            
+            
+        }
+        this.execCmd("formatblock",   tg);
+        
+    },
     
+    insertText : function(txt)
+    {
+        
+        
+        var range = this.createRange();
+        range.deleteContents();
+               //alert(Sender.getAttribute('label'));
+               
+        range.insertNode(this.doc.createTextNode(txt));
+    } ,
     
-      /**
-     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+     
+
+    /**
+     * Executes a Midas editor command on the editor document and performs necessary focus and
+     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
+     * @param {String} cmd The Midas command
+     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
+     */
+    relayCmd : function(cmd, value){
+        this.win.focus();
+        this.execCmd(cmd, value);
+        this.owner.fireEvent('editorevent', this);
+        //this.updateToolbar();
+        this.owner.deferFocus();
+    },
+
+    /**
+     * Executes a Midas editor command directly on the editor document.
+     * For visual commands, you should use {@link #relayCmd} instead.
+     * <b>This should only be called after the editor is initialized.</b>
+     * @param {String} cmd The Midas command
+     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
+     */
+    execCmd : function(cmd, value){
+        this.doc.execCommand(cmd, false, value === undefined ? null : value);
+        this.syncValue();
+    },
+   
+    /**
+     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
+     * to insert tRoo.
+     * @param {String} text | dom node.. 
+     */
+    insertAtCursor : function(text)
+    {
+        
+        if(!this.activated){
+            return;
+        }
+        /*
+        if(Roo.isIE){
+            this.win.focus();
+            var r = this.doc.selection.createRange();
+            if(r){
+                r.collapse(true);
+                r.pasteHTML(text);
+                this.syncValue();
+                this.deferFocus();
+            
+            }
+            return;
+        }
+        */
+        if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
+            this.win.focus();
+            
+            
+            // from jquery ui (MIT licenced)
+            var range, node;
+            var win = this.win;
+            
+            if (win.getSelection && win.getSelection().getRangeAt) {
+                range = win.getSelection().getRangeAt(0);
+                node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
+                range.insertNode(node);
+            } else if (win.document.selection && win.document.selection.createRange) {
+                // no firefox support
+                var txt = typeof(text) == 'string' ? text : text.outerHTML;
+                win.document.selection.createRange().pasteHTML(txt);
+            } else {
+                // no firefox support
+                var txt = typeof(text) == 'string' ? text : text.outerHTML;
+                this.execCmd('InsertHTML', txt);
+            } 
+            
+            this.syncValue();
+            
+            this.deferFocus();
+        }
+    },
+ // private
+    mozKeyPress : function(e){
+        if(e.ctrlKey){
+            var c = e.getCharCode(), cmd;
+          
+            if(c > 0){
+                c = String.fromCharCode(c).toLowerCase();
+                switch(c){
+                    case 'b':
+                        cmd = 'bold';
+                        break;
+                    case 'i':
+                        cmd = 'italic';
+                        break;
+                    
+                    case 'u':
+                        cmd = 'underline';
+                        break;
+                    
+                    case 'v':
+                        this.cleanUpPaste.defer(100, this);
+                        return;
+                        
+                }
+                if(cmd){
+                    this.win.focus();
+                    this.execCmd(cmd);
+                    this.deferFocus();
+                    e.preventDefault();
+                }
+                
+            }
+        }
+    },
+
+    // private
+    fixKeys : function(){ // load time branching for fastest keydown performance
+        if(Roo.isIE){
+            return function(e){
+                var k = e.getKey(), r;
+                if(k == e.TAB){
+                    e.stopEvent();
+                    r = this.doc.selection.createRange();
+                    if(r){
+                        r.collapse(true);
+                        r.pasteHTML('&#160;&#160;&#160;&#160;');
+                        this.deferFocus();
+                    }
+                    return;
+                }
+                
+                if(k == e.ENTER){
+                    r = this.doc.selection.createRange();
+                    if(r){
+                        var target = r.parentElement();
+                        if(!target || target.tagName.toLowerCase() != 'li'){
+                            e.stopEvent();
+                            r.pasteHTML('<br />');
+                            r.collapse(false);
+                            r.select();
+                        }
+                    }
+                }
+                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+                    this.cleanUpPaste.defer(100, this);
+                    return;
+                }
+                
+                
+            };
+        }else if(Roo.isOpera){
+            return function(e){
+                var k = e.getKey();
+                if(k == e.TAB){
+                    e.stopEvent();
+                    this.win.focus();
+                    this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
+                    this.deferFocus();
+                }
+                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+                    this.cleanUpPaste.defer(100, this);
+                    return;
+                }
+                
+            };
+        }else if(Roo.isSafari){
+            return function(e){
+                var k = e.getKey();
+                
+                if(k == e.TAB){
+                    e.stopEvent();
+                    this.execCmd('InsertText','\t');
+                    this.deferFocus();
+                    return;
+                }
+               if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+                    this.cleanUpPaste.defer(100, this);
+                    return;
+                }
+                
+             };
+        }
+    }(),
+    
+    getAllAncestors: function()
+    {
+        var p = this.getSelectedNode();
+        var a = [];
+        if (!p) {
+            a.push(p); // push blank onto stack..
+            p = this.getParentElement();
+        }
+        
+        
+        while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+            a.push(p);
+            p = p.parentNode;
+        }
+        a.push(this.doc.body);
+        return a;
+    },
+    lastSel : false,
+    lastSelNode : false,
+    
+    
+    getSelection : function() 
+    {
+        this.assignDocWin();
+        return Roo.isIE ? this.doc.selection : this.win.getSelection();
+    },
+    
+    getSelectedNode: function() 
+    {
+        // this may only work on Gecko!!!
+        
+        // should we cache this!!!!
+        
+        
+        
+         
+        var range = this.createRange(this.getSelection()).cloneRange();
+        
+        if (Roo.isIE) {
+            var parent = range.parentElement();
+            while (true) {
+                var testRange = range.duplicate();
+                testRange.moveToElementText(parent);
+                if (testRange.inRange(range)) {
+                    break;
+                }
+                if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
+                    break;
+                }
+                parent = parent.parentElement;
+            }
+            return parent;
+        }
+        
+        // is ancestor a text element.
+        var ac =  range.commonAncestorContainer;
+        if (ac.nodeType == 3) {
+            ac = ac.parentNode;
+        }
+        
+        var ar = ac.childNodes;
+         
+        var nodes = [];
+        var other_nodes = [];
+        var has_other_nodes = false;
+        for (var i=0;i<ar.length;i++) {
+            if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
+                continue;
+            }
+            // fullly contained node.
+            
+            if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
+                nodes.push(ar[i]);
+                continue;
+            }
+            
+            // probably selected..
+            if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
+                other_nodes.push(ar[i]);
+                continue;
+            }
+            // outer..
+            if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
+                continue;
+            }
+            
+            
+            has_other_nodes = true;
+        }
+        if (!nodes.length && other_nodes.length) {
+            nodes= other_nodes;
+        }
+        if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
+            return false;
+        }
+        
+        return nodes[0];
+    },
+    createRange: function(sel)
+    {
+        // this has strange effects when using with 
+        // top toolbar - not sure if it's a great idea.
+        //this.editor.contentWindow.focus();
+        if (typeof sel != "undefined") {
+            try {
+                return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
+            } catch(e) {
+                return this.doc.createRange();
+            }
+        } else {
+            return this.doc.createRange();
+        }
+    },
+    getParentElement: function()
+    {
+        
+        this.assignDocWin();
+        var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
+        
+        var range = this.createRange(sel);
+         
+        try {
+            var p = range.commonAncestorContainer;
+            while (p.nodeType == 3) { // text node
+                p = p.parentNode;
+            }
+            return p;
+        } catch (e) {
+            return null;
+        }
+    
+    },
+    /***
+     *
+     * Range intersection.. the hard stuff...
+     *  '-1' = before
+     *  '0' = hits..
+     *  '1' = after.
+     *         [ -- selected range --- ]
+     *   [fail]                        [fail]
+     *
+     *    basically..
+     *      if end is before start or  hits it. fail.
+     *      if start is after end or hits it fail.
+     *
+     *   if either hits (but other is outside. - then it's not 
+     *   
+     *    
+     **/
+    
+    
+    // @see http://www.thismuchiknow.co.uk/?p=64.
+    rangeIntersectsNode : function(range, node)
+    {
+        var nodeRange = node.ownerDocument.createRange();
+        try {
+            nodeRange.selectNode(node);
+        } catch (e) {
+            nodeRange.selectNodeContents(node);
+        }
+    
+        var rangeStartRange = range.cloneRange();
+        rangeStartRange.collapse(true);
+    
+        var rangeEndRange = range.cloneRange();
+        rangeEndRange.collapse(false);
+    
+        var nodeStartRange = nodeRange.cloneRange();
+        nodeStartRange.collapse(true);
+    
+        var nodeEndRange = nodeRange.cloneRange();
+        nodeEndRange.collapse(false);
+    
+        return rangeStartRange.compareBoundaryPoints(
+                 Range.START_TO_START, nodeEndRange) == -1 &&
+               rangeEndRange.compareBoundaryPoints(
+                 Range.START_TO_START, nodeStartRange) == 1;
+        
+         
+    },
+    rangeCompareNode : function(range, node)
+    {
+        var nodeRange = node.ownerDocument.createRange();
+        try {
+            nodeRange.selectNode(node);
+        } catch (e) {
+            nodeRange.selectNodeContents(node);
+        }
+        
+        
+        range.collapse(true);
+    
+        nodeRange.collapse(true);
+     
+        var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
+        var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
+         
+        //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
+        
+        var nodeIsBefore   =  ss == 1;
+        var nodeIsAfter    = ee == -1;
+        
+        if (nodeIsBefore && nodeIsAfter) {
+            return 0; // outer
+        }
+        if (!nodeIsBefore && nodeIsAfter) {
+            return 1; //right trailed.
+        }
+        
+        if (nodeIsBefore && !nodeIsAfter) {
+            return 2;  // left trailed.
+        }
+        // fully contined.
+        return 3;
+    },
+
+    // private? - in a new class?
+    cleanUpPaste :  function()
+    {
+        // cleans up the whole document..
+        Roo.log('cleanuppaste');
+        
+        this.cleanUpChildren(this.doc.body);
+        var clean = this.cleanWordChars(this.doc.body.innerHTML);
+        if (clean != this.doc.body.innerHTML) {
+            this.doc.body.innerHTML = clean;
+        }
+        
+    },
+    
+    cleanWordChars : function(input) {// change the chars to hex code
+        var he = Roo.HtmlEditorCore;
+        
+        var output = input;
+        Roo.each(he.swapCodes, function(sw) { 
+            var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
+            
+            output = output.replace(swapper, sw[1]);
+        });
+        
+        return output;
+    },
+    
+    
+    cleanUpChildren : function (n)
+    {
+        if (!n.childNodes.length) {
+            return;
+        }
+        for (var i = n.childNodes.length-1; i > -1 ; i--) {
+           this.cleanUpChild(n.childNodes[i]);
+        }
+    },
+    
+    
+        
+    
+    cleanUpChild : function (node)
+    {
+        var ed = this;
+        //console.log(node);
+        if (node.nodeName == "#text") {
+            // clean up silly Windows -- stuff?
+            return; 
+        }
+        if (node.nodeName == "#comment") {
+            node.parentNode.removeChild(node);
+            // clean up silly Windows -- stuff?
+            return; 
+        }
+        var lcname = node.tagName.toLowerCase();
+        // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
+        // whitelist of tags..
+        
+        if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
+            // remove node.
+            node.parentNode.removeChild(node);
+            return;
+            
+        }
+        
+        var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
+        
+        // spans with no attributes - just remove them..
+        if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
+            remove_keep_children = true;
+        }
+        
+        // remove <a name=....> as rendering on yahoo mailer is borked with this.
+        // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
+        
+        //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
+        //    remove_keep_children = true;
+        //}
+        
+        if (remove_keep_children) {
+            this.cleanUpChildren(node);
+            // inserts everything just before this node...
+            while (node.childNodes.length) {
+                var cn = node.childNodes[0];
+                node.removeChild(cn);
+                node.parentNode.insertBefore(cn, node);
+            }
+            node.parentNode.removeChild(node);
+            return;
+        }
+        
+        if (!node.attributes || !node.attributes.length) {
+            
+          
+            
+            
+            this.cleanUpChildren(node);
+            return;
+        }
+        
+        function cleanAttr(n,v)
+        {
+            
+            if (v.match(/^\./) || v.match(/^\//)) {
+                return;
+            }
+            if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
+                return;
+            }
+            if (v.match(/^#/)) {
+                return;
+            }
+            if (v.match(/^\{/)) { // allow template editing.
+                return;
+            }
+//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
+            node.removeAttribute(n);
+            
+        }
+        
+        var cwhite = this.cwhite;
+        var cblack = this.cblack;
+            
+        function cleanStyle(n,v)
+        {
+            if (v.match(/expression/)) { //XSS?? should we even bother..
+                node.removeAttribute(n);
+                return;
+            }
+            
+            var parts = v.split(/;/);
+            var clean = [];
+            
+            Roo.each(parts, function(p) {
+                p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
+                if (!p.length) {
+                    return true;
+                }
+                var l = p.split(':').shift().replace(/\s+/g,'');
+                l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
+                
+                if ( cwhite.length && cblack.indexOf(l) > -1) {
+//                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
+                    //node.removeAttribute(n);
+                    return true;
+                }
+                //Roo.log()
+                // only allow 'c whitelisted system attributes'
+                if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
+//                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
+                    //node.removeAttribute(n);
+                    return true;
+                }
+                
+                
+                 
+                
+                clean.push(p);
+                return true;
+            });
+            if (clean.length) { 
+                node.setAttribute(n, clean.join(';'));
+            } else {
+                node.removeAttribute(n);
+            }
+            
+        }
+        
+        
+        for (var i = node.attributes.length-1; i > -1 ; i--) {
+            var a = node.attributes[i];
+            //console.log(a);
+            
+            if (a.name.toLowerCase().substr(0,2)=='on')  {
+                node.removeAttribute(a.name);
+                continue;
+            }
+            if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
+                node.removeAttribute(a.name);
+                continue;
+            }
+            if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
+                cleanAttr(a.name,a.value); // fixme..
+                continue;
+            }
+            if (a.name == 'style') {
+                cleanStyle(a.name,a.value);
+                continue;
+            }
+            /// clean up MS crap..
+            // tecnically this should be a list of valid class'es..
+            
+            
+            if (a.name == 'class') {
+                if (a.value.match(/^Mso/)) {
+                    node.removeAttribute('class');
+                }
+                
+                if (a.value.match(/^body$/)) {
+                    node.removeAttribute('class');
+                }
+                continue;
+            }
+            
+            // style cleanup!?
+            // class cleanup?
+            
+        }
+        
+        
+        this.cleanUpChildren(node);
+        
+        
+    },
+    
+    /**
+     * Clean up MS wordisms...
+     */
+    cleanWord : function(node)
+    {
+        if (!node) {
+            this.cleanWord(this.doc.body);
+            return;
+        }
+        
+        if(
+                node.nodeName == 'SPAN' &&
+                !node.hasAttributes() &&
+                node.childNodes.length == 1 &&
+                node.firstChild.nodeName == "#text"  
+        ) {
+            var textNode = node.firstChild;
+            node.removeChild(textNode);
+            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
+                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
+            }
+            node.parentNode.insertBefore(textNode, node);
+            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
+                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
+            }
+            node.parentNode.removeChild(node);
+        }
+        
+        if (node.nodeName == "#text") {
+            // clean up silly Windows -- stuff?
+            return; 
+        }
+        if (node.nodeName == "#comment") {
+            node.parentNode.removeChild(node);
+            // clean up silly Windows -- stuff?
+            return; 
+        }
+        
+        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+            node.parentNode.removeChild(node);
+            return;
+        }
+        //Roo.log(node.tagName);
+        // remove - but keep children..
+        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
+            //Roo.log('-- removed');
+            while (node.childNodes.length) {
+                var cn = node.childNodes[0];
+                node.removeChild(cn);
+                node.parentNode.insertBefore(cn, node);
+                // move node to parent - and clean it..
+                this.cleanWord(cn);
+            }
+            node.parentNode.removeChild(node);
+            /// no need to iterate chidlren = it's got none..
+            //this.iterateChildren(node, this.cleanWord);
+            return;
+        }
+        // clean styles
+        if (node.className.length) {
+            
+            var cn = node.className.split(/\W+/);
+            var cna = [];
+            Roo.each(cn, function(cls) {
+                if (cls.match(/Mso[a-zA-Z]+/)) {
+                    return;
+                }
+                cna.push(cls);
+            });
+            node.className = cna.length ? cna.join(' ') : '';
+            if (!cna.length) {
+                node.removeAttribute("class");
+            }
+        }
+        
+        if (node.hasAttribute("lang")) {
+            node.removeAttribute("lang");
+        }
+        
+        if (node.hasAttribute("style")) {
+            
+            var styles = node.getAttribute("style").split(";");
+            var nstyle = [];
+            Roo.each(styles, function(s) {
+                if (!s.match(/:/)) {
+                    return;
+                }
+                var kv = s.split(":");
+                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
+                    return;
+                }
+                // what ever is left... we allow.
+                nstyle.push(s);
+            });
+            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+            if (!nstyle.length) {
+                node.removeAttribute('style');
+            }
+        }
+        this.iterateChildren(node, this.cleanWord);
+        
+        
+        
+    },
+    /**
+     * iterateChildren of a Node, calling fn each time, using this as the scole..
+     * @param {DomNode} node node to iterate children of.
+     * @param {Function} fn method of this class to call on each item.
      */
-    toolbars : false,
+    iterateChildren : function(node, fn)
+    {
+        if (!node.childNodes.length) {
+                return;
+        }
+        for (var i = node.childNodes.length-1; i > -1 ; i--) {
+           fn.call(this, node.childNodes[i])
+        }
+    },
     
-     /**
-    * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
-    */
-    btns : [],
-   
-     /**
-     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
-     *                        Roo.resizable.
-     */
-    resizable : false,
-     /**
-     * @cfg {Number} height (in pixels)
-     */   
-    height: 300,
-   /**
-     * @cfg {Number} width (in pixels)
-     */   
-    width: false,
     
     /**
-     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
-     * 
+     * cleanTableWidths.
+     *
+     * Quite often pasting from word etc.. results in tables with column and widths.
+     * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
+     *
      */
-    stylesheets: false,
-    
-    // id of frame..
-    frameId: false,
+    cleanTableWidths : function(node)
+    {
+         
+         
+        if (!node) {
+            this.cleanTableWidths(this.doc.body);
+            return;
+        }
+        
+        // ignore list...
+        if (node.nodeName == "#text" || node.nodeName == "#comment") {
+            return; 
+        }
+        Roo.log(node.tagName);
+        if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
+            this.iterateChildren(node, this.cleanTableWidths);
+            return;
+        }
+        if (node.hasAttribute('width')) {
+            node.removeAttribute('width');
+        }
+        
+         
+        if (node.hasAttribute("style")) {
+            // pretty basic...
+            
+            var styles = node.getAttribute("style").split(";");
+            var nstyle = [];
+            Roo.each(styles, function(s) {
+                if (!s.match(/:/)) {
+                    return;
+                }
+                var kv = s.split(":");
+                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
+                    return;
+                }
+                // what ever is left... we allow.
+                nstyle.push(s);
+            });
+            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+            if (!nstyle.length) {
+                node.removeAttribute('style');
+            }
+        }
+        
+        this.iterateChildren(node, this.cleanTableWidths);
+        
+        
+    },
     
-    // private properties
-    validationEvent : false,
-    deferHeight: true,
-    initialized : false,
-    activated : false,
     
-    onFocus : Roo.emptyFn,
-    iframePad:3,
-    hideMode:'offsets',
     
-    tbContainer : false,
     
-    bodyCls : '',
+    domToHTML : function(currentElement, depth, nopadtext) {
+        
+        depth = depth || 0;
+        nopadtext = nopadtext || false;
     
-    toolbarContainer :function() {
-        return this.wrap.select('.x-html-editor-tb',true).first();
-    },
-
-    /**
-     * Protected method that will not generally be called directly. It
-     * is called when the editor creates its toolbar. Override this method if you need to
-     * add custom toolbar buttons.
-     * @param {HtmlEditor} editor
-     */
-    createToolbar : function(){
-        Roo.log('renewing');
-        Roo.log("create toolbars");
+        if (!currentElement) {
+            return this.domToHTML(this.doc.body);
+        }
         
-        this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
-        this.toolbars[0].render(this.toolbarContainer());
+        //Roo.log(currentElement);
+        var j;
+        var allText = false;
+        var nodeName = currentElement.nodeName;
+        var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
         
-        return;
+        if  (nodeName == '#text') {
+            
+            return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
+        }
         
-//        if (!editor.toolbars || !editor.toolbars.length) {
-//            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
-//        }
-//        
-//        for (var i =0 ; i < editor.toolbars.length;i++) {
-//            editor.toolbars[i] = Roo.factory(
-//                    typeof(editor.toolbars[i]) == 'string' ?
-//                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
-//                Roo.bootstrap.HtmlEditor);
-//            editor.toolbars[i].init(editor);
-//        }
-    },
-
-     
-    // private
-    onRender : function(ct, position)
-    {
-       // Roo.log("Call onRender: " + this.xtype);
-        var _t = this;
-        Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
-      
-        this.wrap = this.inputEl().wrap({
-            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
-        });
         
-        this.editorcore.onRender(ct, position);
-         
-        if (this.resizable) {
-            this.resizeEl = new Roo.Resizable(this.wrap, {
-                pinned : true,
-                wrap: true,
-                dynamic : true,
-                minHeight : this.height,
-                height: this.height,
-                handles : this.resizable,
-                width: this.width,
-                listeners : {
-                    resize : function(r, w, h) {
-                        _t.onResize(w,h); // -something
+        var ret = '';
+        if (nodeName != 'BODY') {
+             
+            var i = 0;
+            // Prints the node tagName, such as <A>, <IMG>, etc
+            if (tagName) {
+                var attr = [];
+                for(i = 0; i < currentElement.attributes.length;i++) {
+                    // quoting?
+                    var aname = currentElement.attributes.item(i).name;
+                    if (!currentElement.attributes.item(i).value.length) {
+                        continue;
                     }
+                    attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
+                }
+                
+                ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
+            } 
+            else {
+                
+                // eack
+            }
+        } else {
+            tagName = false;
+        }
+        if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
+            return ret;
+        }
+        if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
+            nopadtext = true;
+        }
+        
+        
+        // Traverse the tree
+        i = 0;
+        var currentElementChild = currentElement.childNodes.item(i);
+        var allText = true;
+        var innerHTML  = '';
+        lastnode = '';
+        while (currentElementChild) {
+            // Formatting code (indent the tree so it looks nice on the screen)
+            var nopad = nopadtext;
+            if (lastnode == 'SPAN') {
+                nopad  = true;
+            }
+            // text
+            if  (currentElementChild.nodeName == '#text') {
+                var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
+                toadd = nopadtext ? toadd : toadd.trim();
+                if (!nopad && toadd.length > 80) {
+                    innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
                 }
-            });
+                innerHTML  += toadd;
+                
+                i++;
+                currentElementChild = currentElement.childNodes.item(i);
+                lastNode = '';
+                continue;
+            }
+            allText = false;
             
+            innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
+                
+            // Recursively traverse the tree structure of the child node
+            innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
+            lastnode = currentElementChild.nodeName;
+            i++;
+            currentElementChild=currentElement.childNodes.item(i);
         }
-        this.createToolbar(this);
-       
         
-        if(!this.width && this.resizable){
-            this.setSize(this.wrap.getSize());
+        ret += innerHTML;
+        
+        if (!allText) {
+                // The remaining code is mostly for formatting the tree
+            ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
         }
-        if (this.resizeEl) {
-            this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
-            // should trigger onReize..
+        
+        
+        if (tagName) {
+            ret+= "</"+tagName+">";
         }
+        return ret;
         
     },
-
-    // private
-    onResize : function(w, h)
+        
+    applyBlacklists : function()
     {
-        Roo.log('resize: ' +w + ',' + h );
-        Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
-        var ew = false;
-        var eh = false;
+        var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
+        var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
         
-        if(this.inputEl() ){
-            if(typeof w == 'number'){
-                var aw = w - this.wrap.getFrameWidth('lr');
-                this.inputEl().setWidth(this.adjustWidth('textarea', aw));
-                ew = aw;
+        this.white = [];
+        this.black = [];
+        Roo.each(Roo.HtmlEditorCore.white, function(tag) {
+            if (b.indexOf(tag) > -1) {
+                return;
             }
-            if(typeof h == 'number'){
-                 var tbh = -11;  // fixme it needs to tool bar size!
-                for (var i =0; i < this.toolbars.length;i++) {
-                    // fixme - ask toolbars for heights?
-                    tbh += this.toolbars[i].el.getHeight();
-                    //if (this.toolbars[i].footer) {
-                    //    tbh += this.toolbars[i].footer.el.getHeight();
-                    //}
-                }
-              
-                
-                
-                
-                
-                var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
-                ah -= 5; // knock a few pixes off for look..
-                this.inputEl().setHeight(this.adjustWidth('textarea', ah));
-                var eh = ah;
+            this.white.push(tag);
+            
+        }, this);
+        
+        Roo.each(w, function(tag) {
+            if (b.indexOf(tag) > -1) {
+                return;
             }
-        }
-        Roo.log('onResize:' + [w,h,ew,eh].join(',') );
-        this.editorcore.onResize(ew,eh);
+            if (this.white.indexOf(tag) > -1) {
+                return;
+            }
+            this.white.push(tag);
+            
+        }, this);
         
-    },
-
-    /**
-     * Toggles the editor between standard and source edit mode.
-     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
-     */
-    toggleSourceEdit : function(sourceEditMode)
-    {
-        this.editorcore.toggleSourceEdit(sourceEditMode);
         
-        if(this.editorcore.sourceEditMode){
-            Roo.log('editor - showing textarea');
+        Roo.each(Roo.HtmlEditorCore.black, function(tag) {
+            if (w.indexOf(tag) > -1) {
+                return;
+            }
+            this.black.push(tag);
             
-//            Roo.log('in');
-//            Roo.log(this.syncValue());
-            this.syncValue();
-            this.inputEl().removeClass(['hide', 'x-hidden']);
-            this.inputEl().dom.removeAttribute('tabIndex');
-            this.inputEl().focus();
-        }else{
-            Roo.log('editor - hiding textarea');
-//            Roo.log('out')
-//            Roo.log(this.pushValue()); 
-            this.pushValue();
+        }, this);
+        
+        Roo.each(b, function(tag) {
+            if (w.indexOf(tag) > -1) {
+                return;
+            }
+            if (this.black.indexOf(tag) > -1) {
+                return;
+            }
+            this.black.push(tag);
             
-            this.inputEl().addClass(['hide', 'x-hidden']);
-            this.inputEl().dom.setAttribute('tabIndex', -1);
-            //this.deferFocus();
-        }
-         
-        if(this.resizable){
-            this.setSize(this.wrap.getSize());
-        }
+        }, this);
         
-        this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
-    },
-    // private (for BoxComponent)
-    adjustSize : Roo.BoxComponent.prototype.adjustSize,
-
-    // private (for BoxComponent)
-    getResizeEl : function(){
-        return this.wrap;
-    },
-
-    // private (for BoxComponent)
-    getPositionEl : function(){
-        return this.wrap;
-    },
-
-    // private
-    initEvents : function(){
-        this.originalValue = this.getValue();
-    },
-
-//    /**
-//     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
-//     * @method
-//     */
-//    markInvalid : Roo.emptyFn,
-//    /**
-//     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
-//     * @method
-//     */
-//    clearInvalid : Roo.emptyFn,
-
-    setValue : function(v){
-        Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
-        this.editorcore.pushValue();
-    },
-
-     
-    // private
-    deferFocus : function(){
-        this.focus.defer(10, this);
-    },
-
-    // doc'ed in Field
-    focus : function(){
-        this.editorcore.focus();
         
-    },
-      
-
-    // private
-    onDestroy : function(){
+        w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
+        b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
+        
+        this.cwhite = [];
+        this.cblack = [];
+        Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
+            if (b.indexOf(tag) > -1) {
+                return;
+            }
+            this.cwhite.push(tag);
+            
+        }, this);
         
+        Roo.each(w, function(tag) {
+            if (b.indexOf(tag) > -1) {
+                return;
+            }
+            if (this.cwhite.indexOf(tag) > -1) {
+                return;
+            }
+            this.cwhite.push(tag);
+            
+        }, this);
         
         
-        if(this.rendered){
+        Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
+            if (w.indexOf(tag) > -1) {
+                return;
+            }
+            this.cblack.push(tag);
             
-            for (var i =0; i < this.toolbars.length;i++) {
-                // fixme - ask toolbars for heights?
-                this.toolbars[i].onDestroy();
+        }, this);
+        
+        Roo.each(b, function(tag) {
+            if (w.indexOf(tag) > -1) {
+                return;
             }
+            if (this.cblack.indexOf(tag) > -1) {
+                return;
+            }
+            this.cblack.push(tag);
             
-            this.wrap.dom.innerHTML = '';
-            this.wrap.remove();
-        }
+        }, this);
     },
+    
+    setStylesheets : function(stylesheets)
+    {
+        if(typeof(stylesheets) == 'string'){
+            Roo.get(this.iframe.contentDocument.head).createChild({
+                tag : 'link',
+                rel : 'stylesheet',
+                type : 'text/css',
+                href : stylesheets
+            });
+            
+            return;
+        }
+        var _this = this;
+     
+        Roo.each(stylesheets, function(s) {
+            if(!s.length){
+                return;
+            }
+            
+            Roo.get(_this.iframe.contentDocument.head).createChild({
+                tag : 'link',
+                rel : 'stylesheet',
+                type : 'text/css',
+                href : s
+            });
+        });
 
-    // private
-    onFirstFocus : function(){
-        //Roo.log("onFirstFocus");
-        this.editorcore.onFirstFocus();
-         for (var i =0; i < this.toolbars.length;i++) {
-            this.toolbars[i].onFirstFocus();
-        }
         
     },
     
-    // private
-    syncValue : function()
-    {   
-        this.editorcore.syncValue();
+    removeStylesheets : function()
+    {
+        var _this = this;
+        
+        Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
+            s.remove();
+        });
     },
     
-    pushValue : function()
-    {   
-        this.editorcore.pushValue();
+    setStyle : function(style)
+    {
+        Roo.get(this.iframe.contentDocument.head).createChild({
+            tag : 'style',
+            type : 'text/css',
+            html : style
+        });
+
+        return;
     }
-     
     
     // hide stuff that is not compatible
     /**
@@ -26623,7 +27145,9 @@ Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
     /**
      * @cfg {String} inputType @hide
      */
-     
+    /**
+     * @cfg {String} invalidClass @hide
+     */
     /**
      * @cfg {String} invalidText @hide
      */
@@ -26634,939 +27158,895 @@ Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
      * @cfg {String} validateOnBlur @hide
      */
 });
+
+Roo.HtmlEditorCore.white = [
+        'area', 'br', 'img', 'input', 'hr', 'wbr',
+        
+       'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
+       'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
+       'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
+       'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
+       'table',   'ul',         'xmp', 
+       
+       'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
+      'thead',   'tr', 
+     
+      'dir', 'menu', 'ol', 'ul', 'dl',
+       
+      'embed',  'object'
+];
+
+
+Roo.HtmlEditorCore.black = [
+    //    'embed',  'object', // enable - backend responsiblity to clean thiese
+        'applet', // 
+        'base',   'basefont', 'bgsound', 'blink',  'body', 
+        'frame',  'frameset', 'head',    'html',   'ilayer', 
+        'iframe', 'layer',  'link',     'meta',    'object',   
+        'script', 'style' ,'title',  'xml' // clean later..
+];
+Roo.HtmlEditorCore.clean = [
+    'script', 'style', 'title', 'xml'
+];
+Roo.HtmlEditorCore.remove = [
+    'font'
+];
+// attributes..
+
+Roo.HtmlEditorCore.ablack = [
+    'on'
+];
     
-   
-   
-   
+Roo.HtmlEditorCore.aclean = [ 
+    'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
+];
+
+// protocols..
+Roo.HtmlEditorCore.pwhite= [
+        'http',  'https',  'mailto'
+];
+
+// white listed style attributes.
+Roo.HtmlEditorCore.cwhite= [
+      //  'text-align', /// default is to allow most things..
       
-Roo.namespace('Roo.bootstrap.htmleditor');
-/**
- * @class Roo.bootstrap.HtmlEditorToolbar1
- * Basic Toolbar
- * 
- * @example
- * Usage:
+         
+//        'font-size'//??
+];
+
+// black listed style attributes.
+Roo.HtmlEditorCore.cblack= [
+      //  'font-size' -- this can be set by the project 
+];
+
+
+Roo.HtmlEditorCore.swapCodes   =[ 
+    [    8211, "&#8211;" ], 
+    [    8212, "&#8212;" ], 
+    [    8216,  "'" ],  
+    [    8217, "'" ],  
+    [    8220, '"' ],  
+    [    8221, '"' ],  
+    [    8226, "*" ],  
+    [    8230, "..." ]
+]; 
+
+    /*
+ * - LGPL
  *
- new Roo.bootstrap.HtmlEditor({
-    ....
-    toolbars : [
-        new Roo.bootstrap.HtmlEditorToolbar1({
-            disable : { fonts: 1 , format: 1, ..., ... , ...],
-            btns : [ .... ]
-        })
-    }
-     
- * 
- * @cfg {Object} disable List of elements to disable..
- * @cfg {Array} btns List of additional buttons.
- * 
+ * HtmlEditor
  * 
- * NEEDS Extra CSS? 
- * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
  */
-Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
-{
+
+/**
+ * @class Roo.bootstrap.HtmlEditor
+ * @extends Roo.bootstrap.TextArea
+ * Bootstrap HtmlEditor class
+
+ * @constructor
+ * Create a new HtmlEditor
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.HtmlEditor = function(config){
+    Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
+    if (!this.toolbars) {
+        this.toolbars = [];
+    }
     
-    Roo.apply(this, config);
+    this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
+    this.addEvents({
+            /**
+             * @event initialize
+             * Fires when the editor is fully initialized (including the iframe)
+             * @param {HtmlEditor} this
+             */
+            initialize: true,
+            /**
+             * @event activate
+             * Fires when the editor is first receives the focus. Any insertion must wait
+             * until after this event.
+             * @param {HtmlEditor} this
+             */
+            activate: true,
+             /**
+             * @event beforesync
+             * Fires before the textarea is updated with content from the editor iframe. Return false
+             * to cancel the sync.
+             * @param {HtmlEditor} this
+             * @param {String} html
+             */
+            beforesync: true,
+             /**
+             * @event beforepush
+             * Fires before the iframe editor is updated with content from the textarea. Return false
+             * to cancel the push.
+             * @param {HtmlEditor} this
+             * @param {String} html
+             */
+            beforepush: true,
+             /**
+             * @event sync
+             * Fires when the textarea is updated with content from the editor iframe.
+             * @param {HtmlEditor} this
+             * @param {String} html
+             */
+            sync: true,
+             /**
+             * @event push
+             * Fires when the iframe editor is updated with content from the textarea.
+             * @param {HtmlEditor} this
+             * @param {String} html
+             */
+            push: true,
+             /**
+             * @event editmodechange
+             * Fires when the editor switches edit modes
+             * @param {HtmlEditor} this
+             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+             */
+            editmodechange: true,
+            /**
+             * @event editorevent
+             * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+             * @param {HtmlEditor} this
+             */
+            editorevent: true,
+            /**
+             * @event firstfocus
+             * Fires when on first focus - needed by toolbars..
+             * @param {HtmlEditor} this
+             */
+            firstfocus: true,
+            /**
+             * @event autosave
+             * Auto save the htmlEditor value as a file into Events
+             * @param {HtmlEditor} this
+             */
+            autosave: true,
+            /**
+             * @event savedpreview
+             * preview the saved version of htmlEditor
+             * @param {HtmlEditor} this
+             */
+            savedpreview: true
+        });
+};
+
+
+Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
     
-    // default disabled, based on 'good practice'..
-    this.disable = this.disable || {};
-    Roo.applyIf(this.disable, {
-        fontSize : true,
-        colors : true,
-        specialElements : true
-    });
-    Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
     
-    this.editor = config.editor;
-    this.editorcore = config.editor.editorcore;
+      /**
+     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+     */
+    toolbars : false,
+    
+     /**
+    * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
+    */
+    btns : [],
+   
+     /**
+     * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
+     *                        Roo.resizable.
+     */
+    resizable : false,
+     /**
+     * @cfg {Number} height (in pixels)
+     */   
+    height: 300,
+   /**
+     * @cfg {Number} width (in pixels)
+     */   
+    width: false,
     
-    this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
+    /**
+     * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+     * 
+     */
+    stylesheets: false,
     
-    //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
-    // dont call parent... till later.
-}
-Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
-     
-    bar : true,
+    // id of frame..
+    frameId: false,
     
-    editor : false,
-    editorcore : false,
+    // private properties
+    validationEvent : false,
+    deferHeight: true,
+    initialized : false,
+    activated : false,
     
+    onFocus : Roo.emptyFn,
+    iframePad:3,
+    hideMode:'offsets',
     
-    formats : [
-        "p" ,  
-        "h1","h2","h3","h4","h5","h6", 
-        "pre", "code", 
-        "abbr", "acronym", "address", "cite", "samp", "var",
-        'div','span'
-    ],
+    tbContainer : false,
     
-    onRender : function(ct, position)
-    {
-       // Roo.log("Call onRender: " + this.xtype);
-        
-       Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
-       Roo.log(this.el);
-       this.el.dom.style.marginBottom = '0';
-       var _this = this;
-       var editorcore = this.editorcore;
-       var editor= this.editor;
-       
-       var children = [];
-       var btn = function(id,cmd , toggle, handler, html){
-       
-            var  event = toggle ? 'toggle' : 'click';
-       
-            var a = {
-                size : 'sm',
-                xtype: 'Button',
-                xns: Roo.bootstrap,
-                //glyphicon : id,
-                fa: id,
-                cmd : id || cmd,
-                enableToggle:toggle !== false,
-                html : html || '',
-                pressed : toggle ? false : null,
-                listeners : {}
-            };
-            a.listeners[toggle ? 'toggle' : 'click'] = function() {
-                handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
-            };
-            children.push(a);
-            return a;
-       }
-       
-    //    var cb_box = function...
-        
-        var style = {
-                xtype: 'Button',
-                size : 'sm',
-                xns: Roo.bootstrap,
-                fa : 'font',
-                //html : 'submit'
-                menu : {
-                    xtype: 'Menu',
-                    xns: Roo.bootstrap,
-                    items:  []
-                }
-        };
-        Roo.each(this.formats, function(f) {
-            style.menu.items.push({
-                xtype :'MenuItem',
-                xns: Roo.bootstrap,
-                html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
-                tagname : f,
-                listeners : {
-                    click : function()
-                    {
-                        editorcore.insertTag(this.tagname);
-                        editor.focus();
-                    }
-                }
-                
-            });
-        });
-        children.push(style);   
-        
-        btn('bold',false,true);
-        btn('italic',false,true);
-        btn('align-left', 'justifyleft',true);
-        btn('align-center', 'justifycenter',true);
-        btn('align-right' , 'justifyright',true);
-        btn('link', false, false, function(btn) {
-            //Roo.log("create link?");
-            var url = prompt(this.createLinkText, this.defaultLinkValue);
-            if(url && url != 'http:/'+'/'){
-                this.editorcore.relayCmd('createlink', url);
-            }
-        }),
-        btn('list','insertunorderedlist',true);
-        btn('pencil', false,true, function(btn){
-                Roo.log(this);
-                this.toggleSourceEdit(btn.pressed);
-        });
-        
-        if (this.editor.btns.length > 0) {
-            for (var i = 0; i<this.editor.btns.length; i++) {
-                children.push(this.editor.btns[i]);
-            }
-        }
-        
-        /*
-        var cog = {
-                xtype: 'Button',
-                size : 'sm',
-                xns: Roo.bootstrap,
-                glyphicon : 'cog',
-                //html : 'submit'
-                menu : {
-                    xtype: 'Menu',
-                    xns: Roo.bootstrap,
-                    items:  []
-                }
-        };
-        
-        cog.menu.items.push({
-            xtype :'MenuItem',
-            xns: Roo.bootstrap,
-            html : Clean styles,
-            tagname : f,
-            listeners : {
-                click : function()
-                {
-                    editorcore.insertTag(this.tagname);
-                    editor.focus();
-                }
-            }
-            
-        });
-       */
-        
-         
-       this.xtype = 'NavSimplebar';
-        
-        for(var i=0;i< children.length;i++) {
-            
-            this.buttons.add(this.addxtypeChild(children[i]));
-            
-        }
-        
-        editor.on('editorevent', this.updateToolbar, this);
-    },
-    onBtnClick : function(id)
-    {
-       this.editorcore.relayCmd(id);
-       this.editorcore.focus();
-    },
+    bodyCls : '',
     
+    toolbarContainer :function() {
+        return this.wrap.select('.x-html-editor-tb',true).first();
+    },
+
     /**
-     * Protected method that will not generally be called directly. It triggers
-     * a toolbar update by reading the markup state of the current selection in the editor.
+     * Protected method that will not generally be called directly. It
+     * is called when the editor creates its toolbar. Override this method if you need to
+     * add custom toolbar buttons.
+     * @param {HtmlEditor} editor
      */
-    updateToolbar: function(){
-
-        if(!this.editorcore.activated){
-            this.editor.onFirstFocus(); // is this neeed?
-            return;
-        }
-
-        var btns = this.buttons; 
-        var doc = this.editorcore.doc;
-        btns.get('bold').setActive(doc.queryCommandState('bold'));
-        btns.get('italic').setActive(doc.queryCommandState('italic'));
-        //btns.get('underline').setActive(doc.queryCommandState('underline'));
-        
-        btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
-        btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
-        btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
-        
-        //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
-        btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
-         /*
-        
-        var ans = this.editorcore.getAllAncestors();
-        if (this.formatCombo) {
-            
-            
-            var store = this.formatCombo.store;
-            this.formatCombo.setValue("");
-            for (var i =0; i < ans.length;i++) {
-                if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
-                    // select it..
-                    this.formatCombo.setValue(ans[i].tagName.toLowerCase());
-                    break;
-                }
-            }
-        }
+    createToolbar : function(){
+        Roo.log('renewing');
+        Roo.log("create toolbars");
         
+        this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
+        this.toolbars[0].render(this.toolbarContainer());
         
+        return;
         
-        // hides menus... - so this cant be on a menu...
-        Roo.bootstrap.MenuMgr.hideAll();
-        */
-        Roo.bootstrap.MenuMgr.hideAll();
-        //this.editorsyncValue();
+//        if (!editor.toolbars || !editor.toolbars.length) {
+//            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
+//        }
+//        
+//        for (var i =0 ; i < editor.toolbars.length;i++) {
+//            editor.toolbars[i] = Roo.factory(
+//                    typeof(editor.toolbars[i]) == 'string' ?
+//                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
+//                Roo.bootstrap.HtmlEditor);
+//            editor.toolbars[i].init(editor);
+//        }
     },
-    onFirstFocus: function() {
-        this.buttons.each(function(item){
-           item.enable();
+
+     
+    // private
+    onRender : function(ct, position)
+    {
+       // Roo.log("Call onRender: " + this.xtype);
+        var _t = this;
+        Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
+      
+        this.wrap = this.inputEl().wrap({
+            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
         });
-    },
-    toggleSourceEdit : function(sourceEditMode){
         
-          
-        if(sourceEditMode){
-            Roo.log("disabling buttons");
-           this.buttons.each( function(item){
-                if(item.cmd != 'pencil'){
-                    item.disable();
+        this.editorcore.onRender(ct, position);
+         
+        if (this.resizable) {
+            this.resizeEl = new Roo.Resizable(this.wrap, {
+                pinned : true,
+                wrap: true,
+                dynamic : true,
+                minHeight : this.height,
+                height: this.height,
+                handles : this.resizable,
+                width: this.width,
+                listeners : {
+                    resize : function(r, w, h) {
+                        _t.onResize(w,h); // -something
+                    }
                 }
             });
-          
-        }else{
-            Roo.log("enabling buttons");
-            if(this.editorcore.initialized){
-                this.buttons.each( function(item){
-                    item.enable();
-                });
-            }
             
         }
-        Roo.log("calling toggole on editor");
-        // tell the editor that it's been pressed..
-        this.editor.toggleSourceEdit(sourceEditMode);
+        this.createToolbar(this);
        
-    }
-});
-
-
-
-
-/*
- * - LGPL
- */
-
-/**
- * @class Roo.bootstrap.Markdown
- * @extends Roo.bootstrap.TextArea
- * Bootstrap Showdown editable area
- * @cfg {string} content
- * 
- * @constructor
- * Create a new Showdown
- */
-
-Roo.bootstrap.Markdown = function(config){
-    Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
-   
-};
-
-Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
-    
-    editing :false,
-    
-    initEvents : function()
-    {
         
-        Roo.bootstrap.TextArea.prototype.initEvents.call(this);
-        this.markdownEl = this.el.createChild({
-            cls : 'roo-markdown-area'
-        });
-        this.inputEl().addClass('d-none');
-        if (this.getValue() == '') {
-            this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
-            
-        } else {
-            this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
-        }
-        this.markdownEl.on('click', this.toggleTextEdit, this);
-        this.on('blur', this.toggleTextEdit, this);
-        this.on('specialkey', this.resizeTextArea, this);
-    },
-    
-    toggleTextEdit : function()
-    {
-        var sh = this.markdownEl.getHeight();
-        this.inputEl().addClass('d-none');
-        this.markdownEl.addClass('d-none');
-        if (!this.editing) {
-            // show editor?
-            this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
-            this.inputEl().removeClass('d-none');
-            this.inputEl().focus();
-            this.editing = true;
-            return;
+        if(!this.width && this.resizable){
+            this.setSize(this.wrap.getSize());
         }
-        // show showdown...
-        this.updateMarkdown();
-        this.markdownEl.removeClass('d-none');
-        this.editing = false;
-        return;
-    },
-    updateMarkdown : function()
-    {
-        if (this.getValue() == '') {
-            this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
-            return;
+        if (this.resizeEl) {
+            this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
+            // should trigger onReize..
         }
-        this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
-    },
-    
-    resizeTextArea: function () {
         
-        var sh = 100;
-        Roo.log([sh, this.getValue().split("\n").length * 30]);
-        this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
     },
-    setValue : function(val)
+
+    // private
+    onResize : function(w, h)
     {
-        Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
-        if (!this.editing) {
-            this.updateMarkdown();
-        }
+        Roo.log('resize: ' +w + ',' + h );
+        Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
+        var ew = false;
+        var eh = false;
         
-    },
-    focus : function()
-    {
-        if (!this.editing) {
-            this.toggleTextEdit();
+        if(this.inputEl() ){
+            if(typeof w == 'number'){
+                var aw = w - this.wrap.getFrameWidth('lr');
+                this.inputEl().setWidth(this.adjustWidth('textarea', aw));
+                ew = aw;
+            }
+            if(typeof h == 'number'){
+                 var tbh = -11;  // fixme it needs to tool bar size!
+                for (var i =0; i < this.toolbars.length;i++) {
+                    // fixme - ask toolbars for heights?
+                    tbh += this.toolbars[i].el.getHeight();
+                    //if (this.toolbars[i].footer) {
+                    //    tbh += this.toolbars[i].footer.el.getHeight();
+                    //}
+                }
+              
+                
+                
+                
+                
+                var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
+                ah -= 5; // knock a few pixes off for look..
+                this.inputEl().setHeight(this.adjustWidth('textarea', ah));
+                var eh = ah;
+            }
         }
+        Roo.log('onResize:' + [w,h,ew,eh].join(',') );
+        this.editorcore.onResize(ew,eh);
         
-    }
-
-
-});
-/**
- * @class Roo.bootstrap.Table.AbstractSelectionModel
- * @extends Roo.util.Observable
- * Abstract base class for grid SelectionModels.  It provides the interface that should be
- * implemented by descendant classes.  This class should not be directly instantiated.
- * @constructor
- */
-Roo.bootstrap.Table.AbstractSelectionModel = function(){
-    this.locked = false;
-    Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
-};
-
-
-Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
-    /** @ignore Called by the grid automatically. Do not call directly. */
-    init : function(grid){
-        this.grid = grid;
-        this.initEvents();
-    },
-
-    /**
-     * Locks the selections.
-     */
-    lock : function(){
-        this.locked = true;
-    },
-
-    /**
-     * Unlocks the selections.
-     */
-    unlock : function(){
-        this.locked = false;
     },
 
     /**
-     * Returns true if the selections are locked.
-     * @return {Boolean}
+     * Toggles the editor between standard and source edit mode.
+     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
      */
-    isLocked : function(){
-        return this.locked;
-    },
-    
-    
-    initEvents : function ()
+    toggleSourceEdit : function(sourceEditMode)
     {
+        this.editorcore.toggleSourceEdit(sourceEditMode);
         
-    }
-});
-/**
- * @extends Roo.bootstrap.Table.AbstractSelectionModel
- * @class Roo.bootstrap.Table.RowSelectionModel
- * The default SelectionModel used by {@link Roo.bootstrap.Table}.
- * It supports multiple selections and keyboard selection/navigation. 
- * @constructor
- * @param {Object} config
- */
-
-Roo.bootstrap.Table.RowSelectionModel = function(config){
-    Roo.apply(this, config);
-    this.selections = new Roo.util.MixedCollection(false, function(o){
-        return o.id;
-    });
-
-    this.last = false;
-    this.lastActive = false;
+        if(this.editorcore.sourceEditMode){
+            Roo.log('editor - showing textarea');
+            
+//            Roo.log('in');
+//            Roo.log(this.syncValue());
+            this.syncValue();
+            this.inputEl().removeClass(['hide', 'x-hidden']);
+            this.inputEl().dom.removeAttribute('tabIndex');
+            this.inputEl().focus();
+        }else{
+            Roo.log('editor - hiding textarea');
+//            Roo.log('out')
+//            Roo.log(this.pushValue()); 
+            this.pushValue();
+            
+            this.inputEl().addClass(['hide', 'x-hidden']);
+            this.inputEl().dom.setAttribute('tabIndex', -1);
+            //this.deferFocus();
+        }
+         
+        if(this.resizable){
+            this.setSize(this.wrap.getSize());
+        }
+        
+        this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
+    },
+    // private (for BoxComponent)
+    adjustSize : Roo.BoxComponent.prototype.adjustSize,
 
-    this.addEvents({
-        /**
-            * @event selectionchange
-            * Fires when the selection changes
-            * @param {SelectionModel} this
-            */
-           "selectionchange" : true,
-        /**
-            * @event afterselectionchange
-            * Fires after the selection changes (eg. by key press or clicking)
-            * @param {SelectionModel} this
-            */
-           "afterselectionchange" : true,
-        /**
-            * @event beforerowselect
-            * Fires when a row is selected being selected, return false to cancel.
-            * @param {SelectionModel} this
-            * @param {Number} rowIndex The selected index
-            * @param {Boolean} keepExisting False if other selections will be cleared
-            */
-           "beforerowselect" : true,
-        /**
-            * @event rowselect
-            * Fires when a row is selected.
-            * @param {SelectionModel} this
-            * @param {Number} rowIndex The selected index
-            * @param {Roo.data.Record} r The record
-            */
-           "rowselect" : true,
-        /**
-            * @event rowdeselect
-            * Fires when a row is deselected.
-            * @param {SelectionModel} this
-            * @param {Number} rowIndex The selected index
-            */
-        "rowdeselect" : true
-    });
-    Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
-    this.locked = false;
- };
+    // private (for BoxComponent)
+    getResizeEl : function(){
+        return this.wrap;
+    },
 
-Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
-    /**
-     * @cfg {Boolean} singleSelect
-     * True to allow selection of only one row at a time (defaults to false)
-     */
-    singleSelect : false,
+    // private (for BoxComponent)
+    getPositionEl : function(){
+        return this.wrap;
+    },
 
     // private
-    initEvents : function()
-    {
+    initEvents : function(){
+        this.originalValue = this.getValue();
+    },
 
-        //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
-        //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
-        //}else{ // allow click to work like normal
-         //   this.grid.on("rowclick", this.handleDragableRowClick, this);
-        //}
-        //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
-        this.grid.on("rowclick", this.handleMouseDown, this);
-        
-        this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
-            "up" : function(e){
-                if(!e.shiftKey){
-                    this.selectPrevious(e.shiftKey);
-                }else if(this.last !== false && this.lastActive !== false){
-                    var last = this.last;
-                    this.selectRange(this.last,  this.lastActive-1);
-                    this.grid.getView().focusRow(this.lastActive);
-                    if(last !== false){
-                        this.last = last;
-                    }
-                }else{
-                    this.selectFirstRow();
-                }
-                this.fireEvent("afterselectionchange", this);
-            },
-            "down" : function(e){
-                if(!e.shiftKey){
-                    this.selectNext(e.shiftKey);
-                }else if(this.last !== false && this.lastActive !== false){
-                    var last = this.last;
-                    this.selectRange(this.last,  this.lastActive+1);
-                    this.grid.getView().focusRow(this.lastActive);
-                    if(last !== false){
-                        this.last = last;
-                    }
-                }else{
-                    this.selectFirstRow();
-                }
-                this.fireEvent("afterselectionchange", this);
-            },
-            scope: this
-        });
-        this.grid.store.on('load', function(){
-            this.selections.clear();
-        },this);
-        /*
-        var view = this.grid.view;
-        view.on("refresh", this.onRefresh, this);
-        view.on("rowupdated", this.onRowUpdated, this);
-        view.on("rowremoved", this.onRemove, this);
-        */
+//    /**
+//     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+//     * @method
+//     */
+//    markInvalid : Roo.emptyFn,
+//    /**
+//     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+//     * @method
+//     */
+//    clearInvalid : Roo.emptyFn,
+
+    setValue : function(v){
+        Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
+        this.editorcore.pushValue();
     },
 
+     
     // private
-    onRefresh : function()
-    {
-        var ds = this.grid.store, i, v = this.grid.view;
-        var s = this.selections;
-        s.each(function(r){
-            if((i = ds.indexOfId(r.id)) != -1){
-                v.onRowSelect(i);
-            }else{
-                s.remove(r);
-            }
-        });
+    deferFocus : function(){
+        this.focus.defer(10, this);
     },
 
-    // private
-    onRemove : function(v, index, r){
-        this.selections.remove(r);
+    // doc'ed in Field
+    focus : function(){
+        this.editorcore.focus();
+        
     },
+      
 
     // private
-    onRowUpdated : function(v, index, r){
-        if(this.isSelected(r)){
-            v.onRowSelect(index);
+    onDestroy : function(){
+        
+        
+        
+        if(this.rendered){
+            
+            for (var i =0; i < this.toolbars.length;i++) {
+                // fixme - ask toolbars for heights?
+                this.toolbars[i].onDestroy();
+            }
+            
+            this.wrap.dom.innerHTML = '';
+            this.wrap.remove();
         }
     },
 
-    /**
-     * Select records.
-     * @param {Array} records The records to select
-     * @param {Boolean} keepExisting (optional) True to keep existing selections
-     */
-    selectRecords : function(records, keepExisting)
-    {
-        if(!keepExisting){
-            this.clearSelections();
-        }
-           var ds = this.grid.store;
-        for(var i = 0, len = records.length; i < len; i++){
-            this.selectRow(ds.indexOf(records[i]), true);
+    // private
+    onFirstFocus : function(){
+        //Roo.log("onFirstFocus");
+        this.editorcore.onFirstFocus();
+         for (var i =0; i < this.toolbars.length;i++) {
+            this.toolbars[i].onFirstFocus();
         }
+        
     },
-
-    /**
-     * Gets the number of selected rows.
-     * @return {Number}
-     */
-    getCount : function(){
-        return this.selections.length;
+    
+    // private
+    syncValue : function()
+    {   
+        this.editorcore.syncValue();
     },
-
+    
+    pushValue : function()
+    {   
+        this.editorcore.pushValue();
+    }
+     
+    
+    // hide stuff that is not compatible
     /**
-     * Selects the first row in the grid.
+     * @event blur
+     * @hide
      */
-    selectFirstRow : function(){
-        this.selectRow(0);
-    },
-
     /**
-     * Select the last row.
-     * @param {Boolean} keepExisting (optional) True to keep existing selections
+     * @event change
+     * @hide
      */
-    selectLastRow : function(keepExisting){
-        //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
-        this.selectRow(this.grid.store.getCount() - 1, keepExisting);
-    },
-
     /**
-     * Selects the row immediately following the last selected row.
-     * @param {Boolean} keepExisting (optional) True to keep existing selections
+     * @event focus
+     * @hide
      */
-    selectNext : function(keepExisting)
-    {
-           if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
-            this.selectRow(this.last+1, keepExisting);
-            this.grid.getView().focusRow(this.last);
-        }
-    },
-
     /**
-     * Selects the row that precedes the last selected row.
-     * @param {Boolean} keepExisting (optional) True to keep existing selections
+     * @event specialkey
+     * @hide
      */
-    selectPrevious : function(keepExisting){
-        if(this.last){
-            this.selectRow(this.last-1, keepExisting);
-            this.grid.getView().focusRow(this.last);
-        }
-    },
-
     /**
-     * Returns the selected records
-     * @return {Array} Array of selected records
+     * @cfg {String} fieldClass @hide
      */
-    getSelections : function(){
-        return [].concat(this.selections.items);
-    },
-
     /**
-     * Returns the first selected record.
-     * @return {Record}
+     * @cfg {String} focusClass @hide
      */
-    getSelected : function(){
-        return this.selections.itemAt(0);
-    },
-
-
     /**
-     * Clears all selections.
+     * @cfg {String} autoCreate @hide
      */
-    clearSelections : function(fast)
-    {
-        if(this.locked) {
-            return;
-        }
-        if(fast !== true){
-               var ds = this.grid.store;
-            var s = this.selections;
-            s.each(function(r){
-                this.deselectRow(ds.indexOfId(r.id));
-            }, this);
-            s.clear();
-        }else{
-            this.selections.clear();
-        }
-        this.last = false;
-    },
-
-
     /**
-     * Selects all rows.
+     * @cfg {String} inputType @hide
      */
-    selectAll : function(){
-        if(this.locked) {
-            return;
-        }
-        this.selections.clear();
-        for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
-            this.selectRow(i, true);
-        }
-    },
-
+     
     /**
-     * Returns True if there is a selection.
-     * @return {Boolean}
+     * @cfg {String} invalidText @hide
      */
-    hasSelection : function(){
-        return this.selections.length > 0;
-    },
-
     /**
-     * Returns True if the specified row is selected.
-     * @param {Number/Record} record The record or index of the record to check
-     * @return {Boolean}
+     * @cfg {String} msgFx @hide
      */
-    isSelected : function(index){
-           var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
-        return (r && this.selections.key(r.id) ? true : false);
-    },
-
     /**
-     * Returns True if the specified record id is selected.
-     * @param {String} id The id of record to check
-     * @return {Boolean}
+     * @cfg {String} validateOnBlur @hide
      */
-    isIdSelected : function(id){
-        return (this.selections.key(id) ? true : false);
-    },
-
-
-    // private
-    handleMouseDBClick : function(e, t){
-       
-    },
-    // private
-    handleMouseDown : function(e, t)
+});
+    
+   
+   
+   
+      
+Roo.namespace('Roo.bootstrap.htmleditor');
+/**
+ * @class Roo.bootstrap.HtmlEditorToolbar1
+ * Basic Toolbar
+ * 
+ * @example
+ * Usage:
+ *
+ new Roo.bootstrap.HtmlEditor({
+    ....
+    toolbars : [
+        new Roo.bootstrap.HtmlEditorToolbar1({
+            disable : { fonts: 1 , format: 1, ..., ... , ...],
+            btns : [ .... ]
+        })
+    }
+     
+ * 
+ * @cfg {Object} disable List of elements to disable..
+ * @cfg {Array} btns List of additional buttons.
+ * 
+ * 
+ * NEEDS Extra CSS? 
+ * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
+ */
+Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
+{
+    
+    Roo.apply(this, config);
+    
+    // default disabled, based on 'good practice'..
+    this.disable = this.disable || {};
+    Roo.applyIf(this.disable, {
+        fontSize : true,
+        colors : true,
+        specialElements : true
+    });
+    Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
+    
+    this.editor = config.editor;
+    this.editorcore = config.editor.editorcore;
+    
+    this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
+    
+    //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
+    // dont call parent... till later.
+}
+Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
+     
+    bar : true,
+    
+    editor : false,
+    editorcore : false,
+    
+    
+    formats : [
+        "p" ,  
+        "h1","h2","h3","h4","h5","h6", 
+        "pre", "code", 
+        "abbr", "acronym", "address", "cite", "samp", "var",
+        'div','span'
+    ],
+    
+    onRender : function(ct, position)
     {
-           var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
-        if(this.isLocked() || rowIndex < 0 ){
-            return;
+       // Roo.log("Call onRender: " + this.xtype);
+        
+       Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
+       Roo.log(this.el);
+       this.el.dom.style.marginBottom = '0';
+       var _this = this;
+       var editorcore = this.editorcore;
+       var editor= this.editor;
+       
+       var children = [];
+       var btn = function(id,cmd , toggle, handler, html){
+       
+            var  event = toggle ? 'toggle' : 'click';
+       
+            var a = {
+                size : 'sm',
+                xtype: 'Button',
+                xns: Roo.bootstrap,
+                //glyphicon : id,
+                fa: id,
+                cmd : id || cmd,
+                enableToggle:toggle !== false,
+                html : html || '',
+                pressed : toggle ? false : null,
+                listeners : {}
+            };
+            a.listeners[toggle ? 'toggle' : 'click'] = function() {
+                handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
+            };
+            children.push(a);
+            return a;
+       }
+       
+    //    var cb_box = function...
+        
+        var style = {
+                xtype: 'Button',
+                size : 'sm',
+                xns: Roo.bootstrap,
+                fa : 'font',
+                //html : 'submit'
+                menu : {
+                    xtype: 'Menu',
+                    xns: Roo.bootstrap,
+                    items:  []
+                }
         };
-        if(e.shiftKey && this.last !== false){
-            var last = this.last;
-            this.selectRange(last, rowIndex, e.ctrlKey);
-            this.last = last; // reset the last
-            t.focus();
-    
-        }else{
-            var isSelected = this.isSelected(rowIndex);
-            //Roo.log("select row:" + rowIndex);
-            if(isSelected){
-                this.deselectRow(rowIndex);
-            } else {
-                       this.selectRow(rowIndex, true);
+        Roo.each(this.formats, function(f) {
+            style.menu.items.push({
+                xtype :'MenuItem',
+                xns: Roo.bootstrap,
+                html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
+                tagname : f,
+                listeners : {
+                    click : function()
+                    {
+                        editorcore.insertTag(this.tagname);
+                        editor.focus();
+                    }
+                }
+                
+            });
+        });
+        children.push(style);   
+        
+        btn('bold',false,true);
+        btn('italic',false,true);
+        btn('align-left', 'justifyleft',true);
+        btn('align-center', 'justifycenter',true);
+        btn('align-right' , 'justifyright',true);
+        btn('link', false, false, function(btn) {
+            //Roo.log("create link?");
+            var url = prompt(this.createLinkText, this.defaultLinkValue);
+            if(url && url != 'http:/'+'/'){
+                this.editorcore.relayCmd('createlink', url);
             }
-    
-            /*
-                if(e.button !== 0 && isSelected){
-                alert('rowIndex 2: ' + rowIndex);
-                    view.focusRow(rowIndex);
-                }else if(e.ctrlKey && isSelected){
-                    this.deselectRow(rowIndex);
-                }else if(!isSelected){
-                    this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
-                    view.focusRow(rowIndex);
+        }),
+        btn('list','insertunorderedlist',true);
+        btn('pencil', false,true, function(btn){
+                Roo.log(this);
+                this.toggleSourceEdit(btn.pressed);
+        });
+        
+        if (this.editor.btns.length > 0) {
+            for (var i = 0; i<this.editor.btns.length; i++) {
+                children.push(this.editor.btns[i]);
+            }
+        }
+        
+        /*
+        var cog = {
+                xtype: 'Button',
+                size : 'sm',
+                xns: Roo.bootstrap,
+                glyphicon : 'cog',
+                //html : 'submit'
+                menu : {
+                    xtype: 'Menu',
+                    xns: Roo.bootstrap,
+                    items:  []
                 }
-            */
+        };
+        
+        cog.menu.items.push({
+            xtype :'MenuItem',
+            xns: Roo.bootstrap,
+            html : Clean styles,
+            tagname : f,
+            listeners : {
+                click : function()
+                {
+                    editorcore.insertTag(this.tagname);
+                    editor.focus();
+                }
+            }
+            
+        });
+       */
+        
+         
+       this.xtype = 'NavSimplebar';
+        
+        for(var i=0;i< children.length;i++) {
+            
+            this.buttons.add(this.addxtypeChild(children[i]));
+            
         }
-        this.fireEvent("afterselectionchange", this);
+        
+        editor.on('editorevent', this.updateToolbar, this);
     },
-    // private
-    handleDragableRowClick :  function(grid, rowIndex, e) 
+    onBtnClick : function(id)
     {
-        if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
-            this.selectRow(rowIndex, false);
-            grid.view.focusRow(rowIndex);
-             this.fireEvent("afterselectionchange", this);
-        }
+       this.editorcore.relayCmd(id);
+       this.editorcore.focus();
     },
     
     /**
-     * Selects multiple rows.
-     * @param {Array} rows Array of the indexes of the row to select
-     * @param {Boolean} keepExisting (optional) True to keep existing selections
-     */
-    selectRows : function(rows, keepExisting){
-        if(!keepExisting){
-            this.clearSelections();
-        }
-        for(var i = 0, len = rows.length; i < len; i++){
-            this.selectRow(rows[i], true);
-        }
-    },
-
-    /**
-     * Selects a range of rows. All rows in between startRow and endRow are also selected.
-     * @param {Number} startRow The index of the first row in the range
-     * @param {Number} endRow The index of the last row in the range
-     * @param {Boolean} keepExisting (optional) True to retain existing selections
+     * Protected method that will not generally be called directly. It triggers
+     * a toolbar update by reading the markup state of the current selection in the editor.
      */
-    selectRange : function(startRow, endRow, keepExisting){
-        if(this.locked) {
+    updateToolbar: function(){
+
+        if(!this.editorcore.activated){
+            this.editor.onFirstFocus(); // is this neeed?
             return;
         }
-        if(!keepExisting){
-            this.clearSelections();
-        }
-        if(startRow <= endRow){
-            for(var i = startRow; i <= endRow; i++){
-                this.selectRow(i, true);
+
+        var btns = this.buttons; 
+        var doc = this.editorcore.doc;
+        btns.get('bold').setActive(doc.queryCommandState('bold'));
+        btns.get('italic').setActive(doc.queryCommandState('italic'));
+        //btns.get('underline').setActive(doc.queryCommandState('underline'));
+        
+        btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
+        btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
+        btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
+        
+        //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
+        btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
+         /*
+        
+        var ans = this.editorcore.getAllAncestors();
+        if (this.formatCombo) {
+            
+            
+            var store = this.formatCombo.store;
+            this.formatCombo.setValue("");
+            for (var i =0; i < ans.length;i++) {
+                if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
+                    // select it..
+                    this.formatCombo.setValue(ans[i].tagName.toLowerCase());
+                    break;
+                }
             }
+        }
+        
+        
+        
+        // hides menus... - so this cant be on a menu...
+        Roo.bootstrap.MenuMgr.hideAll();
+        */
+        Roo.bootstrap.MenuMgr.hideAll();
+        //this.editorsyncValue();
+    },
+    onFirstFocus: function() {
+        this.buttons.each(function(item){
+           item.enable();
+        });
+    },
+    toggleSourceEdit : function(sourceEditMode){
+        
+          
+        if(sourceEditMode){
+            Roo.log("disabling buttons");
+           this.buttons.each( function(item){
+                if(item.cmd != 'pencil'){
+                    item.disable();
+                }
+            });
+          
         }else{
-            for(var i = startRow; i >= endRow; i--){
-                this.selectRow(i, true);
+            Roo.log("enabling buttons");
+            if(this.editorcore.initialized){
+                this.buttons.each( function(item){
+                    item.enable();
+                });
             }
+            
         }
-    },
+        Roo.log("calling toggole on editor");
+        // tell the editor that it's been pressed..
+        this.editor.toggleSourceEdit(sourceEditMode);
+       
+    }
+});
 
-    /**
-     * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
-     * @param {Number} startRow The index of the first row in the range
-     * @param {Number} endRow The index of the last row in the range
-     */
-    deselectRange : function(startRow, endRow, preventViewNotify){
-        if(this.locked) {
-            return;
-        }
-        for(var i = startRow; i <= endRow; i++){
-            this.deselectRow(i, preventViewNotify);
-        }
-    },
 
-    /**
-     * Selects a row.
-     * @param {Number} row The index of the row to select
-     * @param {Boolean} keepExisting (optional) True to keep existing selections
-     */
-    selectRow : function(index, keepExisting, preventViewNotify)
+
+
+/*
+ * - LGPL
+ */
+
+/**
+ * @class Roo.bootstrap.Markdown
+ * @extends Roo.bootstrap.TextArea
+ * Bootstrap Showdown editable area
+ * @cfg {string} content
+ * 
+ * @constructor
+ * Create a new Showdown
+ */
+
+Roo.bootstrap.Markdown = function(config){
+    Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
+   
+};
+
+Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
+    
+    editing :false,
+    
+    initEvents : function()
     {
-           if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
-            return;
-        }
-        if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
-            if(!keepExisting || this.singleSelect){
-                this.clearSelections();
-            }
-           
-            var r = this.grid.store.getAt(index);
-            //console.log('selectRow - record id :' + r.id);
+        
+        Roo.bootstrap.TextArea.prototype.initEvents.call(this);
+        this.markdownEl = this.el.createChild({
+            cls : 'roo-markdown-area'
+        });
+        this.inputEl().addClass('d-none');
+        if (this.getValue() == '') {
+            this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
             
-            this.selections.add(r);
-            this.last = this.lastActive = index;
-            if(!preventViewNotify){
-                var proxy = new Roo.Element(
-                                this.grid.getRowDom(index)
-                );
-                proxy.addClass('bg-info info');
-            }
-            this.fireEvent("rowselect", this, index, r);
-            this.fireEvent("selectionchange", this);
+        } else {
+            this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
         }
+        this.markdownEl.on('click', this.toggleTextEdit, this);
+        this.on('blur', this.toggleTextEdit, this);
+        this.on('specialkey', this.resizeTextArea, this);
     },
-
-    /**
-     * Deselects a row.
-     * @param {Number} row The index of the row to deselect
-     */
-    deselectRow : function(index, preventViewNotify)
+    
+    toggleTextEdit : function()
     {
-        if(this.locked) {
+        var sh = this.markdownEl.getHeight();
+        this.inputEl().addClass('d-none');
+        this.markdownEl.addClass('d-none');
+        if (!this.editing) {
+            // show editor?
+            this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
+            this.inputEl().removeClass('d-none');
+            this.inputEl().focus();
+            this.editing = true;
             return;
         }
-        if(this.last == index){
-            this.last = false;
-        }
-        if(this.lastActive == index){
-            this.lastActive = false;
-        }
-       
-        var r = this.grid.store.getAt(index);
-        if (!r) {
+        // show showdown...
+        this.updateMarkdown();
+        this.markdownEl.removeClass('d-none');
+        this.editing = false;
+        return;
+    },
+    updateMarkdown : function()
+    {
+        if (this.getValue() == '') {
+            this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
             return;
         }
+        this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
+    },
+    
+    resizeTextArea: function () {
         
-        this.selections.remove(r);
-        //.console.log('deselectRow - record id :' + r.id);
-        if(!preventViewNotify){
-       
-           var proxy = new Roo.Element(
-                this.grid.getRowDom(index)
-            );
-            proxy.removeClass('bg-info info');
-        }
-        this.fireEvent("rowdeselect", this, index);
-        this.fireEvent("selectionchange", this);
+        var sh = 100;
+        Roo.log([sh, this.getValue().split("\n").length * 30]);
+        this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
     },
-
-    // private
-    restoreLast : function(){
-        if(this._last){
-            this.last = this._last;
+    setValue : function(val)
+    {
+        Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
+        if (!this.editing) {
+            this.updateMarkdown();
         }
+        
     },
-
-    // private
-    acceptsNav : function(row, col, cm){
-        return !cm.isHidden(col) && cm.isCellEditable(col, row);
-    },
-
-    // private
-    onEditorKey : function(field, e){
-        var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
-        if(k == e.TAB){
-            e.stopEvent();
-            ed.completeEdit();
-            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);
-            }
-        }else if(k == e.ENTER && !e.ctrlKey){
-            e.stopEvent();
-            ed.completeEdit();
-            if(e.shiftKey){
-                newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
-            }else{
-                newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
-            }
-        }else if(k == e.ESC){
-            ed.cancelEdit();
-        }
-        if(newCell){
-            g.startEditing(newCell[0], newCell[1]);
+    focus : function()
+    {
+        if (!this.editing) {
+            this.toggleTextEdit();
         }
+        
     }
-});
-/*
+
+
+});/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.