GridView.js
上传用户:shuoshiled
上传日期:2018-01-28
资源大小:10124k
文件大小:58k
源码类别:

中间件编程

开发平台:

JavaScript

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