Fix #6874 - Grid column resize
[roojs1] / roojs-bootstrap-debug.js
index 9e39fb8..fbf1526 100644 (file)
@@ -7307,2162 +7307,3175 @@ 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.
+     */
+    /**
+     * @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 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)
+ * @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>';
+    this.view = this; // compat with grid.
+    
+    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;
+    }
+    
+    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
+        
+    });
+};
+
+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?
+
+
+    
+    container: false, // used by gridpanel...
+    
+    lazyLoad : false,
+    
+    CSS : Roo.util.CSS,
+    
+    auto_hide_footer : false,
+    
+    view: false, // actually points to this..
+    
+    getAutoCreate : function()
+    {
+        var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
+        
+        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';
+        }
+        
+        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;
+        }
+        
+        
+        
+        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());
             }
             
-            if(hh.length){
-                c.html = hh;
-            }
+            cfg.cn.push(this.renderBody());
             
-            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(this.footerShow){
+                cfg.cn.push(this.renderFooter());
             }
+            // where does this come from?
+            //cfg.cls+=  ' TableGrid';
+        }
+        
+        return { cn : [ cfg ] };
+    },
+    
+    initEvents : function()
+    {   
+        if(!this.store || !this.cm){
+            return;
+        }
+        if (this.selModel) {
+            this.selModel.initEvents();
+        }
+        
+        
+        //Roo.log('initEvents with ds!!!!');
+        
+        this.bodyEl = this.el.select('tbody', true).first();
+        this.headEl = 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);
+        
+        
+        // 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(typeof(config.dataIndex) != 'undefined'){
-                c.sort = config.dataIndex;
+            if(this.lazyLoad){
+                this.el.select('tfoot tr td').first().addClass('hide');
             }
-            
-           
-            
-            if(typeof(config.align) != 'undefined' && config.align.length){
-                c.style += ' text-align:' + config.align + ';';
+        } 
+        
+        if(this.loadMask) {
+            this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
+        }
+        
+        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;
+    },
+    
+    initCSS : function()
+    {
+        
+        
+        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();
+        
+        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.width) != 'undefined'){
-                c.style += ' width:' + config.width + 'px;';
-                this.totalWidth += config.width;
-            } else {
-                this.totalWidth += 100; // assume minimum of 100 per column?
+            if (w !== false) {
+                cols.push( { rel : false, abs : w });
+                total_abs += w;
+                last = i; // not really..
+                continue;
             }
-            
-            if(typeof(config.cls) != 'undefined'){
-                c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.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;';
                 }
-                 
-                if (!config[size]) { // 0 = hidden
-                    // BS 4 '0' is treated as hide that column and below.
-                    c.cls += ' hidden-' + size + ' hidden' + size + '-down';
-                    return;
+                // 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;";
                 
-                c.cls += ' col-' + size + '-' + config[size] + (
-                    size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
-                );
-                
+            }
+            
+            styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
+            if (this.headEl) {
+                if (i == last) {
+                    splithide = 'display:none;';
+                }
                 
-            });
+                styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
+                            '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
+                );
+            }
             
-            header.cn.push(c)
         }
+        Roo.log(styles.join(''));
+        this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
         
-        return header;
     },
     
-    renderBody : function()
-    {
-        var body = {
-            tag: 'tbody',
-            cn : [
-                {
-                    tag: 'tr',
-                    cn : [
-                        {
-                            tag : 'td',
-                            colspan :  this.cm.getColumnCount()
-                        }
-                    ]
-                }
-            ]
-        };
-        
-        return body;
-    },
     
-    renderFooter : function()
+    
+    onContextMenu : function(e, t)
     {
-        var footer = {
-            tag: 'tfoot',
-            cn : [
-                {
-                    tag: 'tr',
-                    cn : [
-                        {
-                            tag : 'td',
-                            colspan :  this.cm.getColumnCount()
-                        }
-                    ]
-                }
-            ]
-        };
-        
-        return footer;
+        this.processEvent("contextmenu", e);
     },
     
-    
-    
-    onLoad : function()
+    processEvent : function(name, e)
     {
-//        Roo.log('ds onload');
-        this.clear();
+        if (name != 'touchstart' ) {
+            this.fireEvent(name, e);    
+        }
         
-        var _this = this;
-        var cm = this.cm;
-        var ds = this.store;
+        var t = e.getTarget();
         
-        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 cell = Roo.get(t);
         
-        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(!cell){
+            return;
         }
         
-        var tfoot = this.el.select('tfoot', true).first();
+        if(cell.findParent('tfoot', false, true)){
+            return;
+        }
         
-        if(this.footerShow && this.auto_hide_footer && this.mainFoot){
+        if(cell.findParent('thead', false, true)){
             
-            this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
+            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 total = this.ds.getTotalCount();
+            var cellIndex = cell.dom.cellIndex;
             
-            if(this.footer.pageSize < total){
-                this.mainFoot.show();
+            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;
             }
         }
         
-        Roo.each(this.el.select('tbody td', true).elements, function(e){
-            e.on('mouseover', _this.onMouseover, _this);
-        });
+        var row = cell.findParent('tr', false, true);
+        var cellIndex = cell.dom.cellIndex;
+        var rowIndex = row.dom.rowIndex - 1;
         
-        Roo.each(this.el.select('tbody td', true).elements, function(e){
-            e.on('mouseout', _this.onMouseout, _this);
-        });
-        this.fireEvent('rowsrendered', this);
+        if(row !== false){
+            
+            this.fireEvent("row" + name, this, rowIndex, e);
+            
+            if(cell !== false){
+            
+                this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
+            }
+        }
         
-        this.autoSize();
     },
     
-    
-    onUpdate : function(ds,record)
+    onMouseover : function(e, el)
     {
-        this.refreshRow(record);
-        this.autoSize();
-    },
-    
-    onRemove : function(ds, record, index, isUpdate){
-        if(isUpdate !== true){
-            this.fireEvent("beforerowremoved", this, index, record);
-        }
-        var bt = this.mainBody.dom;
+        var cell = Roo.get(el);
         
-        var rows = this.el.select('tbody > tr', true).elements;
+        if(!cell){
+            return;
+        }
         
-        if(typeof(rows[index]) != 'undefined'){
-            bt.removeChild(rows[index].dom);
+        if(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = cell.findParent('td', false, true);
         }
         
-//        if(bt.rows[index]){
-//            bt.removeChild(bt.rows[index]);
-//        }
+        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);
         
-        if(isUpdate !== true){
-            //this.stripeRows(index);
-            //this.syncRowHeights(index, index);
-            //this.layout();
-            this.fireEvent("rowremoved", this, index, record);
-        }
     },
     
-    onAdd : function(ds, records, rowIndex)
+    onMouseout : function(e, el)
     {
-        //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);
+        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);
+        
     },
     
-    
-    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 
-            }
+    onClick : function(e, el)
+    {
+        var cell = Roo.get(el);
+        
+        if(!cell || (!this.cellSelection && !this.rowSelection)){
+            return;
         }
-        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);
+        
+        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);
+        //}
+         
     },
-    
-    insertRow : function(dm, rowIndex, isUpdate){
         
-        if(!isUpdate){
-            this.fireEvent("beforerowsinserted", this, rowIndex);
+    onDblClick : function(e,el)
+    {
+        var cell = Roo.get(el);
+        
+        if(!cell || (!this.cellSelection && !this.rowSelection)){
+            return;
         }
-            //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(e.getTarget().nodeName.toLowerCase() != 'td'){
+            cell = cell.findParent('td', false, true);
         }
-            
-        if(!isUpdate){
-            this.fireEvent("rowsinserted", this, rowIndex);
-            //this.syncRowHeights(firstRow, lastRow);
-            //this.stripeRows(firstRow);
-            //this.layout();
+        
+        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);
+        }
     },
-    
-    
-    getRowDom : function(rowIndex)
+    findRowIndex : function(el)
     {
-        var rows = this.el.select('tbody > tr', true).elements;
+        var cell = Roo.get(el);
+        if(!cell) {
+            return false;
+        }
+        var row = cell.findParent('tr', false, true);
         
-        return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
+        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 sort = col.attr('sort');
+        var dir = 'ASC';
+        
+        if(col.select('i', true).first().hasClass('fa-arrow-up')){
+            dir = 'DESC';
+        }
+        
+        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 } });
+        }
     },
-    // returns the object tree for a tr..
-  
     
-    renderRow : function(cm, ds, rowIndex) 
+    renderHeader : function()
     {
-        var d = ds.getAt(rowIndex);
-        
-        var row = {
-            tag : 'tr',
-            cls : 'x-row-' + rowIndex,
+        var header = {
+            tag: 'thead',
             cn : []
         };
-            
-        var cellObjects = [];
+        
+        var cm = this.cm;
+        this.totalWidth = 0;
         
         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;
+            var config = cm.config[i];
             
-            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.
+            var c = {
+                tag: 'th',
+                cls : 'x-hcol-' + i,
+                style : '',
+                
+                html: cm.getColumnHeader(i)
+            };
             
-            if(typeof(value) === 'object'){
-                id = Roo.id();
-                cellObjects.push({
-                    container : id,
-                    cfg : value 
-                })
+            var tooltip = cm.getColumnTooltip(i);
+            if (tooltip) {
+                c.tooltip = tooltip;
             }
             
-            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
-            };
+            var hh = '';
             
-            if (id) {
-                td.id = id;
+            if(typeof(config.sortable) != 'undefined' && config.sortable){
+                c.cls += ' sortable';
+                c.html = '<i class="fa"></i>' + c.html;
             }
             
-            if(typeof(config.colspan) != 'undefined'){
-                td.colspan = config.colspan;
-            }
+            // could use BS4 hidden-..-down 
             
-            if(typeof(config.hidden) != 'undefined' && config.hidden){
-                td.style += ' display:none;';
+            if(typeof(config.lgHeader) != 'undefined'){
+                hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
             }
             
-            if(typeof(config.align) != 'undefined' && config.align.length){
-                td.style += ' text-align:' + config.align + ';';
+            if(typeof(config.mdHeader) != 'undefined'){
+                hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
             }
-            if(typeof(config.valign) != 'undefined' && config.valign.length){
-                td.style += ' vertical-align:' + config.valign + ';';
+            
+            if(typeof(config.smHeader) != 'undefined'){
+                hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
             }
             
-            if(typeof(config.width) != 'undefined'){
-                td.style += ' width:' +  config.width + 'px;';
+            if(typeof(config.xsHeader) != 'undefined'){
+                hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
             }
             
-            if(typeof(config.cursor) != 'undefined'){
-                td.style += ' cursor:' +  config.cursor + ';';
+            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'){
-                td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+                c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
             }
+            // this is the bit that doesnt reall work at all...
+            
+           /*
             
             ['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';
+                    c.cls += ' hidden-' + size + ' hidden' + size + '-down';
                     return;
                 }
                 
-                td.cls += ' col-' + size + '-' + config[size] + (
-                    size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
+                c.cls += ' col-' + size + '-' + config[size] + (
+                    size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
                 );
-                 
-
+                
+                
             });
+            */
+            // at the end?
             
-            row.cn.push(td);
-           
+            c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
+            
+            
+            
+            
+            header.cn.push(c)
         }
         
-        row.cellObjects = cellObjects;
-        
-        return row;
-          
+        return header;
     },
     
-    
-    
-    onBeforeLoad : function()
+    renderBody : function()
     {
+        var body = {
+            tag: 'tbody',
+            cn : [
+                {
+                    tag: 'tr',
+                    cn : [
+                        {
+                            tag : 'td',
+                            colspan :  this.cm.getColumnCount()
+                        }
+                    ]
+                }
+            ]
+        };
         
+        return body;
     },
-     /**
-     * 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)
+    
+    renderFooter : function()
     {
-        var bt = this.mainBody.dom;
-        
-        var rows = this.el.select('tbody > tr', true).elements;
+        var footer = {
+            tag: 'tfoot',
+            cn : [
+                {
+                    tag: 'tr',
+                    cn : [
+                        {
+                            tag : 'td',
+                            colspan :  this.cm.getColumnCount()
+                        }
+                    ]
+                }
+            ]
+        };
         
-        if(typeof(rows[rowIndex]) == 'undefined'){
-            return;
-        }
-        rows[rowIndex].dom.style.display = state ? '' : 'none';
+        return footer;
     },
     
     
-    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(r.cfg.cn){
-            Roo.each(r.cfg.cn, function(c){
-                var child = {
-                    container: t.getChildContainer(),
-                    cfg: c
-                };
-                _this.renderCellObject(child);
-            })
-        }
-    },
     
-    getRowIndex : function(row)
+    onLoad : function()
     {
-        var rowIndex = -1;
+//        Roo.log('ds onload');
+        this.clear();
         
-        Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
-            if(el != row){
-                return;
+        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']);
+                }
             }
-            
-            rowIndex = index;
         });
         
-        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()
-    {
-        //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 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);
+        }
         
-        var cw = ctr.getWidth();
-        this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
+        var tfoot = this.el.select('tfoot', true).first();
         
-        if (tbd) {
+        if(this.footerShow && this.auto_hide_footer && this.mainFoot){
             
-            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.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
+            
+            var total = this.ds.getTotalCount();
+            
+            if(this.footer.pageSize < total){
+                this.mainFoot.show();
+            }
         }
-        cw = Math.max(cw, this.totalWidth);
-        this.getGridEl().select('tbody tr',true).setWidth(cw);
         
-        // resize 'expandable coloumn?
+        Roo.each(this.el.select('tbody td', true).elements, function(e){
+            e.on('mouseover', _this.onMouseover, _this);
+        });
         
-        return; // we doe not have a view in this design..
+        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
+
         
     },
-    onBodyScroll: function()
+    
+    
+    onUpdate : function(ds,record)
     {
-        //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
-        if(this.mainHead){
-            this.mainHead.setStyle({
-                'position' : 'relative',
-                'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
-            });
+        this.refreshRow(record);
+        this.autoSize();
+    },
+    
+    onRemove : function(ds, record, index, isUpdate){
+        if(isUpdate !== true){
+            this.fireEvent("beforerowremoved", this, index, record);
         }
+        var bt = this.bodyEl.dom;
         
-        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
-                    });
-                }
-            }
-            
+        var rows = this.el.select('tbody > tr', true).elements;
+        
+        if(typeof(rows[index]) != 'undefined'){
+            bt.removeChild(rows[index].dom);
+        }
+        
+//        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);
         }
     },
     
-    onHeaderChange : function()
+    onAdd : function(ds, records, rowIndex)
     {
-        var header = this.renderHeader();
-        var table = this.el.select('table', true).first();
-        
-        this.mainHead.remove();
-        this.mainHead = table.createChild(header, this.mainBody, false);
-        
-        Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
-            e.on('click', this.sort, this);
-        }, this);
-        
+        //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;
+        }
         
     },
     
-    onHiddenChange : function(colModel, colIndex, hidden)
+    
+    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 
+            }
+        }
+        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)
     {
-        var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
-        var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
+        if (rowIndex < 0) {
+            return;
+        }
+        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(row < 0 && row >= this.ds.getCount()){
+            return  null;
+        }
+        col = (col !== undefined ? col : 0);
+        var cm = this.cm;
+        while(cm.isHidden(col)){
+            col++;
+        }
+
+        var el = this.getCellDom(row, col);
+        if(!el){
+            return null;
+        }
+        var c = this.bodyEl.dom;
+
+        var ctop = parseInt(el.offsetTop, 10);
+        var cleft = parseInt(el.offsetLeft, 10);
+        var cbot = ctop + el.offsetHeight;
+        var cright = cleft + el.offsetWidth;
+
+        //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;
+        }
+
+        if(hscroll !== false){
+            if(cleft < sleft){
+                c.scrollLeft = cleft;
+            }else if(cright > sright){
+                c.scrollLeft = cright-c.clientWidth;
+            }
+        }
+
+        return el;
+    },
+    
+    
+    insertRow : function(dm, rowIndex, isUpdate){
         
-        this.CSS.updateRule(thSelector, "display", "");
-        this.CSS.updateRule(tdSelector, "display", "");
+        if(!isUpdate){
+            this.fireEvent("beforerowsinserted", this, rowIndex);
+        }
+            //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));
         
-        if(hidden){
-            this.CSS.updateRule(thSelector, "display", "none");
-            this.CSS.updateRule(tdSelector, "display", "none");
+        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.onHeaderChange();
-        this.onLoad();
     },
     
-    setColumnWidth: function(col_index, width)
+    
+    getRowDom : function(rowIndex)
     {
-        // width = "md-2 xs-2..."
-        if(!this.colModel.config[col_index]) {
-            return;
-        }
+        var rows = this.el.select('tbody > tr', true).elements;
         
-        var w = width.split(" ");
+        return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
         
-        var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
+    },
+    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];
         
-        var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
+    },
+    
+    // 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 = [];
         
-        for(var j = 0; j < w.length; j++) {
+        for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+            var config = cm.config[i];
             
-            if(!w[j]) {
-                continue;
+            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.
             
-            var size_cls = w[j].split("-");
+            if(typeof(value) === 'object'){
+                id = Roo.id();
+                cellObjects.push({
+                    container : id,
+                    cfg : value 
+                })
+            }
             
-            if(!Number.isInteger(size_cls[1] * 1)) {
-                continue;
+            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(!this.colModel.config[col_index][size_cls[0]]) {
-                continue;
+            if(typeof(config.colspan) != 'undefined'){
+                td.colspan = config.colspan;
             }
             
-            if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
-                continue;
+            
+            
+            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;';
             }
+            */
             
-            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.cursor) != 'undefined'){
+                td.style += ' cursor:' +  config.cursor + ';';
+            }
             
-            for(var i = 0; i < rows.length; i++) {
-                
-                var size_cls = w[j].split("-");
+            if(typeof(config.cls) != 'undefined'){
+                td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+            }
+            /*
+            ['xs','sm','md','lg'].map(function(size){
                 
-                if(!Number.isInteger(size_cls[1] * 1)) {
-                    continue;
+                if(typeof(config[size]) == 'undefined'){
+                    return;
                 }
                 
-                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;
+                  
+                if (!config[size]) { // 0 = hidden
+                    // BS 4 '0' is treated as hide that column and below.
+                    td.cls += ' hidden-' + size + ' hidden' + size + '-down';
+                    return;
                 }
                 
-                rows[i].classList.replace(
-                    "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
-                    "col-"+size_cls[0]+"-"+size_cls[1]
+                td.cls += ' col-' + size + '-' + config[size] + (
+                    size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
                 );
-            }
-            
-            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,  {
+            });
+            */
+            row.cn.push(td);
+           
+        }
+        
+        row.cellObjects = cellObjects;
+        
+        return row;
+          
+    },
     
-    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,
     
     
-    getAutoCreate : function(){
-        var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
-       
-        cfg = {
-            tag: 'td'
-        };
+    onBeforeLoad : function()
+    {
         
-        if(this.tag){
-            cfg.tag = this.tag;
-        }
+    },
+     /**
+     * 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;
         
-        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
+        var rows = this.el.select('tbody > tr', true).elements;
+        
+        if(typeof(rows[rowIndex]) == 'undefined'){
+            return;
         }
+        rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
         
-       
-        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;
-        }
-        if(this.align){
-            cfg.align = this.align;
+    getSelectionModel : function(){
+        if(!this.selModel){
+            this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
         }
-        if(this.bgcolor){
-            cfg.bgcolor = this.bgcolor;
+        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(r.cfg.cn){
+            Roo.each(r.cfg.cn, function(c){
+                var child = {
+                    container: t.getChildContainer(),
+                    cfg: c
+                };
+                _this.renderCellObject(child);
+            })
         }
-        if(this.charoff){
-            cfg.charoff = this.charoff;
+    },
+    /**
+     * get the Row Index from a dom element.
+     * @param {Roo.Element} row The row to look for
+     * @returns {Number} the row
+     */
+    getRowIndex : function(row)
+    {
+        var rowIndex = -1;
+        
+        Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
+            if(el != row){
+                return;
+            }
+            
+            rowIndex = index;
+        });
+        
+        return rowIndex;
+    },
+    /**
+     * get the header TH element for columnIndex
+     * @param {Number} columnIndex
+     * @returns {Roo.Element}
+     */
+    getHeaderIndex: function(colIndex)
+    {
+        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);
         }
-        if(this.valign){
-            cfg.valign = this.valign;
+        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 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;
+        }
+        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..
+        
+    },
+    onBodyScroll: function()
+    {
+        //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
+        if(this.headEl){
+            this.headEl.setStyle({
+                'position' : 'relative',
+                'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
+            });
+        }
+        
+        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
+                    });
+                }
+            }
+            
+        }
+    },
+    onColumnSplitterMoved : function(i, diff)
+    {
+        this.userResized = true;
+        
+        var cm = this.colModel;
+        
+        var w = this.getHeaderIndex(i).getWidth() + diff;
+        
+        
+        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);
+    },
+    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)
+    {
+        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");
+        }
+        
+        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 cfg;
     }
-   
 });
 
+// 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
  *
- * table body
+ * table cell
  * 
  */
 
 /**
- * @class Roo.bootstrap.TableBody
+ * @class Roo.bootstrap.TableCell
  * @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
+ * 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 TableBody
+ * Create a new TableCell
  * @param {Object} config The config object
  */
 
-Roo.bootstrap.TableBody = function(config){
-    Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+Roo.bootstrap.TableCell = function(config){
+    Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
 };
 
-Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
+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,
+    
     
     getAutoCreate : function(){
-        var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
+        var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
+       
+        cfg = {
+            tag: 'td'
+        };
+        
+        if(this.tag){
+            cfg.tag = this.tag;
+        }
+        
+        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;
+    }
+   
+});
+
+
+ /*
+ * - 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;
+        }
+        if(this.align){
+            cfg.align = this.align;
+        }
+        if(this.bgcolor){
+            cfg.bgcolor = this.bgcolor;
+        }
+        if(this.charoff){
+            cfg.charoff = this.charoff;
+        }
+        if(this.valign){
+            cfg.valign = this.valign;
+        }
+       
+        return cfg;
+    }
+   
+});
+
+
+ /*
+ * - LGPL
+ *
+ * table body
+ * 
+ */
+
+/**
+ * @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
+ */
+
+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'
@@ -25725,874 +26738,370 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             
             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.
-     */
-    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])
-        }
-    },
-    
-    
-    /**
-     * 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)
-    {
-         
-         
-        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);
-        
-        
-    },
-    
-    
-    
-    
-    domToHTML : function(currentElement, depth, nopadtext) {
-        
-        depth = depth || 0;
-        nopadtext = nopadtext || false;
-    
-        if (!currentElement) {
-            return this.domToHTML(this.doc.body);
-        }
-        
-        //Roo.log(currentElement);
-        var j;
-        var allText = false;
-        var nodeName = currentElement.nodeName;
-        var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
-        
-        if  (nodeName == '#text') {
-            
-            return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
-        }
-        
-        
-        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);
-        }
-        
-        ret += innerHTML;
-        
-        if (!allText) {
-                // The remaining code is mostly for formatting the tree
-            ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
-        }
-        
-        
-        if (tagName) {
-            ret+= "</"+tagName+">";
-        }
-        return ret;
-        
-    },
-        
-    applyBlacklists : function()
-    {
-        var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
-        var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
-        
-        this.white = [];
-        this.black = [];
-        Roo.each(Roo.HtmlEditorCore.white, function(tag) {
-            if (b.indexOf(tag) > -1) {
-                return;
-            }
-            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);
-        
-        
-        Roo.each(Roo.HtmlEditorCore.black, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            this.black.push(tag);
-            
-        }, this);
-        
-        Roo.each(b, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            if (this.black.indexOf(tag) > -1) {
-                return;
-            }
-            this.black.push(tag);
-            
-        }, this);
-        
-        
-        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);
-        
-        
-        Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            this.cblack.push(tag);
-            
-        }, this);
-        
-        Roo.each(b, function(tag) {
-            if (w.indexOf(tag) > -1) {
-                return;
-            }
-            if (this.cblack.indexOf(tag) > -1) {
-                return;
-            }
-            this.cblack.push(tag);
-            
-        }, 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
-            });
-        });
-
-        
-    },
-    
-    removeStylesheets : function()
-    {
-        var _this = this;
-        
-        Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
-            s.remove();
-        });
-    },
-    
-    setStyle : function(style)
-    {
-        Roo.get(this.iframe.contentDocument.head).createChild({
-            tag : 'style',
-            type : 'text/css',
-            html : style
-        });
-
-        return;
-    }
-    
-    // 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
-     */
-    /**
-     * @cfg {String} invalidText @hide
-     */
-    /**
-     * @cfg {String} msgFx @hide
-     */
-    /**
-     * @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..
-      
-         
-//        '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
- *
- * HtmlEditor
- * 
- */
-
-/**
- * @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 = [];
-    }
-    
-    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,  {
-    
-    
-      /**
-     * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+            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 +27132,9 @@ Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
     /**
      * @cfg {String} inputType @hide
      */
-     
+    /**
+     * @cfg {String} invalidClass @hide
+     */
     /**
      * @cfg {String} invalidText @hide
      */
@@ -26634,939 +27145,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.