Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / Table2.html
index 58eb857..dcfeeca 100644 (file)
-<!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-panel.Table'>/**
-</span> * @class Ext.panel.Table
- * @extends Ext.panel.Panel
- * @xtype tablepanel
- * @private
- * @author Nicolas Ferrero
- * TablePanel is a private class and the basis of both TreePanel and GridPanel.
- *
- * TablePanel aggregates:
- *
- *  - a Selection Model
- *  - a View
- *  - a Store
- *  - Scrollers
- *  - Ext.grid.header.Container
- *
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>The source code</title>
+  <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
+  <script type="text/javascript" src="../prettify/prettify.js"></script>
+  <style type="text/css">
+    .highlight { display: block; background-color: #ddd; }
+  </style>
+  <script type="text/javascript">
+    function highlight() {
+      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
+    }
+  </script>
+</head>
+<body onload="prettyPrint(); highlight();">
+  <pre class="prettyprint lang-js"><span id='Ext-layout-container-Table'>/**
+</span> * @class Ext.layout.container.Table
+ * @extends Ext.layout.container.Auto
+ * &lt;p&gt;This layout allows you to easily render content into an HTML table.  The total number of columns can be
+ * specified, and rowspan and colspan can be used to create complex layouts within the table.
+ * This class is intended to be extended or created via the &lt;code&gt;layout: {type: 'table'}&lt;/code&gt;
+ * {@link Ext.container.Container#layout} config, and should generally not need to be created directly via the new keyword.&lt;/p&gt;
+ * &lt;p&gt;Note that when creating a layout via config, the layout-specific config properties must be passed in via
+ * the {@link Ext.container.Container#layout} object which will then be applied internally to the layout.  In the
+ * case of TableLayout, the only valid layout config properties are {@link #columns} and {@link #tableAttrs}.
+ * However, the items added to a TableLayout can supply the following table-specific config properties:&lt;/p&gt;
+ * &lt;ul&gt;
+ * &lt;li&gt;&lt;b&gt;rowspan&lt;/b&gt; Applied to the table cell containing the item.&lt;/li&gt;
+ * &lt;li&gt;&lt;b&gt;colspan&lt;/b&gt; Applied to the table cell containing the item.&lt;/li&gt;
+ * &lt;li&gt;&lt;b&gt;cellId&lt;/b&gt; An id applied to the table cell containing the item.&lt;/li&gt;
+ * &lt;li&gt;&lt;b&gt;cellCls&lt;/b&gt; A CSS class name added to the table cell containing the item.&lt;/li&gt;
+ * &lt;/ul&gt;
+ * &lt;p&gt;The basic concept of building up a TableLayout is conceptually very similar to building up a standard
+ * HTML table.  You simply add each panel (or &quot;cell&quot;) that you want to include along with any span attributes
+ * specified as the special config properties of rowspan and colspan which work exactly like their HTML counterparts.
+ * Rather than explicitly creating and nesting rows and columns as you would in HTML, you simply specify the
+ * total column count in the layoutConfig and start adding panels in their natural order from left to right,
+ * top to bottom.  The layout will automatically figure out, based on the column count, rowspans and colspans,
+ * how to position each panel within the table.  Just like with HTML tables, your rowspans and colspans must add
+ * up correctly in your overall layout or you'll end up with missing and/or extra cells!  Example usage:&lt;/p&gt;
+ * {@img Ext.layout.container.Table/Ext.layout.container.Table.png Ext.layout.container.Table container layout}
+ * &lt;pre&gt;&lt;code&gt;
+// This code will generate a layout table that is 3 columns by 2 rows
+// with some spanning included.  The basic layout will be:
+// +--------+-----------------+
+// |   A    |   B             |
+// |        |--------+--------|
+// |        |   C    |   D    |
+// +--------+--------+--------+
+    Ext.create('Ext.panel.Panel', {
+        title: 'Table Layout',
+        width: 300,
+        height: 150,
+        layout: {
+            type: 'table',
+            // The total column count must be specified here
+            columns: 3
+        },
+        defaults: {
+            // applied to each contained panel
+            bodyStyle:'padding:20px'
+        },
+        items: [{
+            html: 'Cell A content',
+            rowspan: 2
+        },{
+            html: 'Cell B content',
+            colspan: 2
+        },{
+            html: 'Cell C content',
+            cellCls: 'highlight'
+        },{
+            html: 'Cell D content'
+        }],
+        renderTo: Ext.getBody()
+    });
+&lt;/code&gt;&lt;/pre&gt;
  */
-Ext.define('Ext.panel.Table', {
-    extend: 'Ext.panel.Panel',
-
-    alias: 'widget.tablepanel',
 
-    uses: [
-        'Ext.selection.RowModel',
-        'Ext.grid.Scroller',
-        'Ext.grid.header.Container',
-        'Ext.grid.Lockable'
-    ],
+Ext.define('Ext.layout.container.Table', {
 
-    cls: Ext.baseCSSPrefix + 'grid',
-    extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
+    /* Begin Definitions */
 
-    layout: 'fit',
-<span id='Ext-panel.Table-property-hasView'>    /**
-</span>     * Boolean to indicate that a view has been injected into the panel.
-     * @property hasView
-     */
-    hasView: false,
-
-    // each panel should dictate what viewType and selType to use
-    viewType: null,
-    selType: 'rowmodel',
+    alias: ['layout.table'],
+    extend: 'Ext.layout.container.Auto',
+    alternateClassName: 'Ext.layout.TableLayout',
 
-<span id='Ext-panel.Table-cfg-scrollDelta'>    /**
-</span>     * @cfg {Number} scrollDelta
-     * Number of pixels to scroll when scrolling with mousewheel.
-     * Defaults to 40.
-     */
-    scrollDelta: 40,
+    /* End Definitions */
 
-<span id='Ext-panel.Table-cfg-scroll'>    /**
-</span>     * @cfg {String/Boolean} scroll
-     * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'.
-     * Defaults to true.
+<span id='Ext-layout-container-Table-cfg-columns'>    /**
+</span>     * @cfg {Number} columns
+     * The total number of columns to create in the table for this layout.  If not specified, all Components added to
+     * this layout will be rendered into a single row using one column per Component.
      */
-    scroll: true,
 
-<span id='Ext-panel.Table-cfg-columns'>    /**
-</span>     * @cfg {Array} columns
-     * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each
-     * column definition provides the header text for the column, and a definition of where the data for that column comes from.
+    // private
+    monitorResize:false,
+
+    type: 'table',
+
+    // Table layout is a self-sizing layout. When an item of for example, a dock layout, the Panel must expand to accommodate
+    // a table layout. See in particular AbstractDock::onLayout for use of this flag.
+    autoSize: true,
+
+    clearEl: true, // Base class will not create it if already truthy. Not needed in tables.
+
+    targetCls: Ext.baseCSSPrefix + 'table-layout-ct',
+    tableCls: Ext.baseCSSPrefix + 'table-layout',
+    cellCls: Ext.baseCSSPrefix + 'table-layout-cell',
+
+<span id='Ext-layout-container-Table-cfg-tableAttrs'>    /**
+</span>     * @cfg {Object} tableAttrs
+     * &lt;p&gt;An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
+     * used to create the layout's &lt;tt&gt;&amp;lt;table&amp;gt;&lt;/tt&gt; element. Example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;
+{
+    xtype: 'panel',
+    layout: {
+        type: 'table',
+        columns: 3,
+        tableAttrs: {
+            style: {
+                width: '100%'
+            }
+        }
+    }
+}&lt;/code&gt;&lt;/pre&gt;
      */
+    tableAttrs:null,
 
-<span id='Ext-panel.Table-cfg-forceFit'>    /**
-</span>     * @cfg {Boolean} forceFit
-     * Specify as &lt;code&gt;true&lt;/code&gt; to force the columns to fit into the available width. Headers are first sized according to configuration, whether that be
-     * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used..
+<span id='Ext-layout-container-Table-cfg-trAttrs'>    /**
+</span>     * @cfg {Object} trAttrs
+     * &lt;p&gt;An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
+     * used to create the layout's &lt;tt&gt;&amp;lt;tr&amp;gt;&lt;/tt&gt; elements.
      */
 
-<span id='Ext-panel.Table-cfg-hideHeaders'>    /**
-</span>     * @cfg {Boolean} hideHeaders
-     * Specify as &lt;code&gt;true&lt;/code&gt; to hide the headers.
+<span id='Ext-layout-container-Table-cfg-tdAttrs'>    /**
+</span>     * @cfg {Object} tdAttrs
+     * &lt;p&gt;An object containing properties which are added to the {@link Ext.core.DomHelper DomHelper} specification
+     * used to create the layout's &lt;tt&gt;&amp;lt;td&amp;gt;&lt;/tt&gt; elements.
      */
 
-<span id='Ext-panel.Table-cfg-sortableColumns'>    /**
-</span>     * @cfg {Boolean} sortableColumns
-     * Defaults to true. Set to false to disable column sorting via clicking the
-     * header and via the Sorting menu items.
+<span id='Ext-layout-container-Table-method-renderItems'>    /**
+</span>     * @private
+     * Iterates over all passed items, ensuring they are rendered in a cell in the proper
+     * location in the table structure.
      */
-    sortableColumns: true,
-
-    verticalScrollDock: 'right',
-    verticalScrollerType: 'gridscroller',
-
-    horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
-    verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
-
-    // private property used to determine where to go down to find views
-    // this is here to support locking.
-    scrollerOwner: true,
-
-    invalidateScrollerOnRefresh: true,
-    
-    enableColumnMove: true,
-    enableColumnResize: true,
-
-
-    initComponent: function() {
-        //&lt;debug&gt;
-        if (!this.viewType) {
-            Ext.Error.raise(&quot;You must specify a viewType config.&quot;);
-        }
-        if (!this.store) {
-            Ext.Error.raise(&quot;You must specify a store config&quot;);
-        }
-        if (this.headers) {
-            Ext.Error.raise(&quot;The headers config is not supported. Please specify columns instead.&quot;);
-        }
-        //&lt;/debug&gt;
-
-        var me          = this,
-            scroll      = me.scroll,
-            vertical    = false,
-            horizontal  = false,
-            headerCtCfg = me.columns || me.colModel,
-            i           = 0,
-            view,
-            border = me.border;
-
-        // Set our determinScrollbars method to reference a buffered call to determinScrollbars which fires on a 30ms buffer.
-        me.determineScrollbars = Ext.Function.createBuffered(me.determineScrollbars, 30);
-        me.injectView = Ext.Function.createBuffered(me.injectView, 30);
-
-        if (me.hideHeaders) {
-            border = false;
-        }
-
-        // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
-        // Either way, we extract a columns property referencing an array of Column definitions.
-        if (headerCtCfg instanceof Ext.grid.header.Container) {
-            me.headerCt = headerCtCfg;
-            me.headerCt.border = border;
-            me.columns = me.headerCt.items.items;
-        } else {
-            if (Ext.isArray(headerCtCfg)) {
-                headerCtCfg = {
-                    items: headerCtCfg,
-                    border: border
-                };
-            }
-            Ext.apply(headerCtCfg, {
-                forceFit: me.forceFit,
-                sortable: me.sortableColumns,
-                enableColumnMove: me.enableColumnMove,
-                enableColumnResize: me.enableColumnResize,
-                border:  border
-            });
-            me.columns = headerCtCfg.items;
-
-             // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
-             // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
-             if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
-                 me.self.mixin('lockable', Ext.grid.Lockable);
-                 me.injectLockable();
-             }
-        }
-
-        me.store = Ext.data.StoreManager.lookup(me.store);
-        me.addEvents(
-<span id='Ext-panel.Table-event-scrollerhide'>            /**
-</span>             * @event scrollerhide
-             * Fires when a scroller is hidden
-             * @param {Ext.grid.Scroller} scroller
-             * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
-             */
-            'scrollerhide',
-<span id='Ext-panel.Table-event-scrollershow'>            /**
-</span>             * @event scrollershow
-             * Fires when a scroller is shown
-             * @param {Ext.grid.Scroller} scroller
-             * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
-             */
-            'scrollershow'
-        );
-
-        me.bodyCls = me.bodyCls || '';
-        me.bodyCls += (' ' + me.extraBodyCls);
-
-        // autoScroll is not a valid configuration
-        delete me.autoScroll;
-
-        // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
-        // than a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.
-        if (!me.hasView) {
-
-            // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
-            // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
-            if (!me.headerCt) {
-                me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
-            }
+    renderItems: function(items) {
+        var tbody = this.getTable().tBodies[0],
+            rows = tbody.rows,
+            i = 0,
+            len = items.length,
+            cells, curCell, rowIdx, cellIdx, item, trEl, tdEl, itemCt;
 
-            // Extract the array of Column objects
-            me.columns = me.headerCt.items.items;
+        // Calculate the correct cell structure for the current items
+        cells = this.calculateCells(items);
 
-            if (me.hideHeaders) {
-                me.headerCt.height = 0;
-                me.headerCt.border = false;
-                me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
-                me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
-                // IE Quirks Mode fix
-                // If hidden configuration option was used, several layout calculations will be bypassed.
-                if (Ext.isIEQuirks) {
-                    me.headerCt.style = {
-                        display: 'none'
-                    };
+        // Loop over each cell and compare to the current cells in the table, inserting/
+        // removing/moving cells as needed, and making sure each item is rendered into
+        // the correct cell.
+        for (; i &lt; len; i++) {
+            curCell = cells[i];
+            rowIdx = curCell.rowIdx;
+            cellIdx = curCell.cellIdx;
+            item = items[i];
+
+            // If no row present, create and insert one
+            trEl = rows[rowIdx];
+            if (!trEl) {
+                trEl = tbody.insertRow(rowIdx);
+                if (this.trAttrs) {
+                    trEl.set(this.trAttrs);
                 }
             }
 
-            // turn both on.
-            if (scroll === true || scroll === 'both') {
-                vertical = horizontal = true;
-            } else if (scroll === 'horizontal') {
-                horizontal = true;
-            } else if (scroll === 'vertical') {
-                vertical = true;
-            // All other values become 'none' or false.
-            } else {
-                me.headerCt.availableSpaceOffset = 0;
+            // If no cell present, create and insert one
+            itemCt = tdEl = Ext.get(trEl.cells[cellIdx] || trEl.insertCell(cellIdx));
+            if (this.needsDivWrap()) { //create wrapper div if needed - see docs below
+                itemCt = tdEl.first() || tdEl.createChild({tag: 'div'});
+                itemCt.setWidth(null);
             }
 
-            if (vertical) {
-                me.verticalScroller = me.verticalScroller || {};
-                Ext.applyIf(me.verticalScroller, {
-                    dock: me.verticalScrollDock,
-                    xtype: me.verticalScrollerType,
-                    store: me.store
-                });
-                me.verticalScroller = Ext.ComponentManager.create(me.verticalScroller);
-                me.mon(me.verticalScroller, {
-                    bodyscroll: me.onVerticalScroll,
-                    scope: me
-                });
+            // Render or move the component into the cell
+            if (!item.rendered) {
+                this.renderItem(item, itemCt, 0);
             }
-
-            if (horizontal) {
-                me.horizontalScroller = Ext.ComponentManager.create({
-                    xtype: 'gridscroller',
-                    section: me,
-                    dock: 'bottom',
-                    store: me.store
-                });
-                me.mon(me.horizontalScroller, {
-                    bodyscroll: me.onHorizontalScroll,
-                    scope: me
-                });
+            else if (!this.isValidParent(item, itemCt, 0)) {
+                this.moveItem(item, itemCt, 0);
             }
 
-            me.headerCt.on('columnresize', me.onHeaderResize, me);
-            me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']);
-            me.features = me.features || [];
-            me.dockedItems = me.dockedItems || [];
-            me.dockedItems.unshift(me.headerCt);
-            me.viewConfig = me.viewConfig || {};
-            me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;
-
-            // AbstractDataView will look up a Store configured as an object
-            // getView converts viewConfig into a View instance
-            view = me.getView();
-
-            if (view) {
-                me.mon(view.store, {
-                    load: me.onStoreLoad,
-                    scope: me
-                });
-                me.mon(view, {
-                    refresh: {
-                        fn: this.onViewRefresh,
-                        scope: me,
-                        buffer: 50
-                    },
-                    itemupdate: me.onViewItemUpdate,
-                    scope: me
-                });
-                this.relayEvents(view, [
-<span id='Ext-panel.Table-event-beforeitemmousedown'>                    /**
-</span>                     * @event beforeitemmousedown
-                     * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmousedown',
-<span id='Ext-panel.Table-event-beforeitemmouseup'>                    /**
-</span>                     * @event beforeitemmouseup
-                     * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmouseup',
-<span id='Ext-panel.Table-event-beforeitemmouseenter'>                    /**
-</span>                     * @event beforeitemmouseenter
-                     * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmouseenter',
-<span id='Ext-panel.Table-event-beforeitemmouseleave'>                    /**
-</span>                     * @event beforeitemmouseleave
-                     * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmouseleave',
-<span id='Ext-panel.Table-event-beforeitemclick'>                    /**
-</span>                     * @event beforeitemclick
-                     * Fires before the click event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemclick',
-<span id='Ext-panel.Table-event-beforeitemdblclick'>                    /**
-</span>                     * @event beforeitemdblclick
-                     * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemdblclick',
-<span id='Ext-panel.Table-event-beforeitemcontextmenu'>                    /**
-</span>                     * @event beforeitemcontextmenu
-                     * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemcontextmenu',
-<span id='Ext-panel.Table-event-itemmousedown'>                    /**
-</span>                     * @event itemmousedown
-                     * Fires when there is a mouse down on an item
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmousedown',
-<span id='Ext-panel.Table-event-itemmouseup'>                    /**
-</span>                     * @event itemmouseup
-                     * Fires when there is a mouse up on an item
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmouseup',
-<span id='Ext-panel.Table-event-itemmouseenter'>                    /**
-</span>                     * @event itemmouseenter
-                     * Fires when the mouse enters an item.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmouseenter',
-<span id='Ext-panel.Table-event-itemmouseleave'>                    /**
-</span>                     * @event itemmouseleave
-                     * Fires when the mouse leaves an item.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmouseleave',
-<span id='Ext-panel.Table-event-itemclick'>                    /**
-</span>                     * @event itemclick
-                     * Fires when an item is clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemclick',
-<span id='Ext-panel.Table-event-itemdblclick'>                    /**
-</span>                     * @event itemdblclick
-                     * Fires when an item is double clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemdblclick',
-<span id='Ext-panel.Table-event-itemcontextmenu'>                    /**
-</span>                     * @event itemcontextmenu
-                     * Fires when an item is right clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemcontextmenu',
-<span id='Ext-panel.Table-event-beforecontainermousedown'>                    /**
-</span>                     * @event beforecontainermousedown
-                     * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermousedown',
-<span id='Ext-panel.Table-event-beforecontainermouseup'>                    /**
-</span>                     * @event beforecontainermouseup
-                     * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermouseup',
-<span id='Ext-panel.Table-event-beforecontainermouseover'>                    /**
-</span>                     * @event beforecontainermouseover
-                     * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermouseover',
-<span id='Ext-panel.Table-event-beforecontainermouseout'>                    /**
-</span>                     * @event beforecontainermouseout
-                     * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermouseout',
-<span id='Ext-panel.Table-event-beforecontainerclick'>                    /**
-</span>                     * @event beforecontainerclick
-                     * Fires before the click event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainerclick',
-<span id='Ext-panel.Table-event-beforecontainerdblclick'>                    /**
-</span>                     * @event beforecontainerdblclick
-                     * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainerdblclick',
-<span id='Ext-panel.Table-event-beforecontainercontextmenu'>                    /**
-</span>                     * @event beforecontainercontextmenu
-                     * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainercontextmenu',
-<span id='Ext-panel.Table-event-containermouseup'>                    /**
-</span>                     * @event containermouseup
-                     * Fires when there is a mouse up on the container
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containermouseup',
-<span id='Ext-panel.Table-event-containermouseover'>                    /**
-</span>                     * @event containermouseover
-                     * Fires when you move the mouse over the container.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containermouseover',
-<span id='Ext-panel.Table-event-containermouseout'>                    /**
-</span>                     * @event containermouseout
-                     * Fires when you move the mouse out of the container.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containermouseout',
-<span id='Ext-panel.Table-event-containerclick'>                    /**
-</span>                     * @event containerclick
-                     * Fires when the container is clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containerclick',
-<span id='Ext-panel.Table-event-containerdblclick'>                    /**
-</span>                     * @event containerdblclick
-                     * Fires when the container is double clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containerdblclick',
-<span id='Ext-panel.Table-event-containercontextmenu'>                    /**
-</span>                     * @event containercontextmenu
-                     * Fires when the container is right clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containercontextmenu',
-
-<span id='Ext-panel.Table-event-selectionchange'>                    /**
-</span>                     * @event selectionchange
-                     * Fires when the selected nodes change. Relayed event from the underlying selection model.
-                     * @param {Ext.view.View} this
-                     * @param {Array} selections Array of the selected nodes
-                     */
-                    'selectionchange',
-<span id='Ext-panel.Table-event-beforeselect'>                    /**
-</span>                     * @event beforeselect
-                     * Fires before a selection is made. If any handlers return false, the selection is cancelled.
-                     * @param {Ext.view.View} this
-                     * @param {HTMLElement} node The node to be selected
-                     * @param {Array} selections Array of currently selected nodes
-                     */
-                    'beforeselect'
-                ]);
-            }
-        }
-        me.callParent(arguments);
-    },
-
-    // state management
-    initStateEvents: function(){
-        var events = this.stateEvents;
-        // push on stateEvents if they don't exist
-        Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
-            if (Ext.Array.indexOf(events, event)) {
-                events.push(event);
+            // Set the cell properties
+            if (this.tdAttrs) {
+                tdEl.set(this.tdAttrs);
             }
-        });
-        this.callParent();
-    },
-
-    getState: function(){
-        var state = {
-            columns: []
-        },
-        sorter = this.store.sorters.first();
-
-        this.headerCt.items.each(function(header){
-            state.columns.push({
-                id: header.headerId,
-                width: header.flex ? undefined : header.width,
-                hidden: header.hidden,
-                sortable: header.sortable
+            tdEl.set({
+                colSpan: item.colspan || 1,
+                rowSpan: item.rowspan || 1,
+                id: item.cellId || '',
+                cls: this.cellCls + ' ' + (item.cellCls || '')
             });
-        });
 
-        if (sorter) {
-            state.sort = {
-                property: sorter.property,
-                direction: sorter.direction
-            };
-        }
-        return state;
-    },
-
-    applyState: function(state) {
-        var headers = state.columns,
-            length = headers ? headers.length : 0,
-            headerCt = this.headerCt,
-            items = headerCt.items,
-            sorter = state.sort,
-            store = this.store,
-            i = 0,
-            index,
-            headerState,
-            header;
-
-        for (; i &lt; length; ++i) {
-            headerState = headers[i];
-            header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
-            index = items.indexOf(header);
-            if (i !== index) {
-                headerCt.moveHeader(index, i);
-            }
-            header.sortable = headerState.sortable;
-            if (Ext.isDefined(headerState.width)) {
-                delete header.flex;
-                if (header.rendered) {
-                    header.setWidth(headerState.width);
-                } else {
-                    header.minWidth = header.width = headerState.width;
+            // If at the end of a row, remove any extra cells
+            if (!cells[i + 1] || cells[i + 1].rowIdx !== rowIdx) {
+                cellIdx++;
+                while (trEl.cells[cellIdx]) {
+                    trEl.deleteCell(cellIdx);
                 }
             }
-            header.hidden = headerState.hidden;
-        }
-
-        if (sorter) {
-            if (store.remoteSort) {
-                store.sorters.add(Ext.create('Ext.util.Sorter', {
-                    property: sorter.property,
-                    direction: sorter.direction
-                }));
-            }
-            else {
-                store.sort(sorter.property, sorter.direction);
-            }
         }
-    },
-
-<span id='Ext-panel.Table-method-getStore'>    /**
-</span>     * Returns the store associated with this Panel.
-     * @return {Ext.data.Store} The store
-     */
-    getStore: function(){
-        return this.store;
-    },
-
-<span id='Ext-panel.Table-method-getView'>    /**
-</span>     * Gets the view for this panel.
-     * @return {Ext.view.Table}
-     */
-    getView: function() {
-        var me = this,
-            sm;
 
-        if (!me.view) {
-            sm = me.getSelectionModel();
-            me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
-                xtype: me.viewType,
-                store: me.store,
-                headerCt: me.headerCt,
-                selModel: sm,
-                features: me.features,
-                panel: me
-            }));
-            me.mon(me.view, {
-                uievent: me.processEvent,
-                scope: me
-            });
-            sm.view = me.view;
-            me.headerCt.view = me.view;
-            me.relayEvents(me.view, ['cellclick', 'celldblclick']);
+        // Delete any extra rows
+        rowIdx++;
+        while (tbody.rows[rowIdx]) {
+            tbody.deleteRow(rowIdx);
         }
-        return me.view;
     },
 
-<span id='Ext-panel.Table-property-setAutoScroll'>    /**
-</span>     * @private
-     * @override
-     * autoScroll is never valid for all classes which extend TablePanel.
-     */
-    setAutoScroll: Ext.emptyFn,
-
-    // This method hijacks Ext.view.Table's el scroll method.
-    // This enables us to keep the virtualized scrollbars in sync
-    // with the view. It currently does NOT support animation.
-    elScroll: function(direction, distance, animate) {
-        var me = this,
-            scroller;
-
-        if (direction === &quot;up&quot; || direction === &quot;left&quot;) {
-            distance = -distance;
-        }
-
-        if (direction === &quot;down&quot; || direction === &quot;up&quot;) {
-            scroller = me.getVerticalScroller();
-            scroller.scrollByDeltaY(distance);
-        } else {
-            scroller = me.getHorizontalScroller();
-            scroller.scrollByDeltaX(distance);
-        }
-    },
-    
     afterLayout: function() {
-        this.callParent(arguments);
-        this.injectView();
-    },
-    
-
-<span id='Ext-panel.Table-method-injectView'>    /**
-</span>     * @private
-     * Called after this Component has achieved its correct initial size, after all layouts have done their thing.
-     * This is so we can add the View only after the initial size is known. This method is buffered 30ms.
-     */
-    injectView: function() {
-        if (!this.hasView &amp;&amp; !this.collapsed) {
-            var me   = this,
-                view = me.getView();
-
-            me.hasView = true;
-            me.add(view);
+        this.callParent();
 
-            // hijack the view el's scroll method
-            view.el.scroll = Ext.Function.bind(me.elScroll, me);
-            // We use to listen to document.body wheel events, but that's a
-            // little much. We scope just to the view now.
-            me.mon(view.el, {
-                mousewheel: me.onMouseWheel,
-                scope: me
+        if (this.needsDivWrap()) {
+            // set wrapper div width to match layed out item - see docs below
+            Ext.Array.forEach(this.getLayoutItems(), function(item) {
+                Ext.fly(item.el.dom.parentNode).setWidth(item.getWidth());
             });
         }
     },
 
-    afterExpand: function() {
-        this.callParent(arguments);
-        if (!this.hasView) {
-            this.injectView();
-        }
-    },
-
-<span id='Ext-panel.Table-method-processEvent'>    /**
+<span id='Ext-layout-container-Table-method-calculateCells'>    /**
 </span>     * @private
-     * Process UI events from the view. Propagate them to whatever internal Components need to process them
-     * @param {String} type Event type, eg 'click'
-     * @param {TableView} view TableView Component
-     * @param {HtmlElement} cell Cell HtmlElement the event took place within
-     * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
-     * @param {Number} cellIndex Cell index within the row
-     * @param {EventObject} e Original event
-     */
-    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
-        var me = this,
-            header;
-
-        if (cellIndex !== -1) {
-            header = me.headerCt.getGridColumns()[cellIndex];
-            return header.processEvent.apply(header, arguments);
-        }
-    },
-
-<span id='Ext-panel.Table-method-determineScrollbars'>    /**
-</span>     * Request a recalculation of scrollbars and put them in if they are needed.
-     */
-    determineScrollbars: function() {
-        var me = this,
-            viewElDom,
-            centerScrollWidth,
-            centerClientWidth,
-            scrollHeight,
-            clientHeight;
-
-        if (!me.collapsed &amp;&amp; me.view &amp;&amp; me.view.el) {
-            viewElDom = me.view.el.dom;
-            //centerScrollWidth = viewElDom.scrollWidth;
-            centerScrollWidth = me.headerCt.getFullWidth();
-<span id='Ext-panel.Table-property-centerClientWidth'>            /**
-</span>             * clientWidth often returns 0 in IE resulting in an
-             * infinity result, here we use offsetWidth bc there are
-             * no possible scrollbars and we don't care about margins
-             */
-            centerClientWidth = viewElDom.offsetWidth;
-            if (me.verticalScroller &amp;&amp; me.verticalScroller.el) {
-                scrollHeight = me.verticalScroller.getSizeCalculation().height;
-            } else {
-                scrollHeight = viewElDom.scrollHeight;
-            }
-
-            clientHeight = viewElDom.clientHeight;
-
-            if (!me.collapsed &amp;&amp; scrollHeight &gt; clientHeight) {
-                me.showVerticalScroller();
-            } else {
-                me.hideVerticalScroller();
-            }
-
-            if (!me.collapsed &amp;&amp; centerScrollWidth &gt; (centerClientWidth + Ext.getScrollBarWidth() - 2)) {
-                me.showHorizontalScroller();
-            } else {
-                me.hideHorizontalScroller();
-            }
-        }
-    },
-
-    onHeaderResize: function() {
-        if (this.view &amp;&amp; this.view.rendered) {
-            this.determineScrollbars();
-            this.invalidateScroller();
-        }
-    },
-
-<span id='Ext-panel.Table-method-hideHorizontalScroller'>    /**
-</span>     * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
+     * Determine the row and cell indexes for each component, taking into consideration
+     * the number of columns and each item's configured colspan/rowspan values.
+     * @param {Array} items The layout components
+     * @return {Array} List of row and cell indexes for each of the components
      */
-    hideHorizontalScroller: function() {
-        var me = this;
-
-        if (me.horizontalScroller &amp;&amp; me.horizontalScroller.ownerCt === me) {
-            me.verticalScroller.offsets.bottom = 0;
-            me.removeDocked(me.horizontalScroller, false);
-            me.removeCls(me.horizontalScrollerPresentCls);
-            me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
-        }
-
-    },
-
-<span id='Ext-panel.Table-method-showHorizontalScroller'>    /**
-</span>     * Show the horizontalScroller and add the horizontalScrollerPresentCls.
-     */
-    showHorizontalScroller: function() {
-        var me = this;
-
-        if (me.verticalScroller) {
-            me.verticalScroller.offsets.bottom = Ext.getScrollBarWidth() - 2;
-        }
-        if (me.horizontalScroller &amp;&amp; me.horizontalScroller.ownerCt !== me) {
-            me.addDocked(me.horizontalScroller);
-            me.addCls(me.horizontalScrollerPresentCls);
-            me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
-        }
-    },
-
-<span id='Ext-panel.Table-method-hideVerticalScroller'>    /**
-</span>     * Hide the verticalScroller and remove the verticalScrollerPresentCls.
-     */
-    hideVerticalScroller: function() {
-        var me = this,
-            headerCt = me.headerCt;
-
-        // only trigger a layout when reserveOffset is changing
-        if (headerCt &amp;&amp; headerCt.layout.reserveOffset) {
-            headerCt.layout.reserveOffset = false;
-            headerCt.doLayout();
-        }
-        if (me.verticalScroller &amp;&amp; me.verticalScroller.ownerCt === me) {
-            me.removeDocked(me.verticalScroller, false);
-            me.removeCls(me.verticalScrollerPresentCls);
-            me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
-        }
-    },
-
-<span id='Ext-panel.Table-method-showVerticalScroller'>    /**
-</span>     * Show the verticalScroller and add the verticalScrollerPresentCls.
-     */
-    showVerticalScroller: function() {
-        var me = this,
-            headerCt = me.headerCt;
-
-        // only trigger a layout when reserveOffset is changing
-        if (headerCt &amp;&amp; !headerCt.layout.reserveOffset) {
-            headerCt.layout.reserveOffset = true;
-            headerCt.doLayout();
-        }
-        if (me.verticalScroller &amp;&amp; me.verticalScroller.ownerCt !== me) {
-            me.addDocked(me.verticalScroller);
-            me.addCls(me.verticalScrollerPresentCls);
-            me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
-        }
-    },
-
-<span id='Ext-panel.Table-method-invalidateScroller'>    /**
-</span>     * Invalides scrollers that are present and forces a recalculation.
-     * (Not related to showing/hiding the scrollers)
-     */
-    invalidateScroller: function() {
-        var me = this,
-            vScroll = me.verticalScroller,
-            hScroll = me.horizontalScroller;
-
-        if (vScroll) {
-            vScroll.invalidate();
-        }
-        if (hScroll) {
-            hScroll.invalidate();
-        }
-    },
-
-    // refresh the view when a header moves
-    onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
-        this.view.refresh();
-    },
-
-    // Section onHeaderHide is invoked after view.
-    onHeaderHide: function(headerCt, header) {
-        this.invalidateScroller();
-    },
-
-    onHeaderShow: function(headerCt, header) {
-        this.invalidateScroller();
-    },
-
-    getVerticalScroller: function() {
-        return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
-    },
-
-    getHorizontalScroller: function() {
-        return this.getScrollerOwner().down('gridscroller[dock=bottom]');
-    },
-
-    onMouseWheel: function(e) {
-        var me = this,
-            browserEvent = e.browserEvent,
-            vertScroller = me.getVerticalScroller(),
-            horizScroller = me.getHorizontalScroller(),
-            scrollDelta = me.scrollDelta,
-            deltaY, deltaX,
-            vertScrollerEl, horizScrollerEl,
-            origScrollLeft, origScrollTop,
-            newScrollLeft, newScrollTop;
-
-        // Track original scroll values, so we can see if we've
-        // reached the end of our scroll height/width.
-        if (horizScroller) {
-            horizScrollerEl = horizScroller.el;
-            if (horizScrollerEl) {
-                origScrollLeft = horizScrollerEl.dom.scrollLeft;
-            }
-        }
-        if (vertScroller) {
-            vertScrollerEl = vertScroller.el;
-            if (vertScrollerEl) {
-                origScrollTop = vertScrollerEl.dom.scrollTop;
-            }
-        }
-
-        // Webkit Horizontal Axis
-        if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {
-            deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
-            deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
-            if (horizScroller) {
-                newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
-            }
-            if (vertScroller) {
-                newScrollTop = vertScroller.scrollByDeltaY(deltaY);
-            }
-        } else {
-            // Gecko Horizontal Axis
-            if (browserEvent.axis &amp;&amp; browserEvent.axis === 1) {
-                if (horizScroller) {
-                    deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
-                    newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
-                }
-            } else {
-                if (vertScroller) {
-
-                    deltaY = -(scrollDelta * e.getWheelDelta() / 3);
-                    newScrollTop = vertScroller.scrollByDeltaY(deltaY);
-                }
-            }
-        }
-
-        // If after given our delta, the scroller has not progressed, then we're
-        // at the end of our scroll range and shouldn't stop the browser event.
-        if ((deltaX !== 0 &amp;&amp; newScrollLeft !== origScrollLeft) ||
-            (deltaY !== 0 &amp;&amp; newScrollTop !== origScrollTop)) {
-            e.stopEvent();
-        }
-    },
-
-<span id='Ext-panel.Table-method-onViewRefresh'>    /**
-</span>     * @private
-     * Determine and invalidate scrollers on view refresh
-     */
-    onViewRefresh: function() {
-        if (Ext.isIE) {
-            this.syncCellHeight();
-        }
-        this.determineScrollbars();
-        if (this.invalidateScrollerOnRefresh) {
-            this.invalidateScroller();
-        }
-    },
-
-    onViewItemUpdate: function(record, index, tr) {
-        if (Ext.isIE) {
-            this.syncCellHeight([tr]);
-        }
-    },
-
-    // BrowserBug: IE will not stretch the td to fit the height of the entire
-    // tr, so manually sync cellheights on refresh and when an item has been
-    // updated.
-    syncCellHeight: function(trs) {
-        var me    = this,
-            i     = 0,
-            tds,
-            j, tdsLn,
-            tr, td,
-            trsLn,
-            rowHeights = [],
-            cellHeights,
-            cellClsSelector = ('.' + Ext.baseCSSPrefix + 'grid-cell');
-
-        trs   = trs || me.view.getNodes();
-        
-        trsLn = trs.length;
-        // Reading loop
-        for (; i &lt; trsLn; i++) {
-            tr = trs[i];
-            tds = Ext.fly(tr).query(cellClsSelector);
-            tdsLn = tds.length;
-            cellHeights = [];
-            for (j = 0; j &lt; tdsLn; j++) {
-                td = tds[j];
-                cellHeights.push(td.clientHeight);
-            }
-            rowHeights.push(Ext.Array.max(cellHeights));
-        }
+    calculateCells: function(items) {
+        var cells = [],
+            rowIdx = 0,
+            colIdx = 0,
+            cellIdx = 0,
+            totalCols = this.columns || Infinity,
+            rowspans = [], //rolling list of active rowspans for each column
+            i = 0, j,
+            len = items.length,
+            item;
 
-        // Setting loop
-        for (i = 0; i &lt; trsLn; i++) {
-            tr = trs[i];
-            tdsLn = tr.childNodes.length;
-            for (j = 0; j &lt; tdsLn; j++) {
-                td = Ext.fly(tr.childNodes[j]);
-                if (rowHeights[i]) {
-                    if (td.is(cellClsSelector)) {
-                        td.setHeight(rowHeights[i]);
-                    } else {
-                        td.down(cellClsSelector).setHeight(rowHeights[i]);
+        for (; i &lt; len; i++) {
+            item = items[i];
+
+            // Find the first available row/col slot not taken up by a spanning cell
+            while (colIdx &gt;= totalCols || rowspans[colIdx] &gt; 0) {
+                if (colIdx &gt;= totalCols) {
+                    // move down to next row
+                    colIdx = 0;
+                    cellIdx = 0;
+                    rowIdx++;
+
+                    // decrement all rowspans
+                    for (j = 0; j &lt; totalCols; j++) {
+                        if (rowspans[j] &gt; 0) {
+                            rowspans[j]--;
+                        }
                     }
+                } else {
+                    colIdx++;
                 }
-                
             }
-        }
-    },
-
-<span id='Ext-panel.Table-method-setScrollTop'>    /**
-</span>     * Sets the scrollTop of the TablePanel.
-     * @param {Number} deltaY
-     */
-    setScrollTop: function(top) {
-        var me               = this,
-            rootCmp          = me.getScrollerOwner(),
-            verticalScroller = me.getVerticalScroller();
 
-        rootCmp.virtualScrollTop = top;
-        if (verticalScroller) {
-            verticalScroller.setScrollTop(top);
-        }
-
-    },
-
-    getScrollerOwner: function() {
-        var rootCmp = this;
-        if (!this.scrollerOwner) {
-            rootCmp = this.up('[scrollerOwner]');
-        }
-        return rootCmp;
-    },
+            // Add the cell info to the list
+            cells.push({
+                rowIdx: rowIdx,
+                cellIdx: cellIdx
+            });
 
-<span id='Ext-panel.Table-method-scrollByDeltaY'>    /**
-</span>     * Scrolls the TablePanel by deltaY
-     * @param {Number} deltaY
-     */
-    scrollByDeltaY: function(deltaY) {
-        var rootCmp = this.getScrollerOwner(),
-            scrollerRight;
-        scrollerRight = rootCmp.down('gridscroller[dock=' + this.verticalScrollDock + ']');
-        if (scrollerRight) {
-            scrollerRight.scrollByDeltaY(deltaY);
+            // Increment
+            for (j = item.colspan || 1; j; --j) {
+                rowspans[colIdx] = item.rowspan || 1;
+                ++colIdx;
+            }
+            ++cellIdx;
         }
-    },
-
-
-<span id='Ext-panel.Table-method-scrollByDeltaX'>    /**
-</span>     * Scrolls the TablePanel by deltaX
-     * @param {Number} deltaY
-     */
-    scrollByDeltaX: function(deltaX) {
-        this.horizontalScroller.scrollByDeltaX(deltaX);
-    },
-
-<span id='Ext-panel.Table-method-getLhsMarker'>    /**
-</span>     * Get left hand side marker for header resizing.
-     * @private
-     */
-    getLhsMarker: function() {
-        var me = this;
 
-        if (!me.lhsMarker) {
-            me.lhsMarker = Ext.core.DomHelper.append(me.el, {
-                cls: Ext.baseCSSPrefix + 'grid-resize-marker'
-            }, true);
-        }
-        return me.lhsMarker;
+        return cells;
     },
 
-<span id='Ext-panel.Table-method-getRhsMarker'>    /**
-</span>     * Get right hand side marker for header resizing.
-     * @private
+<span id='Ext-layout-container-Table-method-getTable'>    /**
+</span>     * @private
+     * Return the layout's table element, creating it if necessary.
      */
-    getRhsMarker: function() {
-        var me = this;
-
-        if (!me.rhsMarker) {
-            me.rhsMarker = Ext.core.DomHelper.append(me.el, {
-                cls: Ext.baseCSSPrefix + 'grid-resize-marker'
-            }, true);
-        }
-        return me.rhsMarker;
-    },
-
-<span id='Ext-panel.Table-method-getSelectionModel'>    /**
-</span>     * Returns the selection model being used and creates it via the configuration
-     * if it has not been created already.
-     * @return {Ext.selection.Model} selModel
+    getTable: function() {
+        var table = this.table;
+        if (!table) {
+            table = this.table = this.getTarget().createChild(
+                Ext.apply({
+                    tag: 'table',
+                    role: 'presentation',
+                    cls: this.tableCls,
+                    cellspacing: 0, //TODO should this be specified or should CSS handle it?
+                    cn: {tag: 'tbody'}
+                }, this.tableAttrs),
+                null, true
+            );
+        }
+        return table;
+    },
+
+<span id='Ext-layout-container-Table-method-needsDivWrap'>    /**
+</span>     * @private
+     * Opera 10.5 has a bug where if a table cell's child has box-sizing:border-box and padding, it
+     * will include that padding in the size of the cell, making it always larger than the
+     * shrink-wrapped size of its contents. To get around this we have to wrap the contents in a div
+     * and then set that div's width to match the item rendered within it afterLayout. This method
+     * determines whether we need the wrapper div; it currently does a straight UA sniff as this bug
+     * seems isolated to just Opera 10.5, but feature detection could be added here if needed.
      */
-    getSelectionModel: function(){
-        if (!this.selModel) {
-            this.selModel = {};
-        }
-
-        var mode = 'SINGLE',
-            type;
-        if (this.simpleSelect) {
-            mode = 'SIMPLE';
-        } else if (this.multiSelect) {
-            mode = 'MULTI';
-        }
-
-        Ext.applyIf(this.selModel, {
-            allowDeselect: this.allowDeselect,
-            mode: mode
-        });
-
-        if (!this.selModel.events) {
-            type = this.selModel.selType || this.selType;
-            this.selModel = Ext.create('selection.' + type, this.selModel);
-        }
-
-        if (!this.selModel.hasRelaySetup) {
-            this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
-            this.selModel.hasRelaySetup = true;
-        }
-
-        // lock the selection model if user
-        // has disabled selection
-        if (this.disableSelection) {
-            this.selModel.locked = true;
-        }
-        return this.selModel;
-    },
-
-    onVerticalScroll: function(event, target) {
-        var owner = this.getScrollerOwner(),
-            items = owner.query('tableview'),
-            i = 0,
-            len = items.length;
-
-        for (; i &lt; len; i++) {
-            items[i].el.dom.scrollTop = target.scrollTop;
-        }
-    },
-
-    onHorizontalScroll: function(event, target) {
-        var owner = this.getScrollerOwner(),
-            items = owner.query('tableview'),
-            i = 0,
-            len = items.length,
-            center,
-            centerEl,
-            centerScrollWidth,
-            centerClientWidth,
-            width;
-
-        center = items[1] || items[0];
-        centerEl = center.el.dom;
-        centerScrollWidth = centerEl.scrollWidth;
-        centerClientWidth = centerEl.offsetWidth;
-        width = this.horizontalScroller.getWidth();
-
-        centerEl.scrollLeft = target.scrollLeft;
-        this.headerCt.el.dom.scrollLeft = target.scrollLeft;
-    },
-
-    // template method meant to be overriden
-    onStoreLoad: Ext.emptyFn,
-
-    getEditorParent: function() {
-        return this.body;
-    },
-
-    bindStore: function(store) {
-        var me = this;
-        me.store = store;
-        me.getView().bindStore(store);
-    },
-
-    reconfigure: function(store, columns) {
-        var me = this;
-
-        if (me.lockable) {
-            me.reconfigureLockable(store, columns);
-            return;
-        }
-
-        if (columns) {
-            me.headerCt.removeAll();
-            me.headerCt.add(columns);
-        }
-        if (store) {
-            store = Ext.StoreManager.lookup(store);
-            me.bindStore(store);
-        } else {
-            me.getView().refresh();
-        }
-    },
-
-    afterComponentLayout: function() {
-        this.callParent(arguments);
-        this.determineScrollbars();
-        this.invalidateScroller();
+    needsDivWrap: function() {
+        return Ext.isOpera10_5;
     }
-});</pre></pre></body></html>
\ No newline at end of file
+});</pre>
+</body>
+</html>