GridView.js
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:61k
源码类别:

JavaScript

开发平台:

JavaScript

  1. /*!
  2.  * Ext JS Library 3.1.0
  3.  * Copyright(c) 2006-2009 Ext JS, LLC
  4.  * licensing@extjs.com
  5.  * http://www.extjs.com/license
  6.  */
  7. /**
  8.  * @class Ext.grid.GridView
  9.  * @extends Ext.util.Observable
  10.  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
  11.  * Methods of this class may be used to access user interface elements to enable
  12.  * special display effects. Do not change the DOM structure of the user interface.</p>
  13.  * <p>This class does not provide ways to manipulate the underlying data. The data
  14.  * model of a Grid is held in an {@link Ext.data.Store}.</p>
  15.  * @constructor
  16.  * @param {Object} config
  17.  */
  18. Ext.grid.GridView = Ext.extend(Ext.util.Observable, {
  19.     /**
  20.      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
  21.      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
  22.      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
  23.      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
  24.      * (e.g., 'my-class another-class'). Example usage:
  25.     <pre><code>
  26. viewConfig: {
  27.     forceFit: true,
  28.     showPreview: true, // custom property
  29.     enableRowBody: true, // required to create a second, full-width row to show expanded Record data
  30.     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams
  31.         if(this.showPreview){
  32.             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';
  33.             return 'x-grid3-row-expanded';
  34.         }
  35.         return 'x-grid3-row-collapsed';
  36.     }
  37. },     
  38.     </code></pre>
  39.      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.
  40.      * @param {Number} index The row index.
  41.      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
  42.      * customization of various aspects of a grid row.
  43.      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set
  44.      * by this function, and will be used to render a full-width expansion row below each grid row:</p>
  45.      * <ul>
  46.      * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be used as the expansion row's body content (defaults to '').</div></li>
  47.      * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style specification that will be applied to the expansion row's &lt;tr> element. (defaults to '').</div></li>
  48.      * </ul>
  49.      * The following property will be passed in, and may be appended to:
  50.      * <ul>
  51.      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates
  52.      * both the standard grid row, and any expansion row.</div></li>
  53.      * </ul>
  54.      * @param {Store} store The {@link Ext.data.Store} this grid is bound to
  55.      * @method getRowClass
  56.      * @return {String} a CSS class name to add to the row.
  57.      */
  58.     /**
  59.      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
  60.      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
  61.      */
  62.     /**
  63.      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows
  64.      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:
  65.     <pre><code>
  66.     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');
  67.     </code></pre>
  68.      */
  69.     /**
  70.      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>). 
  71.      * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>
  72.      * config to disable the <i>menu</i> for individual columns.  While this config is true the
  73.      * following will be disabled:<div class="mdetail-params"><ul>
  74.      * <li>clicking on header to sort</li>
  75.      * <li>the trigger to reveal the menu.</li>
  76.      * </ul></div>
  77.      */
  78.     /**
  79.      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations
  80.      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.
  81.      * See {@link Ext.grid.GridDragZone} for details.</p>
  82.      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>
  83.      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>
  84.      * <li><i>after</i> the owning GridPanel has been rendered.</li>
  85.      * </ul></div>
  86.      * @property dragZone
  87.      * @type {Ext.grid.GridDragZone}
  88.      */
  89.     /**
  90.      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's
  91.      * first load (defaults to <tt>true</tt>).
  92.      */
  93.     deferEmptyText : true,
  94.     /**
  95.      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar
  96.      * (defaults to <tt>undefined</tt>). If an explicit value isn't specified, this will be automatically
  97.      * calculated.
  98.      */
  99.     scrollOffset : undefined,
  100.     /**
  101.      * @cfg {Boolean} autoFill
  102.      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
  103.      * when the grid is <b>initially rendered</b>.  The 
  104.      * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted
  105.      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually
  106.      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.
  107.      * See <tt>{@link #forceFit}</tt> also.
  108.      */
  109.     autoFill : false,
  110.     /**
  111.      * @cfg {Boolean} forceFit
  112.      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned
  113.      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each
  114.      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are
  115.      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized
  116.      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.
  117.      */
  118.     forceFit : false,
  119.     /**
  120.      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>)
  121.      */
  122.     sortClasses : ['sort-asc', 'sort-desc'],
  123.     /**
  124.      * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>)
  125.      */
  126.     sortAscText : 'Sort Ascending',
  127.     /**
  128.      * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>)
  129.      */
  130.     sortDescText : 'Sort Descending',
  131.     /**
  132.      * @cfg {String} columnsText The text displayed in the 'Columns' menu item (defaults to <tt>'Columns'</tt>)
  133.      */
  134.     columnsText : 'Columns',
  135.     /**
  136.      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>'x-grid3-row-selected'</tt>). An
  137.      * example overriding the default styling:
  138.     <pre><code>
  139.     .x-grid3-row-selected {background-color: yellow;}
  140.     </code></pre>
  141.      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner
  142.      * facets (like text) use something like:
  143.     <pre><code>
  144.     .x-grid3-row-selected .x-grid3-cell-inner {
  145.         color: #FFCC00;
  146.     }
  147.     </code></pre>
  148.      * @type String
  149.      */
  150.     selectedRowClass : 'x-grid3-row-selected',
  151.     // private
  152.     borderWidth : 2,
  153.     tdClass : 'x-grid3-cell',
  154.     hdCls : 'x-grid3-hd',
  155.     markDirty : true,
  156.     /**
  157.      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)
  158.      */
  159.     cellSelectorDepth : 4,
  160.     /**
  161.      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)
  162.      */
  163.     rowSelectorDepth : 10,
  164.     
  165.     /**
  166.      * @cfg {Number} rowBodySelectorDepth The number of levels to search for row bodies in event delegation (defaults to <tt>10</tt>)
  167.      */
  168.     rowBodySelectorDepth : 10,
  169.     /**
  170.      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)
  171.      */
  172.     cellSelector : 'td.x-grid3-cell',
  173.     /**
  174.      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)
  175.      */
  176.     rowSelector : 'div.x-grid3-row',
  177.     
  178.     /**
  179.      * @cfg {String} rowBodySelector The selector used to find row bodies internally (defaults to <tt>'div.x-grid3-row'</tt>)
  180.      */
  181.     rowBodySelector : 'div.x-grid3-row-body',
  182.     
  183.     // private
  184.     firstRowCls: 'x-grid3-row-first',
  185.     lastRowCls: 'x-grid3-row-last',
  186.     rowClsRe: /(?:^|s+)x-grid3-row-(first|last|alt)(?:s+|$)/g,
  187.     
  188.     constructor : function(config){
  189.         Ext.apply(this, config);
  190.     // These events are only used internally by the grid components
  191.     this.addEvents(
  192.         /**
  193.          * @event beforerowremoved
  194.          * Internal UI Event. Fired before a row is removed.
  195.          * @param {Ext.grid.GridView} view
  196.          * @param {Number} rowIndex The index of the row to be removed.
  197.          * @param {Ext.data.Record} record The Record to be removed
  198.          */
  199.         'beforerowremoved',
  200.         /**
  201.          * @event beforerowsinserted
  202.          * Internal UI Event. Fired before rows are inserted.
  203.          * @param {Ext.grid.GridView} view
  204.          * @param {Number} firstRow The index of the first row to be inserted.
  205.          * @param {Number} lastRow The index of the last row to be inserted.
  206.          */
  207.         'beforerowsinserted',
  208.         /**
  209.          * @event beforerefresh
  210.          * Internal UI Event. Fired before the view is refreshed.
  211.          * @param {Ext.grid.GridView} view
  212.          */
  213.         'beforerefresh',
  214.         /**
  215.          * @event rowremoved
  216.          * Internal UI Event. Fired after a row is removed.
  217.          * @param {Ext.grid.GridView} view
  218.          * @param {Number} rowIndex The index of the row that was removed.
  219.          * @param {Ext.data.Record} record The Record that was removed
  220.          */
  221.         'rowremoved',
  222.         /**
  223.          * @event rowsinserted
  224.          * Internal UI Event. Fired after rows are inserted.
  225.          * @param {Ext.grid.GridView} view
  226.          * @param {Number} firstRow The index of the first inserted.
  227.          * @param {Number} lastRow The index of the last row inserted.
  228.          */
  229.         'rowsinserted',
  230.         /**
  231.          * @event rowupdated
  232.          * Internal UI Event. Fired after a row has been updated.
  233.          * @param {Ext.grid.GridView} view
  234.          * @param {Number} firstRow The index of the row updated.
  235.          * @param {Ext.data.record} record The Record backing the row updated.
  236.          */
  237.         'rowupdated',
  238.         /**
  239.          * @event refresh
  240.          * Internal UI Event. Fired after the GridView's body has been refreshed.
  241.          * @param {Ext.grid.GridView} view
  242.          */
  243.         'refresh'
  244.     );
  245.     Ext.grid.GridView.superclass.constructor.call(this);    
  246.     },
  247.     /* -------------------------------- UI Specific ----------------------------- */
  248.     // private
  249.     initTemplates : function(){
  250.         var ts = this.templates || {};
  251.         if(!ts.master){
  252.             ts.master = new Ext.Template(
  253.                     '<div class="x-grid3" hidefocus="true">',
  254.                         '<div class="x-grid3-viewport">',
  255.                             '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
  256.                             '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
  257.                         '</div>',
  258.                         '<div class="x-grid3-resize-marker">&#160;</div>',
  259.                         '<div class="x-grid3-resize-proxy">&#160;</div>',
  260.                     '</div>'
  261.                     );
  262.         }
  263.         if(!ts.header){
  264.             ts.header = new Ext.Template(
  265.                     '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  266.                     '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
  267.                     '</table>'
  268.                     );
  269.         }
  270.         if(!ts.hcell){
  271.             ts.hcell = new Ext.Template(
  272.                     '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
  273.                     '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
  274.                     '</div></td>'
  275.                     );
  276.         }
  277.         if(!ts.body){
  278.             ts.body = new Ext.Template('{rows}');
  279.         }
  280.         if(!ts.row){
  281.             ts.row = new Ext.Template(
  282.                     '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  283.                     '<tbody><tr>{cells}</tr>',
  284.                     (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
  285.                     '</tbody></table></div>'
  286.                     );
  287.         }
  288.         if(!ts.cell){
  289.             ts.cell = new Ext.Template(
  290.                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
  291.                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
  292.                     '</td>'
  293.                     );
  294.         }
  295.         for(var k in ts){
  296.             var t = ts[k];
  297.             if(t && Ext.isFunction(t.compile) && !t.compiled){
  298.                 t.disableFormats = true;
  299.                 t.compile();
  300.             }
  301.         }
  302.         this.templates = ts;
  303.         this.colRe = new RegExp('x-grid3-td-([^\s]+)', '');
  304.     },
  305.     // private
  306.     fly : function(el){
  307.         if(!this._flyweight){
  308.             this._flyweight = new Ext.Element.Flyweight(document.body);
  309.         }
  310.         this._flyweight.dom = el;
  311.         return this._flyweight;
  312.     },
  313.     // private
  314.     getEditorParent : function(){
  315.         return this.scroller.dom;
  316.     },
  317.     // private
  318.     initElements : function(){
  319.         var E = Ext.Element;
  320.         var el = this.grid.getGridEl().dom.firstChild;
  321.         var cs = el.childNodes;
  322.         this.el = new E(el);
  323.         this.mainWrap = new E(cs[0]);
  324.         this.mainHd = new E(this.mainWrap.dom.firstChild);
  325.         if(this.grid.hideHeaders){
  326.             this.mainHd.setDisplayed(false);
  327.         }
  328.         this.innerHd = this.mainHd.dom.firstChild;
  329.         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
  330.         if(this.forceFit){
  331.             this.scroller.setStyle('overflow-x', 'hidden');
  332.         }
  333.         /**
  334.          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.
  335.          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.
  336.          * @type Ext.Element
  337.          * @property mainBody
  338.          */
  339.         this.mainBody = new E(this.scroller.dom.firstChild);
  340.         this.focusEl = new E(this.scroller.dom.childNodes[1]);
  341.         this.focusEl.swallowEvent('click', true);
  342.         this.resizeMarker = new E(cs[1]);
  343.         this.resizeProxy = new E(cs[2]);
  344.     },
  345.     // private
  346.     getRows : function(){
  347.         return this.hasRows() ? this.mainBody.dom.childNodes : [];
  348.     },
  349.     // finder methods, used with delegation
  350.     // private
  351.     findCell : function(el){
  352.         if(!el){
  353.             return false;
  354.         }
  355.         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
  356.     },
  357.     /**
  358.      * <p>Return the index of the grid column which contains the passed HTMLElement.</p>
  359.      * See also {@link #findRowIndex}
  360.      * @param {HTMLElement} el The target element
  361.      * @return {Number} The column index, or <b>false</b> if the target element is not within a row of this GridView.
  362.      */
  363.     findCellIndex : function(el, requiredCls){
  364.         var cell = this.findCell(el);
  365.         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
  366.             return this.getCellIndex(cell);
  367.         }
  368.         return false;
  369.     },
  370.     // private
  371.     getCellIndex : function(el){
  372.         if(el){
  373.             var m = el.className.match(this.colRe);
  374.             if(m && m[1]){
  375.                 return this.cm.getIndexById(m[1]);
  376.             }
  377.         }
  378.         return false;
  379.     },
  380.     // private
  381.     findHeaderCell : function(el){
  382.         var cell = this.findCell(el);
  383.         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
  384.     },
  385.     // private
  386.     findHeaderIndex : function(el){
  387.         return this.findCellIndex(el, this.hdCls);
  388.     },
  389.     /**
  390.      * Return the HtmlElement representing the grid row which contains the passed element.
  391.      * @param {HTMLElement} el The target HTMLElement
  392.      * @return {HTMLElement} The row element, or null if the target element is not within a row of this GridView.
  393.      */
  394.     findRow : function(el){
  395.         if(!el){
  396.             return false;
  397.         }
  398.         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
  399.     },
  400.     /**
  401.      * <p>Return the index of the grid row which contains the passed HTMLElement.</p>
  402.      * See also {@link #findCellIndex}
  403.      * @param {HTMLElement} el The target HTMLElement
  404.      * @return {Number} The row index, or <b>false</b> if the target element is not within a row of this GridView.
  405.      */
  406.     findRowIndex : function(el){
  407.         var r = this.findRow(el);
  408.         return r ? r.rowIndex : false;
  409.     },
  410.     
  411.     /**
  412.      * Return the HtmlElement representing the grid row body which contains the passed element.
  413.      * @param {HTMLElement} el The target HTMLElement
  414.      * @return {HTMLElement} The row body element, or null if the target element is not within a row body of this GridView.
  415.      */
  416.     findRowBody : function(el){
  417.         if(!el){
  418.             return false;
  419.         }
  420.         return this.fly(el).findParent(this.rowBodySelector, this.rowBodySelectorDepth);
  421.     },
  422.     // getter methods for fetching elements dynamically in the grid
  423.     /**
  424.      * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.
  425.      * @param {Number} index The row index
  426.      * @return {HtmlElement} The div element.
  427.      */
  428.     getRow : function(row){
  429.         return this.getRows()[row];
  430.     },
  431.     /**
  432.      * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.
  433.      * @param {Number} row The row index in which to find the cell.
  434.      * @param {Number} col The column index of the cell.
  435.      * @return {HtmlElement} The td at the specified coordinates.
  436.      */
  437.     getCell : function(row, col){
  438.         return this.getRow(row).getElementsByTagName('td')[col];
  439.     },
  440.     /**
  441.      * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.
  442.      * @param {Number} index The column index
  443.      * @return {HtmlElement} The td element.
  444.      */
  445.     getHeaderCell : function(index){
  446.       return this.mainHd.dom.getElementsByTagName('td')[index];
  447.     },
  448.     // manipulating elements
  449.     // private - use getRowClass to apply custom row classes
  450.     addRowClass : function(row, cls){
  451.         var r = this.getRow(row);
  452.         if(r){
  453.             this.fly(r).addClass(cls);
  454.         }
  455.     },
  456.     // private
  457.     removeRowClass : function(row, cls){
  458.         var r = this.getRow(row);
  459.         if(r){
  460.             this.fly(r).removeClass(cls);
  461.         }
  462.     },
  463.     // private
  464.     removeRow : function(row){
  465.         Ext.removeNode(this.getRow(row));
  466.         this.syncFocusEl(row);
  467.     },
  468.     
  469.     // private
  470.     removeRows : function(firstRow, lastRow){
  471.         var bd = this.mainBody.dom;
  472.         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
  473.             Ext.removeNode(bd.childNodes[firstRow]);
  474.         }
  475.         this.syncFocusEl(firstRow);
  476.     },
  477.     // scrolling stuff
  478.     // private
  479.     getScrollState : function(){
  480.         var sb = this.scroller.dom;
  481.         return {left: sb.scrollLeft, top: sb.scrollTop};
  482.     },
  483.     // private
  484.     restoreScroll : function(state){
  485.         var sb = this.scroller.dom;
  486.         sb.scrollLeft = state.left;
  487.         sb.scrollTop = state.top;
  488.     },
  489.     /**
  490.      * Scrolls the grid to the top
  491.      */
  492.     scrollToTop : function(){
  493.         this.scroller.dom.scrollTop = 0;
  494.         this.scroller.dom.scrollLeft = 0;
  495.     },
  496.     // private
  497.     syncScroll : function(){
  498.       this.syncHeaderScroll();
  499.       var mb = this.scroller.dom;
  500.         this.grid.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
  501.     },
  502.     // private
  503.     syncHeaderScroll : function(){
  504.         var mb = this.scroller.dom;
  505.         this.innerHd.scrollLeft = mb.scrollLeft;
  506.         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
  507.     },
  508.     // private
  509.     updateSortIcon : function(col, dir){
  510.         var sc = this.sortClasses;
  511.         var hds = this.mainHd.select('td').removeClass(sc);
  512.         hds.item(col).addClass(sc[dir == 'DESC' ? 1 : 0]);
  513.     },
  514.     // private
  515.     updateAllColumnWidths : function(){
  516.         var tw = this.getTotalWidth(),
  517.             clen = this.cm.getColumnCount(),
  518.             ws = [],
  519.             len,
  520.             i;
  521.         for(i = 0; i < clen; i++){
  522.             ws[i] = this.getColumnWidth(i);
  523.         }
  524.         this.innerHd.firstChild.style.width = this.getOffsetWidth();
  525.         this.innerHd.firstChild.firstChild.style.width = tw;
  526.         this.mainBody.dom.style.width = tw;
  527.         for(i = 0; i < clen; i++){
  528.             var hd = this.getHeaderCell(i);
  529.             hd.style.width = ws[i];
  530.         }
  531.         var ns = this.getRows(), row, trow;
  532.         for(i = 0, len = ns.length; i < len; i++){
  533.             row = ns[i];
  534.             row.style.width = tw;
  535.             if(row.firstChild){
  536.                 row.firstChild.style.width = tw;
  537.                 trow = row.firstChild.rows[0];
  538.                 for (var j = 0; j < clen; j++) {
  539.                    trow.childNodes[j].style.width = ws[j];
  540.                 }
  541.             }
  542.         }
  543.         this.onAllColumnWidthsUpdated(ws, tw);
  544.     },
  545.     // private
  546.     updateColumnWidth : function(col, width){
  547.         var w = this.getColumnWidth(col);
  548.         var tw = this.getTotalWidth();
  549.         this.innerHd.firstChild.style.width = this.getOffsetWidth();
  550.         this.innerHd.firstChild.firstChild.style.width = tw;
  551.         this.mainBody.dom.style.width = tw;
  552.         var hd = this.getHeaderCell(col);
  553.         hd.style.width = w;
  554.         var ns = this.getRows(), row;
  555.         for(var i = 0, len = ns.length; i < len; i++){
  556.             row = ns[i];
  557.             row.style.width = tw;
  558.             if(row.firstChild){
  559.                 row.firstChild.style.width = tw;
  560.                 row.firstChild.rows[0].childNodes[col].style.width = w;
  561.             }
  562.         }
  563.         this.onColumnWidthUpdated(col, w, tw);
  564.     },
  565.     // private
  566.     updateColumnHidden : function(col, hidden){
  567.         var tw = this.getTotalWidth();
  568.         this.innerHd.firstChild.style.width = this.getOffsetWidth();
  569.         this.innerHd.firstChild.firstChild.style.width = tw;
  570.         this.mainBody.dom.style.width = tw;
  571.         var display = hidden ? 'none' : '';
  572.         var hd = this.getHeaderCell(col);
  573.         hd.style.display = display;
  574.         var ns = this.getRows(), row;
  575.         for(var i = 0, len = ns.length; i < len; i++){
  576.             row = ns[i];
  577.             row.style.width = tw;
  578.             if(row.firstChild){
  579.                 row.firstChild.style.width = tw;
  580.                 row.firstChild.rows[0].childNodes[col].style.display = display;
  581.             }
  582.         }
  583.         this.onColumnHiddenUpdated(col, hidden, tw);
  584.         delete this.lastViewWidth; // force recalc
  585.         this.layout();
  586.     },
  587.     // private
  588.     doRender : function(cs, rs, ds, startRow, colCount, stripe){
  589.         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
  590.         var tstyle = 'width:'+this.getTotalWidth()+';';
  591.         // buffers
  592.         var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
  593.         for(var j = 0, len = rs.length; j < len; j++){
  594.             r = rs[j]; cb = [];
  595.             var rowIndex = (j+startRow);
  596.             for(var i = 0; i < colCount; i++){
  597.                 c = cs[i];
  598.                 p.id = c.id;
  599.                 p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  600.                 p.attr = p.cellAttr = '';
  601.                 p.value = c.renderer.call(c.scope, r.data[c.name], p, r, rowIndex, i, ds);
  602.                 p.style = c.style;
  603.                 if(Ext.isEmpty(p.value)){
  604.                     p.value = '&#160;';
  605.                 }
  606.                 if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
  607.                     p.css += ' x-grid3-dirty-cell';
  608.                 }
  609.                 cb[cb.length] = ct.apply(p);
  610.             }
  611.             var alt = [];
  612.             if(stripe && ((rowIndex+1) % 2 === 0)){
  613.                 alt[0] = 'x-grid3-row-alt';
  614.             }
  615.             if(r.dirty){
  616.                 alt[1] = ' x-grid3-dirty-row';
  617.             }
  618.             rp.cols = colCount;
  619.             if(this.getRowClass){
  620.                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
  621.             }
  622.             rp.alt = alt.join(' ');
  623.             rp.cells = cb.join('');
  624.             buf[buf.length] =  rt.apply(rp);
  625.         }
  626.         return buf.join('');
  627.     },
  628.     // private
  629.     processRows : function(startRow, skipStripe){
  630.         if(!this.ds || this.ds.getCount() < 1){
  631.             return;
  632.         }
  633.         var rows = this.getRows(),
  634.             len = rows.length,
  635.             i, r;
  636.             
  637.         skipStripe = skipStripe || !this.grid.stripeRows;
  638.         startRow = startRow || 0;
  639.         for(i = 0; i<len; i++) {
  640.             r = rows[i];
  641.             if(r) {
  642.                 r.rowIndex = i;
  643.                 if(!skipStripe){
  644.                     r.className = r.className.replace(this.rowClsRe, ' ');
  645.                     if ((i + 1) % 2 === 0){
  646.                         r.className += ' x-grid3-row-alt';
  647.                     }
  648.                 }   
  649.             }          
  650.         }
  651.         // add first/last-row classes
  652.         if(startRow === 0){
  653.             Ext.fly(rows[0]).addClass(this.firstRowCls);
  654.         }
  655.         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
  656.     },
  657.     afterRender : function(){
  658.         if(!this.ds || !this.cm){
  659.             return;
  660.         }
  661.         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';
  662.         this.processRows(0, true);
  663.         if(this.deferEmptyText !== true){
  664.             this.applyEmptyText();
  665.         }
  666.         this.grid.fireEvent('viewready', this.grid);
  667.     },
  668.     // private
  669.     renderUI : function(){
  670.         var header = this.renderHeaders();
  671.         var body = this.templates.body.apply({rows:'&#160;'});
  672.         var html = this.templates.master.apply({
  673.             body: body,
  674.             header: header,
  675.             ostyle: 'width:'+this.getOffsetWidth()+';',
  676.             bstyle: 'width:'+this.getTotalWidth()+';'
  677.         });
  678.         var g = this.grid;
  679.         g.getGridEl().dom.innerHTML = html;
  680.         this.initElements();
  681.         // get mousedowns early
  682.         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
  683.         this.mainHd.on({
  684.             scope: this,
  685.             mouseover: this.handleHdOver,
  686.             mouseout: this.handleHdOut,
  687.             mousemove: this.handleHdMove
  688.         });
  689.         this.scroller.on('scroll', this.syncScroll,  this);
  690.         if(g.enableColumnResize !== false){
  691.             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
  692.         }
  693.         if(g.enableColumnMove){
  694.             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
  695.             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
  696.         }
  697.         if(g.enableHdMenu !== false){
  698.             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
  699.             this.hmenu.add(
  700.                 {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
  701.                 {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
  702.             );
  703.             if(g.enableColumnHide !== false){
  704.                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
  705.                 this.colMenu.on({
  706.                     scope: this,
  707.                     beforeshow: this.beforeColMenuShow,
  708.                     itemclick: this.handleHdMenuClick
  709.                 });
  710.                 this.hmenu.add('-', {
  711.                     itemId:'columns',
  712.                     hideOnClick: false,
  713.                     text: this.columnsText,
  714.                     menu: this.colMenu,
  715.                     iconCls: 'x-cols-icon'
  716.                 });
  717.             }
  718.             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
  719.         }
  720.         if(g.trackMouseOver){
  721.             this.mainBody.on({
  722.                 scope: this,
  723.                 mouseover: this.onRowOver,
  724.                 mouseout: this.onRowOut
  725.             });
  726.         }
  727.         if(g.enableDragDrop || g.enableDrag){
  728.             this.dragZone = new Ext.grid.GridDragZone(g, {
  729.                 ddGroup : g.ddGroup || 'GridDD'
  730.             });
  731.         }
  732.         this.updateHeaderSortState();
  733.     },
  734.     
  735.     // private
  736.     processEvent: Ext.emptyFn,
  737.     // private
  738.     layout : function(){
  739.         if(!this.mainBody){
  740.             return; // not rendered
  741.         }
  742.         var g = this.grid;
  743.         var c = g.getGridEl();
  744.         var csize = c.getSize(true);
  745.         var vw = csize.width;
  746.         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?
  747.             return;
  748.         }
  749.         
  750.         if(g.autoHeight){
  751.             this.scroller.dom.style.overflow = 'visible';
  752.             if(Ext.isWebKit){
  753.                 this.scroller.dom.style.position = 'static';
  754.             }
  755.         }else{
  756.             this.el.setSize(csize.width, csize.height);
  757.             var hdHeight = this.mainHd.getHeight();
  758.             var vh = csize.height - (hdHeight);
  759.             this.scroller.setSize(vw, vh);
  760.             if(this.innerHd){
  761.                 this.innerHd.style.width = (vw)+'px';
  762.             }
  763.         }
  764.         if(this.forceFit){
  765.             if(this.lastViewWidth != vw){
  766.                 this.fitColumns(false, false);
  767.                 this.lastViewWidth = vw;
  768.             }
  769.         }else {
  770.             this.autoExpand();
  771.             this.syncHeaderScroll();
  772.         }
  773.         this.onLayout(vw, vh);
  774.     },
  775.     // template functions for subclasses and plugins
  776.     // these functions include precalculated values
  777.     onLayout : function(vw, vh){
  778.         // do nothing
  779.     },
  780.     onColumnWidthUpdated : function(col, w, tw){
  781.         //template method
  782.     },
  783.     onAllColumnWidthsUpdated : function(ws, tw){
  784.         //template method
  785.     },
  786.     onColumnHiddenUpdated : function(col, hidden, tw){
  787.         // template method
  788.     },
  789.     updateColumnText : function(col, text){
  790.         // template method
  791.     },
  792.     afterMove : function(colIndex){
  793.         // template method
  794.     },
  795.     /* ----------------------------------- Core Specific -------------------------------------------*/
  796.     // private
  797.     init : function(grid){
  798.         this.grid = grid;
  799.         this.initTemplates();
  800.         this.initData(grid.store, grid.colModel);
  801.         this.initUI(grid);
  802.     },
  803.     // private
  804.     getColumnId : function(index){
  805.       return this.cm.getColumnId(index);
  806.     },
  807.     
  808.     // private 
  809.     getOffsetWidth : function() {
  810.         return (this.cm.getTotalWidth() + this.getScrollOffset()) + 'px';
  811.     },
  812.     
  813.     getScrollOffset: function(){
  814.         return Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
  815.     },
  816.     // private
  817.     renderHeaders : function(){
  818.         var cm = this.cm, 
  819.             ts = this.templates,
  820.             ct = ts.hcell,
  821.             cb = [], 
  822.             p = {},
  823.             len = cm.getColumnCount(),
  824.             last = len - 1;
  825.             
  826.         for(var i = 0; i < len; i++){
  827.             p.id = cm.getColumnId(i);
  828.             p.value = cm.getColumnHeader(i) || '';
  829.             p.style = this.getColumnStyle(i, true);
  830.             p.tooltip = this.getColumnTooltip(i);
  831.             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  832.             if(cm.config[i].align == 'right'){
  833.                 p.istyle = 'padding-right:16px';
  834.             } else {
  835.                 delete p.istyle;
  836.             }
  837.             cb[cb.length] = ct.apply(p);
  838.         }
  839.         return ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'});
  840.     },
  841.     // private
  842.     getColumnTooltip : function(i){
  843.         var tt = this.cm.getColumnTooltip(i);
  844.         if(tt){
  845.             if(Ext.QuickTips.isEnabled()){
  846.                 return 'ext:qtip="'+tt+'"';
  847.             }else{
  848.                 return 'title="'+tt+'"';
  849.             }
  850.         }
  851.         return '';
  852.     },
  853.     // private
  854.     beforeUpdate : function(){
  855.         this.grid.stopEditing(true);
  856.     },
  857.     // private
  858.     updateHeaders : function(){
  859.         this.innerHd.firstChild.innerHTML = this.renderHeaders();
  860.         this.innerHd.firstChild.style.width = this.getOffsetWidth();
  861.         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
  862.     },
  863.     /**
  864.      * Focuses the specified row.
  865.      * @param {Number} row The row index
  866.      */
  867.     focusRow : function(row){
  868.         this.focusCell(row, 0, false);
  869.     },
  870.     /**
  871.      * Focuses the specified cell.
  872.      * @param {Number} row The row index
  873.      * @param {Number} col The column index
  874.      */
  875.     focusCell : function(row, col, hscroll){
  876.         this.syncFocusEl(this.ensureVisible(row, col, hscroll));
  877.         if(Ext.isGecko){
  878.             this.focusEl.focus();
  879.         }else{
  880.             this.focusEl.focus.defer(1, this.focusEl);
  881.         }
  882.     },
  883.     resolveCell : function(row, col, hscroll){
  884.         if(!Ext.isNumber(row)){
  885.             row = row.rowIndex;
  886.         }
  887.         if(!this.ds){
  888.             return null;
  889.         }
  890.         if(row < 0 || row >= this.ds.getCount()){
  891.             return null;
  892.         }
  893.         col = (col !== undefined ? col : 0);
  894.         var rowEl = this.getRow(row),
  895.             cm = this.cm,
  896.             colCount = cm.getColumnCount(),
  897.             cellEl;
  898.         if(!(hscroll === false && col === 0)){
  899.             while(col < colCount && cm.isHidden(col)){
  900.                 col++;
  901.             }
  902.             cellEl = this.getCell(row, col);
  903.         }
  904.         return {row: rowEl, cell: cellEl};
  905.     },
  906.     getResolvedXY : function(resolved){
  907.         if(!resolved){
  908.             return null;
  909.         }
  910.         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
  911.         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
  912.     },
  913.     syncFocusEl : function(row, col, hscroll){
  914.         var xy = row;
  915.         if(!Ext.isArray(xy)){
  916.             row = Math.min(row, Math.max(0, this.getRows().length-1));
  917.             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
  918.         }
  919.         this.focusEl.setXY(xy||this.scroller.getXY());
  920.     },
  921.     ensureVisible : function(row, col, hscroll){
  922.         var resolved = this.resolveCell(row, col, hscroll);
  923.         if(!resolved || !resolved.row){
  924.             return;
  925.         }
  926.         var rowEl = resolved.row, 
  927.             cellEl = resolved.cell,
  928.             c = this.scroller.dom,
  929.             ctop = 0,
  930.             p = rowEl, 
  931.             stop = this.el.dom;
  932.             
  933.         while(p && p != stop){
  934.             ctop += p.offsetTop;
  935.             p = p.offsetParent;
  936.         }
  937.         
  938.         ctop -= this.mainHd.dom.offsetHeight;
  939.         stop = parseInt(c.scrollTop, 10);
  940.         
  941.         var cbot = ctop + rowEl.offsetHeight,
  942.             ch = c.clientHeight,
  943.             sbot = stop + ch;
  944.         
  945.         if(ctop < stop){
  946.           c.scrollTop = ctop;
  947.         }else if(cbot > sbot){
  948.             c.scrollTop = cbot-ch;
  949.         }
  950.         if(hscroll !== false){
  951.             var cleft = parseInt(cellEl.offsetLeft, 10);
  952.             var cright = cleft + cellEl.offsetWidth;
  953.             var sleft = parseInt(c.scrollLeft, 10);
  954.             var sright = sleft + c.clientWidth;
  955.             if(cleft < sleft){
  956.                 c.scrollLeft = cleft;
  957.             }else if(cright > sright){
  958.                 c.scrollLeft = cright-c.clientWidth;
  959.             }
  960.         }
  961.         return this.getResolvedXY(resolved);
  962.     },
  963.     // private
  964.     insertRows : function(dm, firstRow, lastRow, isUpdate){
  965.         var last = dm.getCount() - 1;
  966.         if(!isUpdate && firstRow === 0 && lastRow >= last){
  967.     this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
  968.             this.refresh();
  969.     this.fireEvent('rowsinserted', this, firstRow, lastRow);
  970.         }else{
  971.             if(!isUpdate){
  972.                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
  973.             }
  974.             var html = this.renderRows(firstRow, lastRow),
  975.                 before = this.getRow(firstRow);
  976.             if(before){
  977.                 if(firstRow === 0){
  978.                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);
  979.                 }
  980.                 Ext.DomHelper.insertHtml('beforeBegin', before, html);
  981.             }else{
  982.                 var r = this.getRow(last - 1);
  983.                 if(r){
  984.                     Ext.fly(r).removeClass(this.lastRowCls);
  985.                 }
  986.                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
  987.             }
  988.             if(!isUpdate){
  989.                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
  990.                 this.processRows(firstRow);
  991.             }else if(firstRow === 0 || firstRow >= last){
  992.                 //ensure first/last row is kept after an update.
  993.                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);
  994.             }
  995.         }
  996.         this.syncFocusEl(firstRow);
  997.     },
  998.     // private
  999.     deleteRows : function(dm, firstRow, lastRow){
  1000.         if(dm.getRowCount()<1){
  1001.             this.refresh();
  1002.         }else{
  1003.             this.fireEvent('beforerowsdeleted', this, firstRow, lastRow);
  1004.             this.removeRows(firstRow, lastRow);
  1005.             this.processRows(firstRow);
  1006.             this.fireEvent('rowsdeleted', this, firstRow, lastRow);
  1007.         }
  1008.     },
  1009.     // private
  1010.     getColumnStyle : function(col, isHeader){
  1011.         var style = !isHeader ? (this.cm.config[col].css || '') : '';
  1012.         style += 'width:'+this.getColumnWidth(col)+';';
  1013.         if(this.cm.isHidden(col)){
  1014.             style += 'display:none;';
  1015.         }
  1016.         var align = this.cm.config[col].align;
  1017.         if(align){
  1018.             style += 'text-align:'+align+';';
  1019.         }
  1020.         return style;
  1021.     },
  1022.     // private
  1023.     getColumnWidth : function(col){
  1024.         var w = this.cm.getColumnWidth(col);
  1025.         if(Ext.isNumber(w)){
  1026.             return (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? w : (w - this.borderWidth > 0 ? w - this.borderWidth : 0)) + 'px';
  1027.         }
  1028.         return w;
  1029.     },
  1030.     // private
  1031.     getTotalWidth : function(){
  1032.         return this.cm.getTotalWidth()+'px';
  1033.     },
  1034.     // private
  1035.     fitColumns : function(preventRefresh, onlyExpand, omitColumn){
  1036.         var cm = this.cm, i;
  1037.         var tw = cm.getTotalWidth(false);
  1038.         var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
  1039.         if(aw < 20){ // not initialized, so don't screw up the default widths
  1040.             return;
  1041.         }
  1042.         var extra = aw - tw;
  1043.         if(extra === 0){
  1044.             return false;
  1045.         }
  1046.         var vc = cm.getColumnCount(true);
  1047.         var ac = vc-(Ext.isNumber(omitColumn) ? 1 : 0);
  1048.         if(ac === 0){
  1049.             ac = 1;
  1050.             omitColumn = undefined;
  1051.         }
  1052.         var colCount = cm.getColumnCount();
  1053.         var cols = [];
  1054.         var extraCol = 0;
  1055.         var width = 0;
  1056.         var w;
  1057.         for (i = 0; i < colCount; i++){
  1058.             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
  1059.                 w = cm.getColumnWidth(i);
  1060.                 cols.push(i);
  1061.                 extraCol = i;
  1062.                 cols.push(w);
  1063.                 width += w;
  1064.             }
  1065.         }
  1066.         var frac = (aw - cm.getTotalWidth())/width;
  1067.         while (cols.length){
  1068.             w = cols.pop();
  1069.             i = cols.pop();
  1070.             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
  1071.         }
  1072.         if((tw = cm.getTotalWidth(false)) > aw){
  1073.             var adjustCol = ac != vc ? omitColumn : extraCol;
  1074.              cm.setColumnWidth(adjustCol, Math.max(1,
  1075.                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);
  1076.         }
  1077.         if(preventRefresh !== true){
  1078.             this.updateAllColumnWidths();
  1079.         }
  1080.         return true;
  1081.     },
  1082.     // private
  1083.     autoExpand : function(preventUpdate){
  1084.         var g = this.grid, cm = this.cm;
  1085.         if(!this.userResized && g.autoExpandColumn){
  1086.             var tw = cm.getTotalWidth(false);
  1087.             var aw = this.grid.getGridEl().getWidth(true)-this.getScrollOffset();
  1088.             if(tw != aw){
  1089.                 var ci = cm.getIndexById(g.autoExpandColumn);
  1090.                 var currentWidth = cm.getColumnWidth(ci);
  1091.                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
  1092.                 if(cw != currentWidth){
  1093.                     cm.setColumnWidth(ci, cw, true);
  1094.                     if(preventUpdate !== true){
  1095.                         this.updateColumnWidth(ci, cw);
  1096.                     }
  1097.                 }
  1098.             }
  1099.         }
  1100.     },
  1101.     // private
  1102.     getColumnData : function(){
  1103.         // build a map for all the columns
  1104.         var cs = [], cm = this.cm, colCount = cm.getColumnCount();
  1105.         for(var i = 0; i < colCount; i++){
  1106.             var name = cm.getDataIndex(i);
  1107.             cs[i] = {
  1108.                 name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
  1109.                 renderer : cm.getRenderer(i),
  1110.                 scope: cm.getRendererScope(i),
  1111.                 id : cm.getColumnId(i),
  1112.                 style : this.getColumnStyle(i)
  1113.             };
  1114.         }
  1115.         return cs;
  1116.     },
  1117.     // private
  1118.     renderRows : function(startRow, endRow){
  1119.         // pull in all the crap needed to render rows
  1120.         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
  1121.         var colCount = cm.getColumnCount();
  1122.         if(ds.getCount() < 1){
  1123.             return '';
  1124.         }
  1125.         var cs = this.getColumnData();
  1126.         startRow = startRow || 0;
  1127.         endRow = !Ext.isDefined(endRow) ? ds.getCount()-1 : endRow;
  1128.         // records to render
  1129.         var rs = ds.getRange(startRow, endRow);
  1130.         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
  1131.     },
  1132.     // private
  1133.     renderBody : function(){
  1134.         var markup = this.renderRows() || '&#160;';
  1135.         return this.templates.body.apply({rows: markup});
  1136.     },
  1137.     // private
  1138.     refreshRow : function(record){
  1139.         var ds = this.ds, index;
  1140.         if(Ext.isNumber(record)){
  1141.             index = record;
  1142.             record = ds.getAt(index);
  1143.             if(!record){
  1144.                 return;
  1145.             }
  1146.         }else{
  1147.             index = ds.indexOf(record);
  1148.             if(index < 0){
  1149.                 return;
  1150.             }
  1151.         }
  1152.         this.insertRows(ds, index, index, true);
  1153.         this.getRow(index).rowIndex = index;
  1154.         this.onRemove(ds, record, index+1, true);
  1155.         this.fireEvent('rowupdated', this, index, record);
  1156.     },
  1157.     /**
  1158.      * Refreshs the grid UI
  1159.      * @param {Boolean} headersToo (optional) True to also refresh the headers
  1160.      */
  1161.     refresh : function(headersToo){
  1162.         this.fireEvent('beforerefresh', this);
  1163.         this.grid.stopEditing(true);
  1164.         var result = this.renderBody();
  1165.         this.mainBody.update(result).setWidth(this.getTotalWidth());
  1166.         if(headersToo === true){
  1167.             this.updateHeaders();
  1168.             this.updateHeaderSortState();
  1169.         }
  1170.         this.processRows(0, true);
  1171.         this.layout();
  1172.         this.applyEmptyText();
  1173.         this.fireEvent('refresh', this);
  1174.     },
  1175.     // private
  1176.     applyEmptyText : function(){
  1177.         if(this.emptyText && !this.hasRows()){
  1178.             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
  1179.         }
  1180.     },
  1181.     // private
  1182.     updateHeaderSortState : function(){
  1183.         var state = this.ds.getSortState();
  1184.         if(!state){
  1185.             return;
  1186.         }
  1187.         if(!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)){
  1188.             this.grid.fireEvent('sortchange', this.grid, state);
  1189.         }
  1190.         this.sortState = state;
  1191.         var sortColumn = this.cm.findColumnIndex(state.field);
  1192.         if(sortColumn != -1){
  1193.             var sortDir = state.direction;
  1194.             this.updateSortIcon(sortColumn, sortDir);
  1195.         }
  1196.     },
  1197.     // private
  1198.     clearHeaderSortState : function(){
  1199.         if(!this.sortState){
  1200.             return;
  1201.         }
  1202.         this.grid.fireEvent('sortchange', this.grid, null);
  1203.         this.mainHd.select('td').removeClass(this.sortClasses);
  1204.         delete this.sortState;
  1205.     },
  1206.     // private
  1207.     destroy : function(){
  1208.         if(this.colMenu){
  1209.             Ext.menu.MenuMgr.unregister(this.colMenu);
  1210.             this.colMenu.destroy();
  1211.             delete this.colMenu;
  1212.         }
  1213.         if(this.hmenu){
  1214.             Ext.menu.MenuMgr.unregister(this.hmenu);
  1215.             this.hmenu.destroy();
  1216.             delete this.hmenu;
  1217.         }
  1218.         this.initData(null, null);
  1219.         this.purgeListeners();
  1220.         Ext.fly(this.innerHd).un("click", this.handleHdDown, this);
  1221.         if(this.grid.enableColumnMove){
  1222.             Ext.destroy(
  1223.                 this.columnDrag.el,
  1224.                 this.columnDrag.proxy.ghost,
  1225.                 this.columnDrag.proxy.el,
  1226.                 this.columnDrop.el,
  1227.                 this.columnDrop.proxyTop,
  1228.                 this.columnDrop.proxyBottom,
  1229.                 this.columnDrag.dragData.ddel,
  1230.                 this.columnDrag.dragData.header
  1231.             );
  1232.             if (this.columnDrag.proxy.anim) {
  1233.                 Ext.destroy(this.columnDrag.proxy.anim);
  1234.             }
  1235.             delete this.columnDrag.proxy.ghost;
  1236.             delete this.columnDrag.dragData.ddel;
  1237.             delete this.columnDrag.dragData.header;
  1238.             this.columnDrag.destroy();
  1239.             delete Ext.dd.DDM.locationCache[this.columnDrag.id];
  1240.             delete this.columnDrag._domRef;
  1241.             delete this.columnDrop.proxyTop;
  1242.             delete this.columnDrop.proxyBottom;
  1243.             this.columnDrop.destroy();
  1244.             delete Ext.dd.DDM.locationCache["gridHeader" + this.grid.getGridEl().id];
  1245.             delete this.columnDrop._domRef;
  1246.             delete Ext.dd.DDM.ids[this.columnDrop.ddGroup];
  1247.         }
  1248.         if (this.splitZone){ // enableColumnResize
  1249.             this.splitZone.destroy();
  1250.             delete this.splitZone._domRef;
  1251.             delete Ext.dd.DDM.ids["gridSplitters" + this.grid.getGridEl().id];
  1252.         }
  1253.         Ext.fly(this.innerHd).removeAllListeners();
  1254.         Ext.removeNode(this.innerHd);
  1255.         delete this.innerHd;
  1256.         Ext.destroy(
  1257.             this.el,
  1258.             this.mainWrap,
  1259.             this.mainHd,
  1260.             this.scroller,
  1261.             this.mainBody,
  1262.             this.focusEl,
  1263.             this.resizeMarker,
  1264.             this.resizeProxy,
  1265.             this.activeHdBtn,
  1266.             this.dragZone,
  1267.             this.splitZone,
  1268.             this._flyweight
  1269.         );
  1270.         delete this.grid.container;
  1271.         if(this.dragZone){
  1272.             this.dragZone.destroy();
  1273.         }
  1274.         Ext.dd.DDM.currentTarget = null;
  1275.         delete Ext.dd.DDM.locationCache[this.grid.getGridEl().id];
  1276.         Ext.EventManager.removeResizeListener(this.onWindowResize, this);
  1277.     },
  1278.     // private
  1279.     onDenyColumnHide : function(){
  1280.     },
  1281.     // private
  1282.     render : function(){
  1283.         if(this.autoFill){
  1284.             var ct = this.grid.ownerCt;
  1285.             if (ct && ct.getLayout()){
  1286.                 ct.on('afterlayout', function(){ 
  1287.                     this.fitColumns(true, true);
  1288.                     this.updateHeaders(); 
  1289.                 }, this, {single: true}); 
  1290.             }else{ 
  1291.                 this.fitColumns(true, true); 
  1292.             }
  1293.         }else if(this.forceFit){
  1294.             this.fitColumns(true, false);
  1295.         }else if(this.grid.autoExpandColumn){
  1296.             this.autoExpand(true);
  1297.         }
  1298.         this.renderUI();
  1299.     },
  1300.     /* --------------------------------- Model Events and Handlers --------------------------------*/
  1301.     // private
  1302.     initData : function(ds, cm){
  1303.         if(this.ds){
  1304.             this.ds.un('load', this.onLoad, this);
  1305.             this.ds.un('datachanged', this.onDataChange, this);
  1306.             this.ds.un('add', this.onAdd, this);
  1307.             this.ds.un('remove', this.onRemove, this);
  1308.             this.ds.un('update', this.onUpdate, this);
  1309.             this.ds.un('clear', this.onClear, this);
  1310.             if(this.ds !== ds && this.ds.autoDestroy){
  1311.                 this.ds.destroy();
  1312.             }
  1313.         }
  1314.         if(ds){
  1315.             ds.on({
  1316.                 scope: this,
  1317.                 load: this.onLoad,
  1318.                 datachanged: this.onDataChange,
  1319.                 add: this.onAdd,
  1320.                 remove: this.onRemove,
  1321.                 update: this.onUpdate,
  1322.                 clear: this.onClear
  1323.             });
  1324.         }
  1325.         this.ds = ds;
  1326.         if(this.cm){
  1327.             this.cm.un('configchange', this.onColConfigChange, this);
  1328.             this.cm.un('widthchange', this.onColWidthChange, this);
  1329.             this.cm.un('headerchange', this.onHeaderChange, this);
  1330.             this.cm.un('hiddenchange', this.onHiddenChange, this);
  1331.             this.cm.un('columnmoved', this.onColumnMove, this);
  1332.         }
  1333.         if(cm){
  1334.             delete this.lastViewWidth;
  1335.             cm.on({
  1336.                 scope: this,
  1337.                 configchange: this.onColConfigChange,
  1338.                 widthchange: this.onColWidthChange,
  1339.                 headerchange: this.onHeaderChange,
  1340.                 hiddenchange: this.onHiddenChange,
  1341.                 columnmoved: this.onColumnMove
  1342.             });
  1343.         }
  1344.         this.cm = cm;
  1345.     },
  1346.     // private
  1347.     onDataChange : function(){
  1348.         this.refresh();
  1349.         this.updateHeaderSortState();
  1350.         this.syncFocusEl(0);
  1351.     },
  1352.     // private
  1353.     onClear : function(){
  1354.         this.refresh();
  1355.         this.syncFocusEl(0);
  1356.     },
  1357.     // private
  1358.     onUpdate : function(ds, record){
  1359.         this.refreshRow(record);
  1360.     },
  1361.     // private
  1362.     onAdd : function(ds, records, index){
  1363.         this.insertRows(ds, index, index + (records.length-1));
  1364.     },
  1365.     // private
  1366.     onRemove : function(ds, record, index, isUpdate){
  1367.         if(isUpdate !== true){
  1368.             this.fireEvent('beforerowremoved', this, index, record);
  1369.         }
  1370.         this.removeRow(index);
  1371.         if(isUpdate !== true){
  1372.             this.processRows(index);
  1373.             this.applyEmptyText();
  1374.             this.fireEvent('rowremoved', this, index, record);
  1375.         }
  1376.     },
  1377.     // private
  1378.     onLoad : function(){
  1379.         this.scrollToTop.defer(Ext.isGecko ? 1 : 0, this);
  1380.     },
  1381.     // private
  1382.     onColWidthChange : function(cm, col, width){
  1383.         this.updateColumnWidth(col, width);
  1384.     },
  1385.     // private
  1386.     onHeaderChange : function(cm, col, text){
  1387.         this.updateHeaders();
  1388.     },
  1389.     // private
  1390.     onHiddenChange : function(cm, col, hidden){
  1391.         this.updateColumnHidden(col, hidden);
  1392.     },
  1393.     // private
  1394.     onColumnMove : function(cm, oldIndex, newIndex){
  1395.         this.indexMap = null;
  1396.         var s = this.getScrollState();
  1397.         this.refresh(true);
  1398.         this.restoreScroll(s);
  1399.         this.afterMove(newIndex);
  1400.         this.grid.fireEvent('columnmove', oldIndex, newIndex);
  1401.     },
  1402.     // private
  1403.     onColConfigChange : function(){
  1404.         delete this.lastViewWidth;
  1405.         this.indexMap = null;
  1406.         this.refresh(true);
  1407.     },
  1408.     /* -------------------- UI Events and Handlers ------------------------------ */
  1409.     // private
  1410.     initUI : function(grid){
  1411.         grid.on('headerclick', this.onHeaderClick, this);
  1412.     },
  1413.     // private
  1414.     initEvents : function(){
  1415.     },
  1416.     // private
  1417.     onHeaderClick : function(g, index){
  1418.         if(this.headersDisabled || !this.cm.isSortable(index)){
  1419.             return;
  1420.         }
  1421.         g.stopEditing(true);
  1422.         g.store.sort(this.cm.getDataIndex(index));
  1423.     },
  1424.     // private
  1425.     onRowOver : function(e, t){
  1426.         var row;
  1427.         if((row = this.findRowIndex(t)) !== false){
  1428.             this.addRowClass(row, 'x-grid3-row-over');
  1429.         }
  1430.     },
  1431.     // private
  1432.     onRowOut : function(e, t){
  1433.         var row;
  1434.         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
  1435.             this.removeRowClass(row, 'x-grid3-row-over');
  1436.         }
  1437.     },
  1438.     // private
  1439.     handleWheel : function(e){
  1440.         e.stopPropagation();
  1441.     },
  1442.     // private
  1443.     onRowSelect : function(row){
  1444.         this.addRowClass(row, this.selectedRowClass);
  1445.     },
  1446.     // private
  1447.     onRowDeselect : function(row){
  1448.         this.removeRowClass(row, this.selectedRowClass);
  1449.     },
  1450.     // private
  1451.     onCellSelect : function(row, col){
  1452.         var cell = this.getCell(row, col);
  1453.         if(cell){
  1454.             this.fly(cell).addClass('x-grid3-cell-selected');
  1455.         }
  1456.     },
  1457.     // private
  1458.     onCellDeselect : function(row, col){
  1459.         var cell = this.getCell(row, col);
  1460.         if(cell){
  1461.             this.fly(cell).removeClass('x-grid3-cell-selected');
  1462.         }
  1463.     },
  1464.     // private
  1465.     onColumnSplitterMoved : function(i, w){
  1466.         this.userResized = true;
  1467.         var cm = this.grid.colModel;
  1468.         cm.setColumnWidth(i, w, true);
  1469.         if(this.forceFit){
  1470.             this.fitColumns(true, false, i);
  1471.             this.updateAllColumnWidths();
  1472.         }else{
  1473.             this.updateColumnWidth(i, w);
  1474.             this.syncHeaderScroll();
  1475.         }
  1476.         this.grid.fireEvent('columnresize', i, w);
  1477.     },
  1478.     // private
  1479.     handleHdMenuClick : function(item){
  1480.         var index = this.hdCtxIndex,
  1481.             cm = this.cm, 
  1482.             ds = this.ds,
  1483.             id = item.getItemId();
  1484.         switch(id){
  1485.             case 'asc':
  1486.                 ds.sort(cm.getDataIndex(index), 'ASC');
  1487.                 break;
  1488.             case 'desc':
  1489.                 ds.sort(cm.getDataIndex(index), 'DESC');
  1490.                 break;
  1491.             default:
  1492.                 index = cm.getIndexById(id.substr(4));
  1493.                 if(index != -1){
  1494.                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
  1495.                         this.onDenyColumnHide();
  1496.                         return false;
  1497.                     }
  1498.                     cm.setHidden(index, item.checked);
  1499.                 }
  1500.         }
  1501.         return true;
  1502.     },
  1503.     // private
  1504.     isHideableColumn : function(c){
  1505.         return !c.hidden && !c.fixed;
  1506.     },
  1507.     // private
  1508.     beforeColMenuShow : function(){
  1509.         var cm = this.cm,  colCount = cm.getColumnCount();
  1510.         this.colMenu.removeAll();
  1511.         for(var i = 0; i < colCount; i++){
  1512.             if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
  1513.                 this.colMenu.add(new Ext.menu.CheckItem({
  1514.                     itemId: 'col-'+cm.getColumnId(i),
  1515.                     text: cm.getColumnHeader(i),
  1516.                     checked: !cm.isHidden(i),
  1517.                     hideOnClick:false,
  1518.                     disabled: cm.config[i].hideable === false
  1519.                 }));
  1520.             }
  1521.         }
  1522.     },
  1523.     // private
  1524.     handleHdDown : function(e, t){
  1525.         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
  1526.             e.stopEvent();
  1527.             var hd = this.findHeaderCell(t);
  1528.             Ext.fly(hd).addClass('x-grid3-hd-menu-open');
  1529.             var index = this.getCellIndex(hd);
  1530.             this.hdCtxIndex = index;
  1531.             var ms = this.hmenu.items, cm = this.cm;
  1532.             ms.get('asc').setDisabled(!cm.isSortable(index));
  1533.             ms.get('desc').setDisabled(!cm.isSortable(index));
  1534.             this.hmenu.on('hide', function(){
  1535.                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
  1536.             }, this, {single:true});
  1537.             this.hmenu.show(t, 'tl-bl?');
  1538.         }
  1539.     },
  1540.     // private
  1541.     handleHdOver : function(e, t){
  1542.         var hd = this.findHeaderCell(t);
  1543.         if(hd && !this.headersDisabled){
  1544.             this.activeHdRef = t;
  1545.             this.activeHdIndex = this.getCellIndex(hd);
  1546.             var fly = this.fly(hd);
  1547.             this.activeHdRegion = fly.getRegion();
  1548.             if(!this.cm.isMenuDisabled(this.activeHdIndex)){
  1549.                 fly.addClass('x-grid3-hd-over');
  1550.                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
  1551.                 if(this.activeHdBtn){
  1552.                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
  1553.                 }
  1554.             }
  1555.         }
  1556.     },
  1557.     // private
  1558.     handleHdMove : function(e, t){
  1559.         var hd = this.findHeaderCell(this.activeHdRef);
  1560.         if(hd && !this.headersDisabled){
  1561.             var hw = this.splitHandleWidth || 5,
  1562.                 r = this.activeHdRegion,
  1563.                 x = e.getPageX(),
  1564.                 ss = hd.style,
  1565.                 cur = '';
  1566.             if(this.grid.enableColumnResize !== false){
  1567.                 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
  1568.                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported
  1569.                 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
  1570.                     cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
  1571.                 }
  1572.             }
  1573.             ss.cursor = cur;
  1574.         }
  1575.     },
  1576.     // private
  1577.     handleHdOut : function(e, t){
  1578.         var hd = this.findHeaderCell(t);
  1579.         if(hd && (!Ext.isIE || !e.within(hd, true))){
  1580.             this.activeHdRef = null;
  1581.             this.fly(hd).removeClass('x-grid3-hd-over');
  1582.             hd.style.cursor = '';
  1583.         }
  1584.     },
  1585.     // private
  1586.     hasRows : function(){
  1587.         var fc = this.mainBody.dom.firstChild;
  1588.         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';
  1589.     },
  1590.     // back compat
  1591.     bind : function(d, c){
  1592.         this.initData(d, c);
  1593.     }
  1594. });
  1595. // private
  1596. // This is a support class used internally by the Grid components
  1597. Ext.grid.GridView.SplitDragZone = function(grid, hd){
  1598.     this.grid = grid;
  1599.     this.view = grid.getView();
  1600.     this.marker = this.view.resizeMarker;
  1601.     this.proxy = this.view.resizeProxy;
  1602.     Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
  1603.         'gridSplitters' + this.grid.getGridEl().id, {
  1604.         dragElId : Ext.id(this.proxy.dom), resizeFrame:false
  1605.     });
  1606.     this.scroll = false;
  1607.     this.hw = this.view.splitHandleWidth || 5;
  1608. };
  1609. Ext.extend(Ext.grid.GridView.SplitDragZone, Ext.dd.DDProxy, {
  1610.     b4StartDrag : function(x, y){
  1611.         this.view.headersDisabled = true;
  1612.         var h = this.view.mainWrap.getHeight();
  1613.         this.marker.setHeight(h);
  1614.         this.marker.show();
  1615.         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
  1616.         this.proxy.setHeight(h);
  1617.         var w = this.cm.getColumnWidth(this.cellIndex);
  1618.         var minw = Math.max(w-this.grid.minColumnWidth, 0);
  1619.         this.resetConstraints();
  1620.         this.setXConstraint(minw, 1000);
  1621.         this.setYConstraint(0, 0);
  1622.         this.minX = x - minw;
  1623.         this.maxX = x + 1000;
  1624.         this.startPos = x;
  1625.         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
  1626.     },
  1627.     
  1628.     allowHeaderDrag : function(e){
  1629.         return true;
  1630.     },
  1631.     handleMouseDown : function(e){
  1632.         var t = this.view.findHeaderCell(e.getTarget());
  1633.         if(t && this.allowHeaderDrag(e)){
  1634.             var xy = this.view.fly(t).getXY(), x = xy[0], y = xy[1];
  1635.             var exy = e.getXY(), ex = exy[0];
  1636.             var w = t.offsetWidth, adjust = false;
  1637.             if((ex - x) <= this.hw){
  1638.                 adjust = -1;
  1639.             }else if((x+w) - ex <= this.hw){
  1640.                 adjust = 0;
  1641.             }
  1642.             if(adjust !== false){
  1643.                 this.cm = this.grid.colModel;
  1644.                 var ci = this.view.getCellIndex(t);
  1645.                 if(adjust == -1){
  1646.                   if (ci + adjust < 0) {
  1647.                     return;
  1648.                   }
  1649.                     while(this.cm.isHidden(ci+adjust)){
  1650.                         --adjust;
  1651.                         if(ci+adjust < 0){
  1652.                             return;
  1653.                         }
  1654.                     }
  1655.                 }
  1656.                 this.cellIndex = ci+adjust;
  1657.                 this.split = t.dom;
  1658.                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
  1659.                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
  1660.                 }
  1661.             }else if(this.view.columnDrag){
  1662.                 this.view.columnDrag.callHandleMouseDown(e);
  1663.             }
  1664.         }
  1665.     },
  1666.     endDrag : function(e){
  1667.         this.marker.hide();
  1668.         var v = this.view;
  1669.         var endX = Math.max(this.minX, e.getPageX());
  1670.         var diff = endX - this.startPos;
  1671.         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
  1672.         setTimeout(function(){
  1673.             v.headersDisabled = false;
  1674.         }, 50);
  1675.     },
  1676.     autoOffset : function(){
  1677.         this.setDelta(0,0);
  1678.     }
  1679. });