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

中间件编程

开发平台:

JavaScript

  1.         Ext.grid.GridPanel.superclass.afterRender.call(this);
  2.         this.view.layout();
  3.         if(this.deferRowRender){
  4.             this.view.afterRender.defer(10, this.view);
  5.         }else{
  6.             this.view.afterRender();
  7.         }
  8.         this.viewReady = true;
  9.     },
  10.     /**
  11.      * <p>Reconfigures the grid to use a different Store and Column Model
  12.      * and fires the 'reconfigure' event. The View will be bound to the new
  13.      * objects and refreshed.</p>
  14.      * <p>Be aware that upon reconfiguring a GridPanel, certain existing settings <i>may</i> become
  15.      * invalidated. For example the configured {@link #autoExpandColumn} may no longer exist in the
  16.      * new ColumnModel. Also, an existing {@link Ext.PagingToolbar PagingToolbar} will still be bound
  17.      * to the old Store, and will need rebinding. Any {@link #plugins} might also need reconfiguring
  18.      * with the new data.</p>
  19.      * @param {Ext.data.Store} store The new {@link Ext.data.Store} object
  20.      * @param {Ext.grid.ColumnModel} colModel The new {@link Ext.grid.ColumnModel} object
  21.      */
  22.     reconfigure : function(store, colModel){
  23.         if(this.loadMask){
  24.             this.loadMask.destroy();
  25.             this.loadMask = new Ext.LoadMask(this.bwrap,
  26.                     Ext.apply({}, {store:store}, this.initialConfig.loadMask));
  27.         }
  28.         this.view.initData(store, colModel);
  29.         this.store = store;
  30.         this.colModel = colModel;
  31.         if(this.rendered){
  32.             this.view.refresh(true);
  33.         }
  34.         this.fireEvent('reconfigure', this, store, colModel);
  35.     },
  36.     // private
  37.     onKeyDown : function(e){
  38.         this.fireEvent('keydown', e);
  39.     },
  40.     // private
  41.     onDestroy : function(){
  42.         if(this.rendered){
  43.             var c = this.body;
  44.             c.removeAllListeners();
  45.             c.update('');
  46.             Ext.destroy(this.view, this.loadMask);
  47.         }else if(this.store && this.store.autoDestroy){
  48.             this.store.destroy();
  49.         }
  50.         Ext.destroy(this.colModel, this.selModel);
  51.         this.store = this.selModel = this.colModel = this.view = this.loadMask = null;
  52.         Ext.grid.GridPanel.superclass.onDestroy.call(this);
  53.     },
  54.     // private
  55.     processEvent : function(name, e){
  56.         this.fireEvent(name, e);
  57.         var t = e.getTarget();
  58.         var v = this.view;
  59.         var header = v.findHeaderIndex(t);
  60.         if(header !== false){
  61.             this.fireEvent('header' + name, this, header, e);
  62.         }else{
  63.             var row = v.findRowIndex(t);
  64.             var cell = v.findCellIndex(t);
  65.             if(row !== false){
  66.                 this.fireEvent('row' + name, this, row, e);
  67.                 if(cell !== false){
  68.                     this.fireEvent('cell' + name, this, row, cell, e);
  69.                 }
  70.             }
  71.         }
  72.     },
  73.     // private
  74.     onClick : function(e){
  75.         this.processEvent('click', e);
  76.     },
  77.     // private
  78.     onMouseDown : function(e){
  79.         this.processEvent('mousedown', e);
  80.     },
  81.     // private
  82.     onContextMenu : function(e, t){
  83.         this.processEvent('contextmenu', e);
  84.     },
  85.     // private
  86.     onDblClick : function(e){
  87.         this.processEvent('dblclick', e);
  88.     },
  89.     // private
  90.     walkCells : function(row, col, step, fn, scope){
  91.         var cm = this.colModel, clen = cm.getColumnCount();
  92.         var ds = this.store, rlen = ds.getCount(), first = true;
  93.         if(step < 0){
  94.             if(col < 0){
  95.                 row--;
  96.                 first = false;
  97.             }
  98.             while(row >= 0){
  99.                 if(!first){
  100.                     col = clen-1;
  101.                 }
  102.                 first = false;
  103.                 while(col >= 0){
  104.                     if(fn.call(scope || this, row, col, cm) === true){
  105.                         return [row, col];
  106.                     }
  107.                     col--;
  108.                 }
  109.                 row--;
  110.             }
  111.         } else {
  112.             if(col >= clen){
  113.                 row++;
  114.                 first = false;
  115.             }
  116.             while(row < rlen){
  117.                 if(!first){
  118.                     col = 0;
  119.                 }
  120.                 first = false;
  121.                 while(col < clen){
  122.                     if(fn.call(scope || this, row, col, cm) === true){
  123.                         return [row, col];
  124.                     }
  125.                     col++;
  126.                 }
  127.                 row++;
  128.             }
  129.         }
  130.         return null;
  131.     },
  132.     // private
  133.     onResize : function(){
  134.         Ext.grid.GridPanel.superclass.onResize.apply(this, arguments);
  135.         if(this.viewReady){
  136.             this.view.layout();
  137.         }
  138.     },
  139.     /**
  140.      * Returns the grid's underlying element.
  141.      * @return {Element} The element
  142.      */
  143.     getGridEl : function(){
  144.         return this.body;
  145.     },
  146.     // private for compatibility, overridden by editor grid
  147.     stopEditing : Ext.emptyFn,
  148.     /**
  149.      * Returns the grid's selection model configured by the <code>{@link #selModel}</code>
  150.      * configuration option. If no selection model was configured, this will create
  151.      * and return a {@link Ext.grid.RowSelectionModel RowSelectionModel}.
  152.      * @return {SelectionModel}
  153.      */
  154.     getSelectionModel : function(){
  155.         if(!this.selModel){
  156.             this.selModel = new Ext.grid.RowSelectionModel(
  157.                     this.disableSelection ? {selectRow: Ext.emptyFn} : null);
  158.         }
  159.         return this.selModel;
  160.     },
  161.     /**
  162.      * Returns the grid's data store.
  163.      * @return {Ext.data.Store} The store
  164.      */
  165.     getStore : function(){
  166.         return this.store;
  167.     },
  168.     /**
  169.      * Returns the grid's ColumnModel.
  170.      * @return {Ext.grid.ColumnModel} The column model
  171.      */
  172.     getColumnModel : function(){
  173.         return this.colModel;
  174.     },
  175.     /**
  176.      * Returns the grid's GridView object.
  177.      * @return {Ext.grid.GridView} The grid view
  178.      */
  179.     getView : function(){
  180.         if(!this.view){
  181.             this.view = new Ext.grid.GridView(this.viewConfig);
  182.         }
  183.         return this.view;
  184.     },
  185.     /**
  186.      * Called to get grid's drag proxy text, by default returns this.ddText.
  187.      * @return {String} The text
  188.      */
  189.     getDragDropText : function(){
  190.         var count = this.selModel.getCount();
  191.         return String.format(this.ddText, count, count == 1 ? '' : 's');
  192.     }
  193.     /** 
  194.      * @cfg {String/Number} activeItem 
  195.      * @hide 
  196.      */
  197.     /** 
  198.      * @cfg {Boolean} autoDestroy 
  199.      * @hide 
  200.      */
  201.     /** 
  202.      * @cfg {Object/String/Function} autoLoad 
  203.      * @hide 
  204.      */
  205.     /** 
  206.      * @cfg {Boolean} autoWidth 
  207.      * @hide 
  208.      */
  209.     /** 
  210.      * @cfg {Boolean/Number} bufferResize 
  211.      * @hide 
  212.      */
  213.     /** 
  214.      * @cfg {String} defaultType 
  215.      * @hide 
  216.      */
  217.     /** 
  218.      * @cfg {Object} defaults 
  219.      * @hide 
  220.      */
  221.     /** 
  222.      * @cfg {Boolean} hideBorders 
  223.      * @hide 
  224.      */
  225.     /** 
  226.      * @cfg {Mixed} items 
  227.      * @hide 
  228.      */
  229.     /** 
  230.      * @cfg {String} layout 
  231.      * @hide 
  232.      */
  233.     /** 
  234.      * @cfg {Object} layoutConfig 
  235.      * @hide 
  236.      */
  237.     /** 
  238.      * @cfg {Boolean} monitorResize 
  239.      * @hide 
  240.      */
  241.     /** 
  242.      * @property items 
  243.      * @hide 
  244.      */
  245.     /** 
  246.      * @method add 
  247.      * @hide 
  248.      */
  249.     /** 
  250.      * @method cascade 
  251.      * @hide 
  252.      */
  253.     /** 
  254.      * @method doLayout 
  255.      * @hide 
  256.      */
  257.     /** 
  258.      * @method find 
  259.      * @hide 
  260.      */
  261.     /** 
  262.      * @method findBy 
  263.      * @hide 
  264.      */
  265.     /** 
  266.      * @method findById 
  267.      * @hide 
  268.      */
  269.     /** 
  270.      * @method findByType 
  271.      * @hide 
  272.      */
  273.     /** 
  274.      * @method getComponent 
  275.      * @hide 
  276.      */
  277.     /** 
  278.      * @method getLayout 
  279.      * @hide 
  280.      */
  281.     /** 
  282.      * @method getUpdater 
  283.      * @hide 
  284.      */
  285.     /** 
  286.      * @method insert 
  287.      * @hide 
  288.      */
  289.     /** 
  290.      * @method load 
  291.      * @hide 
  292.      */
  293.     /** 
  294.      * @method remove 
  295.      * @hide 
  296.      */
  297.     /** 
  298.      * @event add 
  299.      * @hide 
  300.      */
  301.     /** 
  302.      * @event afterLayout 
  303.      * @hide 
  304.      */
  305.     /** 
  306.      * @event beforeadd 
  307.      * @hide 
  308.      */
  309.     /** 
  310.      * @event beforeremove 
  311.      * @hide 
  312.      */
  313.     /** 
  314.      * @event remove 
  315.      * @hide 
  316.      */
  317.     /**
  318.      * @cfg {String} allowDomMove  @hide
  319.      */
  320.     /**
  321.      * @cfg {String} autoEl @hide
  322.      */
  323.     /**
  324.      * @cfg {String} applyTo  @hide
  325.      */
  326.     /**
  327.      * @cfg {String} autoScroll  @hide
  328.      */
  329.     /**
  330.      * @cfg {String} bodyBorder  @hide
  331.      */
  332.     /**
  333.      * @cfg {String} bodyStyle  @hide
  334.      */
  335.     /**
  336.      * @cfg {String} contentEl  @hide
  337.      */
  338.     /**
  339.      * @cfg {String} disabledClass  @hide
  340.      */
  341.     /**
  342.      * @cfg {String} elements  @hide
  343.      */
  344.     /**
  345.      * @cfg {String} html  @hide
  346.      */
  347.     /**
  348.      * @cfg {Boolean} preventBodyReset
  349.      * @hide
  350.      */
  351.     /**
  352.      * @property disabled
  353.      * @hide
  354.      */
  355.     /**
  356.      * @method applyToMarkup
  357.      * @hide
  358.      */
  359.     /**
  360.      * @method enable
  361.      * @hide
  362.      */
  363.     /**
  364.      * @method disable
  365.      * @hide
  366.      */
  367.     /**
  368.      * @method setDisabled
  369.      * @hide
  370.      */
  371. });
  372. Ext.reg('grid', Ext.grid.GridPanel);/**  * @class Ext.grid.GridView  * @extends Ext.util.Observable  * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.  * Methods of this class may be used to access user interface elements to enable  * special display effects. Do not change the DOM structure of the user interface.</p>  * <p>This class does not provide ways to manipulate the underlying data. The data  * model of a Grid is held in an {@link Ext.data.Store}.</p>  * @constructor  * @param {Object} config  */ Ext.grid.GridView = function(config){     Ext.apply(this, config);     // These events are only used internally by the grid components     this.addEvents(         /**          * @event beforerowremoved          * Internal UI Event. Fired before a row is removed.          * @param {Ext.grid.GridView} view          * @param {Number} rowIndex The index of the row to be removed.          * @param {Ext.data.Record} record The Record to be removed          */         "beforerowremoved",         /**          * @event beforerowsinserted          * Internal UI Event. Fired before rows are inserted.          * @param {Ext.grid.GridView} view          * @param {Number} firstRow The index of the first row to be inserted.          * @param {Number} lastRow The index of the last row to be inserted.          */         "beforerowsinserted",         /**          * @event beforerefresh          * Internal UI Event. Fired before the view is refreshed.          * @param {Ext.grid.GridView} view          */         "beforerefresh",         /**          * @event rowremoved          * Internal UI Event. Fired after a row is removed.          * @param {Ext.grid.GridView} view          * @param {Number} rowIndex The index of the row that was removed.          * @param {Ext.data.Record} record The Record that was removed          */         "rowremoved",         /**          * @event rowsinserted          * Internal UI Event. Fired after rows are inserted.          * @param {Ext.grid.GridView} view          * @param {Number} firstRow The index of the first inserted.          * @param {Number} lastRow The index of the last row inserted.          */         "rowsinserted",         /**          * @event rowupdated          * Internal UI Event. Fired after a row has been updated.          * @param {Ext.grid.GridView} view          * @param {Number} firstRow The index of the row updated.          * @param {Ext.data.record} record The Record backing the row updated.          */         "rowupdated",         /**          * @event refresh          * Internal UI Event. Fired after the GridView's body has been refreshed.          * @param {Ext.grid.GridView} view          */         "refresh"     );     Ext.grid.GridView.superclass.constructor.call(this); }; Ext.extend(Ext.grid.GridView, Ext.util.Observable, {     /**      * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom      * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>      * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added      * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string      * (e.g., 'my-class another-class'). Example usage:     <pre><code> viewConfig: {     forceFit: true,     showPreview: true, // custom property     enableRowBody: true, // required to create a second, full-width row to show expanded Record data     getRowClass: function(record, rowIndex, rp, ds){ // rp = rowParams         if(this.showPreview){             rp.body = '&lt;p>'+record.data.excerpt+'&lt;/p>';             return 'x-grid3-row-expanded';         }         return 'x-grid3-row-collapsed';     } },          </code></pre>      * @param {Record} record The {@link Ext.data.Record} corresponding to the current row.      * @param {Number} index The row index.      * @param {Object} rowParams A config object that is passed to the row template during rendering that allows      * customization of various aspects of a grid row.      * <p>If {@link #enableRowBody} is configured <b><tt></tt>true</b>, then the following properties may be set      * by this function, and will be used to render a full-width expansion row below each grid row:</p>      * <ul>      * <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>      * <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>      * </ul>      * The following property will be passed in, and may be appended to:      * <ul>      * <li><code>tstyle</code> : String <div class="sub-desc">A CSS style specification that willl be applied to the &lt;table> element which encapsulates      * both the standard grid row, and any expansion row.</div></li>      * </ul>      * @param {Store} store The {@link Ext.data.Store} this grid is bound to      * @method getRowClass      * @return {String} a CSS class name to add to the row.      */     /**      * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body      * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.      */     /**      * @cfg {String} emptyText Default text (html tags are accepted) to display in the grid body when no rows      * are available (defaults to ''). This value will be used to update the <tt>{@link #mainBody}</tt>:     <pre><code>     this.mainBody.update('&lt;div class="x-grid-empty">' + this.emptyText + '&lt;/div>');     </code></pre>      */     /**      * @cfg {Boolean} headersDisabled True to disable the grid column headers (defaults to <tt>false</tt>).       * Use the {@link Ext.grid.ColumnModel ColumnModel} <tt>{@link Ext.grid.ColumnModel#menuDisabled menuDisabled}</tt>      * config to disable the <i>menu</i> for individual columns.  While this config is true the      * following will be disabled:<div class="mdetail-params"><ul>      * <li>clicking on header to sort</li>      * <li>the trigger to reveal the menu.</li>      * </ul></div>      */     /**      * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations      * of the template methods of DragZone to enable dragging of the selected rows of a GridPanel.      * See {@link Ext.grid.GridDragZone} for details.</p>      * <p>This will <b>only</b> be present:<div class="mdetail-params"><ul>      * <li><i>if</i> the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop}: <tt>true</tt>.</li>      * <li><i>after</i> the owning GridPanel has been rendered.</li>      * </ul></div>      * @property dragZone      * @type {Ext.grid.GridDragZone}      */     /**      * @cfg {Boolean} deferEmptyText True to defer <tt>{@link #emptyText}</tt> being applied until the store's      * first load (defaults to <tt>true</tt>).      */     deferEmptyText : true,     /**      * @cfg {Number} scrollOffset The amount of space to reserve for the vertical scrollbar      * (defaults to <tt>19</tt> pixels).      */     scrollOffset : 19,     /**      * @cfg {Boolean} autoFill      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned      * when the grid is <b>initially rendered</b>.  The       * {@link Ext.grid.Column#width initially configured width}</tt> of each column will be adjusted      * to fit the grid width and prevent horizontal scrolling. If columns are later resized (manually      * or programmatically), the other columns in the grid will <b>not</b> be resized to fit the grid width.      * See <tt>{@link #forceFit}</tt> also.      */     autoFill : false,     /**      * @cfg {Boolean} forceFit      * Defaults to <tt>false</tt>.  Specify <tt>true</tt> to have the column widths re-proportioned      * at <b>all times</b>.  The {@link Ext.grid.Column#width initially configured width}</tt> of each      * column will be adjusted to fit the grid width and prevent horizontal scrolling. If columns are      * later resized (manually or programmatically), the other columns in the grid <b>will</b> be resized      * to fit the grid width. See <tt>{@link #autoFill}</tt> also.      */     forceFit : false,     /**      * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>["sort-asc", "sort-desc"]</tt>)      */     sortClasses : ["sort-asc", "sort-desc"],     /**      * @cfg {String} sortAscText The text displayed in the "Sort Ascending" menu item (defaults to <tt>"Sort Ascending"</tt>)      */     sortAscText : "Sort Ascending",     /**      * @cfg {String} sortDescText The text displayed in the "Sort Descending" menu item (defaults to <tt>"Sort Descending"</tt>)      */     sortDescText : "Sort Descending",     /**      * @cfg {String} columnsText The text displayed in the "Columns" menu item (defaults to <tt>"Columns"</tt>)      */     columnsText : "Columns",     /**      * @cfg {String} selectedRowClass The CSS class applied to a selected row (defaults to <tt>"x-grid3-row-selected"</tt>). An      * example overriding the default styling:     <pre><code>     .x-grid3-row-selected {background-color: yellow;}     </code></pre>      * Note that this only controls the row, and will not do anything for the text inside it.  To style inner      * facets (like text) use something like:     <pre><code>     .x-grid3-row-selected .x-grid3-cell-inner {         color: #FFCC00;     }     </code></pre>      * @type String      */     selectedRowClass : "x-grid3-row-selected",     // private     borderWidth : 2,     tdClass : 'x-grid3-cell',     hdCls : 'x-grid3-hd',     markDirty : true,     /**      * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to <tt>4</tt>)      */     cellSelectorDepth : 4,     /**      * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to <tt>10</tt>)      */     rowSelectorDepth : 10,     /**      * @cfg {String} cellSelector The selector used to find cells internally (defaults to <tt>'td.x-grid3-cell'</tt>)      */     cellSelector : 'td.x-grid3-cell',     /**      * @cfg {String} rowSelector The selector used to find rows internally (defaults to <tt>'div.x-grid3-row'</tt>)      */     rowSelector : 'div.x-grid3-row',          // private     firstRowCls: 'x-grid3-row-first',     lastRowCls: 'x-grid3-row-last',     rowClsRe: /(?:^|s+)x-grid3-row-(first|last|alt)(?:s+|$)/g,     /* -------------------------------- UI Specific ----------------------------- */     // private     initTemplates : function(){         var ts = this.templates || {};         if(!ts.master){             ts.master = new Ext.Template(                     '<div class="x-grid3" hidefocus="true">',                         '<div class="x-grid3-viewport">',                             '<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>',                             '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',                         '</div>',                         '<div class="x-grid3-resize-marker">&#160;</div>',                         '<div class="x-grid3-resize-proxy">&#160;</div>',                     '</div>'                     );         }         if(!ts.header){             ts.header = new Ext.Template(                     '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',                     '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',                     '</table>'                     );         }         if(!ts.hcell){             ts.hcell = new Ext.Template(                     '<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>' : '',                     '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',                     '</div></td>'                     );         }         if(!ts.body){             ts.body = new Ext.Template('{rows}');         }         if(!ts.row){             ts.row = new Ext.Template(                     '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',                     '<tbody><tr>{cells}</tr>',                     (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>' : ''),                     '</tbody></table></div>'                     );         }         if(!ts.cell){             ts.cell = new Ext.Template(                     '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',                     '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',                     '</td>'                     );         }         for(var k in ts){             var t = ts[k];             if(t && typeof t.compile == 'function' && !t.compiled){                 t.disableFormats = true;                 t.compile();             }         }         this.templates = ts;         this.colRe = new RegExp("x-grid3-td-([^\s]+)", "");     },     // private     fly : function(el){         if(!this._flyweight){             this._flyweight = new Ext.Element.Flyweight(document.body);         }         this._flyweight.dom = el;         return this._flyweight;     },     // private     getEditorParent : function(){         return this.scroller.dom;     },     // private     initElements : function(){         var E = Ext.Element;         var el = this.grid.getGridEl().dom.firstChild;         var cs = el.childNodes;         this.el = new E(el);         this.mainWrap = new E(cs[0]);         this.mainHd = new E(this.mainWrap.dom.firstChild);         if(this.grid.hideHeaders){             this.mainHd.setDisplayed(false);         }         this.innerHd = this.mainHd.dom.firstChild;         this.scroller = new E(this.mainWrap.dom.childNodes[1]);         if(this.forceFit){             this.scroller.setStyle('overflow-x', 'hidden');         }         /**          * <i>Read-only</i>. The GridView's body Element which encapsulates all rows in the Grid.          * This {@link Ext.Element Element} is only available after the GridPanel has been rendered.          * @type Ext.Element          * @property mainBody          */         this.mainBody = new E(this.scroller.dom.firstChild);         this.focusEl = new E(this.scroller.dom.childNodes[1]);         this.focusEl.swallowEvent("click", true);         this.resizeMarker = new E(cs[1]);         this.resizeProxy = new E(cs[2]);     },     // private     getRows : function(){         return this.hasRows() ? this.mainBody.dom.childNodes : [];     },     // finder methods, used with delegation     // private     findCell : function(el){         if(!el){             return false;         }         return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);     }, /**  * <p>Return the index of the grid column which contains the passed element.</p>  * See also {@link #findRowIndex}  * @param {Element} el The target element  * @return The column index, or <b>false</b> if the target element is not within a row of this GridView.  */     findCellIndex : function(el, requiredCls){         var cell = this.findCell(el);         if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){             return this.getCellIndex(cell);         }         return false;     },     // private     getCellIndex : function(el){         if(el){             var m = el.className.match(this.colRe);             if(m && m[1]){                 return this.cm.getIndexById(m[1]);             }         }         return false;     },     // private     findHeaderCell : function(el){         var cell = this.findCell(el);         return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;     },     // private     findHeaderIndex : function(el){         return this.findCellIndex(el, this.hdCls);     }, /**  * Return the HtmlElement representing the grid row which contains the passed element.  * @param {Element} el The target element  * @return The row element, or null if the target element is not within a row of this GridView.  */     findRow : function(el){         if(!el){             return false;         }         return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);     }, /**  * <p>Return the index of the grid row which contains the passed element.</p>  * See also {@link #findCellIndex}  * @param {Element} el The target element  * @return The row index, or <b>false</b> if the target element is not within a row of this GridView.  */     findRowIndex : function(el){         var r = this.findRow(el);         return r ? r.rowIndex : false;     },     // getter methods for fetching elements dynamically in the grid /**  * Return the <tt>&lt;div></tt> HtmlElement which represents a Grid row for the specified index.  * @param {Number} index The row index  * @return {HtmlElement} The div element.  */     getRow : function(row){         return this.getRows()[row];     }, /**  * Returns the grid's <tt>&lt;td></tt> HtmlElement at the specified coordinates.  * @param {Number} row The row index in which to find the cell.  * @param {Number} col The column index of the cell.  * @return {HtmlElement} The td at the specified coordinates.  */     getCell : function(row, col){         return this.getRow(row).getElementsByTagName('td')[col];     }, /**  * Return the <tt>&lt;td></tt> HtmlElement which represents the Grid's header cell for the specified column index.  * @param {Number} index The column index  * @return {HtmlElement} The td element.  */     getHeaderCell : function(index){       return this.mainHd.dom.getElementsByTagName('td')[index];     },     // manipulating elements     // private - use getRowClass to apply custom row classes     addRowClass : function(row, cls){         var r = this.getRow(row);         if(r){             this.fly(r).addClass(cls);         }     },     // private     removeRowClass : function(row, cls){         var r = this.getRow(row);         if(r){             this.fly(r).removeClass(cls);         }     },     // private     removeRow : function(row){         Ext.removeNode(this.getRow(row));         this.syncFocusEl(row);     },          // private     removeRows : function(firstRow, lastRow){         var bd = this.mainBody.dom;         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){             Ext.removeNode(bd.childNodes[firstRow]);         }         this.syncFocusEl(firstRow);     },     // scrolling stuff     // private     getScrollState : function(){         var sb = this.scroller.dom;         return {left: sb.scrollLeft, top: sb.scrollTop};     },     // private     restoreScroll : function(state){         var sb = this.scroller.dom;         sb.scrollLeft = state.left;         sb.scrollTop = state.top;     },     /**      * Scrolls the grid to the top      */     scrollToTop : function(){         this.scroller.dom.scrollTop = 0;         this.scroller.dom.scrollLeft = 0;     },     // private     syncScroll : function(){       this.syncHeaderScroll();       var mb = this.scroller.dom;         this.grid.fireEvent("bodyscroll", mb.scrollLeft, mb.scrollTop);     },     // private     syncHeaderScroll : function(){         var mb = this.scroller.dom;         this.innerHd.scrollLeft = mb.scrollLeft;         this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)     },     // private     updateSortIcon : function(col, dir){         var sc = this.sortClasses;         var hds = this.mainHd.select('td').removeClass(sc);         hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);     },     // private     updateAllColumnWidths : function(){         var tw = this.getTotalWidth(),             clen = this.cm.getColumnCount(),             ws = [],             len,             i;         for(i = 0; i < clen; i++){             ws[i] = this.getColumnWidth(i);         }         this.innerHd.firstChild.style.width = this.getOffsetWidth();         this.innerHd.firstChild.firstChild.style.width = tw;         this.mainBody.dom.style.width = tw;         for(i = 0; i < clen; i++){             var hd = this.getHeaderCell(i);             hd.style.width = ws[i];         }         var ns = this.getRows(), row, trow;         for(i = 0, len = ns.length; i < len; i++){             row = ns[i];             row.style.width = tw;             if(row.firstChild){                 row.firstChild.style.width = tw;                 trow = row.firstChild.rows[0];                 for (var j = 0; j < clen; j++) {                    trow.childNodes[j].style.width = ws[j];                 }             }         }         this.onAllColumnWidthsUpdated(ws, tw);     },     // private     updateColumnWidth : function(col, width){         var w = this.getColumnWidth(col);         var tw = this.getTotalWidth();         this.innerHd.firstChild.style.width = this.getOffsetWidth();         this.innerHd.firstChild.firstChild.style.width = tw;         this.mainBody.dom.style.width = tw;         var hd = this.getHeaderCell(col);         hd.style.width = w;         var ns = this.getRows(), row;         for(var i = 0, len = ns.length; i < len; i++){             row = ns[i];             row.style.width = tw;             if(row.firstChild){                 row.firstChild.style.width = tw;                 row.firstChild.rows[0].childNodes[col].style.width = w;             }         }         this.onColumnWidthUpdated(col, w, tw);     },     // private     updateColumnHidden : function(col, hidden){         var tw = this.getTotalWidth();         this.innerHd.firstChild.style.width = this.getOffsetWidth();         this.innerHd.firstChild.firstChild.style.width = tw;         this.mainBody.dom.style.width = tw;         var display = hidden ? 'none' : '';         var hd = this.getHeaderCell(col);         hd.style.display = display;         var ns = this.getRows(), row;         for(var i = 0, len = ns.length; i < len; i++){             row = ns[i];             row.style.width = tw;             if(row.firstChild){                 row.firstChild.style.width = tw;                 row.firstChild.rows[0].childNodes[col].style.display = display;             }         }         this.onColumnHiddenUpdated(col, hidden, tw);         delete this.lastViewWidth; // force recalc         this.layout();     },     // private     doRender : function(cs, rs, ds, startRow, colCount, stripe){         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;         var tstyle = 'width:'+this.getTotalWidth()+';';         // buffers         var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;         for(var j = 0, len = rs.length; j < len; j++){             r = rs[j]; cb = [];             var rowIndex = (j+startRow);             for(var i = 0; i < colCount; i++){                 c = cs[i];                 p.id = c.id;                 p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');                 p.attr = p.cellAttr = "";                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);                 p.style = c.style;                 if(Ext.isEmpty(p.value)){                     p.value = "&#160;";                 }                 if(this.markDirty && r.dirty && typeof r.modified[c.name] !== 'undefined'){                     p.css += ' x-grid3-dirty-cell';                 }                 cb[cb.length] = ct.apply(p);             }             var alt = [];             if(stripe && ((rowIndex+1) % 2 === 0)){                 alt[0] = "x-grid3-row-alt";             }             if(r.dirty){                 alt[1] = " x-grid3-dirty-row";             }             rp.cols = colCount;             if(this.getRowClass){                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);             }             rp.alt = alt.join(" ");             rp.cells = cb.join("");             buf[buf.length] =  rt.apply(rp);         }         return buf.join("");     },     // private     processRows : function(startRow, skipStripe){         if(!this.ds || this.ds.getCount() < 1){             return;         }         var rows = this.getRows();         skipStripe = skipStripe || !this.grid.stripeRows;         startRow = startRow || 0;         Ext.each(rows, function(row, idx){             row.rowIndex = idx;             row.className = row.className.replace(this.rowClsRe, ' ');             if (!skipStripe && (idx + 1) % 2 === 0) {                 row.className += ' x-grid3-row-alt';             }         });         // add first/last-row classes         if(startRow === 0){             Ext.fly(rows[0]).addClass(this.firstRowCls);         }         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);     },     afterRender : function(){         if(!this.ds || !this.cm){             return;         }         this.mainBody.dom.innerHTML = this.renderRows() || '&#160;';         this.processRows(0, true);         if(this.deferEmptyText !== true){             this.applyEmptyText();         }     },     // private     renderUI : function(){         var header = this.renderHeaders();         var body = this.templates.body.apply({rows:'&#160;'});         var html = this.templates.master.apply({             body: body,             header: header,             ostyle: 'width:'+this.getOffsetWidth()+';',             bstyle: 'width:'+this.getTotalWidth()+';'         });         var g = this.grid;         g.getGridEl().dom.innerHTML = html;         this.initElements();         // get mousedowns early         Ext.fly(this.innerHd).on("click", this.handleHdDown, this);         this.mainHd.on({             scope: this,             mouseover: this.handleHdOver,             mouseout: this.handleHdOut,             mousemove: this.handleHdMove         });         this.scroller.on('scroll', this.syncScroll,  this);         if(g.enableColumnResize !== false){             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);         }         if(g.enableColumnMove){             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);         }         if(g.enableHdMenu !== false){             this.hmenu = new Ext.menu.Menu({id: g.id + "-hctx"});             this.hmenu.add(                 {itemId:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},                 {itemId:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}             );             if(g.enableColumnHide !== false){                 this.colMenu = new Ext.menu.Menu({id:g.id + "-hcols-menu"});                 this.colMenu.on({                     scope: this,                     beforeshow: this.beforeColMenuShow,                     itemclick: this.handleHdMenuClick                 });                 this.hmenu.add('-', {                     itemId:"columns",                     hideOnClick: false,                     text: this.columnsText,                     menu: this.colMenu,                     iconCls: 'x-cols-icon'                 });             }             this.hmenu.on("itemclick", this.handleHdMenuClick, this);         }         if(g.trackMouseOver){             this.mainBody.on({                 scope: this,                 mouseover: this.onRowOver,                 mouseout: this.onRowOut             });         }         if(g.enableDragDrop || g.enableDrag){             this.dragZone = new Ext.grid.GridDragZone(g, {                 ddGroup : g.ddGroup || 'GridDD'             });         }         this.updateHeaderSortState();     },     // private     layout : function(){         if(!this.mainBody){             return; // not rendered         }         var g = this.grid;         var c = g.getGridEl();         var csize = c.getSize(true);         var vw = csize.width;         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){ // display: none?             return;         }                  if(g.autoHeight){             this.scroller.dom.style.overflow = 'visible';             if(Ext.isWebKit){                 this.scroller.dom.style.position = 'static';             }         }else{             this.el.setSize(csize.width, csize.height);             var hdHeight = this.mainHd.getHeight();             var vh = csize.height - (hdHeight);             this.scroller.setSize(vw, vh);             if(this.innerHd){                 this.innerHd.style.width = (vw)+'px';             }         }         if(this.forceFit){             if(this.lastViewWidth != vw){                 this.fitColumns(false, false);                 this.lastViewWidth = vw;             }         }else {             this.autoExpand();             this.syncHeaderScroll();         }         this.onLayout(vw, vh);     },     // template functions for subclasses and plugins     // these functions include precalculated values     onLayout : function(vw, vh){         // do nothing     },     onColumnWidthUpdated : function(col, w, tw){         //template method     },     onAllColumnWidthsUpdated : function(ws, tw){         //template method     },     onColumnHiddenUpdated : function(col, hidden, tw){         // template method     },     updateColumnText : function(col, text){         // template method     },     afterMove : function(colIndex){         // template method     },     /* ----------------------------------- Core Specific -------------------------------------------*/     // private     init : function(grid){         this.grid = grid;         this.initTemplates();         this.initData(grid.store, grid.colModel);         this.initUI(grid);     },     // private     getColumnId : function(index){       return this.cm.getColumnId(index);     },          // private      getOffsetWidth : function() {         return (this.cm.getTotalWidth() + this.scrollOffset) + 'px';     },     // private     renderHeaders : function(){         var cm = this.cm,              ts = this.templates,             ct = ts.hcell,             cb = [],              p = {},             len = cm.getColumnCount(),             last = len - 1;                      for(var i = 0; i < len; i++){             p.id = cm.getColumnId(i);             p.value = cm.getColumnHeader(i) || "";             p.style = this.getColumnStyle(i, true);             p.tooltip = this.getColumnTooltip(i);             p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');             if(cm.config[i].align == 'right'){                 p.istyle = 'padding-right:16px';             } else {                 delete p.istyle;             }             cb[cb.length] = ct.apply(p);         }         return ts.header.apply({cells: cb.join(""), tstyle:'width:'+this.getTotalWidth()+';'});     },     // private     getColumnTooltip : function(i){         var tt = this.cm.getColumnTooltip(i);         if(tt){             if(Ext.QuickTips.isEnabled()){                 return 'ext:qtip="'+tt+'"';             }else{                 return 'title="'+tt+'"';             }         }         return "";     },     // private     beforeUpdate : function(){         this.grid.stopEditing(true);     },     // private     updateHeaders : function(){         this.innerHd.firstChild.innerHTML = this.renderHeaders();         this.innerHd.firstChild.style.width = this.getOffsetWidth();         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();     },     /**      * Focuses the specified row.      * @param {Number} row The row index      */     focusRow : function(row){         this.focusCell(row, 0, false);     },     /**      * Focuses the specified cell.      * @param {Number} row The row index      * @param {Number} col The column index      */     focusCell : function(row, col, hscroll){         this.syncFocusEl(this.ensureVisible(row, col, hscroll));         if(Ext.isGecko){             this.focusEl.focus();         }else{             this.focusEl.focus.defer(1, this.focusEl);         }     },     resolveCell : function(row, col, hscroll){         if(typeof row != "number"){             row = row.rowIndex;         }         if(!this.ds){             return null;         }         if(row < 0 || row >= this.ds.getCount()){             return null;         }         col = (col !== undefined ? col : 0);         var rowEl = this.getRow(row),             cm = this.cm,             colCount = cm.getColumnCount(),             cellEl;         if(!(hscroll === false && col === 0)){             while(col < colCount && cm.isHidden(col)){                 col++;             }             cellEl = this.getCell(row, col);         }         return {row: rowEl, cell: cellEl};     },     getResolvedXY : function(resolved){         if(!resolved){             return null;         }         var s = this.scroller.dom, c = resolved.cell, r = resolved.row;         return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];     },     syncFocusEl : function(row, col, hscroll){         var xy = row;         if(!Ext.isArray(xy)){             row = Math.min(row, Math.max(0, this.getRows().length-1));             xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));         }         this.focusEl.setXY(xy||this.scroller.getXY());     },     ensureVisible : function(row, col, hscroll){         var resolved = this.resolveCell(row, col, hscroll);         if(!resolved || !resolved.row){             return;         }         var rowEl = resolved.row,              cellEl = resolved.cell,             c = this.scroller.dom,             ctop = 0,             p = rowEl,              stop = this.el.dom;                      while(p && p != stop){             ctop += p.offsetTop;             p = p.offsetParent;         }         ctop -= this.mainHd.dom.offsetHeight;         var cbot = ctop + rowEl.offsetHeight,             ch = c.clientHeight,             sbot = stop + ch;                      stop = parseInt(c.scrollTop, 10);                  if(ctop < stop){           c.scrollTop = ctop;         }else if(cbot > sbot){             c.scrollTop = cbot-ch;         }         if(hscroll !== false){             var cleft = parseInt(cellEl.offsetLeft, 10);             var cright = cleft + cellEl.offsetWidth;             var sleft = parseInt(c.scrollLeft, 10);             var sright = sleft + c.clientWidth;             if(cleft < sleft){                 c.scrollLeft = cleft;             }else if(cright > sright){                 c.scrollLeft = cright-c.clientWidth;             }         }         return this.getResolvedXY(resolved);     },     // private     insertRows : function(dm, firstRow, lastRow, isUpdate){         var last = dm.getCount() - 1;         if(!isUpdate && firstRow === 0 && lastRow >= last){             this.refresh();         }else{             if(!isUpdate){                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);             }             var html = this.renderRows(firstRow, lastRow),                 before = this.getRow(firstRow);             if(before){                 if(firstRow === 0){                     Ext.fly(this.getRow(0)).removeClass(this.firstRowCls);                 }                 Ext.DomHelper.insertHtml('beforeBegin', before, html);             }else{                 var r = this.getRow(last - 1);                 if(r){                     Ext.fly(r).removeClass(this.lastRowCls);                 }                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);             }             if(!isUpdate){                 this.fireEvent("rowsinserted", this, firstRow, lastRow);                 this.processRows(firstRow);             }else if(firstRow === 0 || firstRow >= last){                 //ensure first/last row is kept after an update.                 Ext.fly(this.getRow(firstRow)).addClass(firstRow === 0 ? this.firstRowCls : this.lastRowCls);             }         }         this.syncFocusEl(firstRow);     },     // private     deleteRows : function(dm, firstRow, lastRow){         if(dm.getRowCount()<1){             this.refresh();         }else{             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);             this.removeRows(firstRow, lastRow);             this.processRows(firstRow);             this.fireEvent("rowsdeleted", this, firstRow, lastRow);         }     },     // private     getColumnStyle : function(col, isHeader){         var style = !isHeader ? (this.cm.config[col].css || '') : '';         style += 'width:'+this.getColumnWidth(col)+';';         if(this.cm.isHidden(col)){             style += 'display:none;';         }         var align = this.cm.config[col].align;         if(align){             style += 'text-align:'+align+';';         }         return style;     },     // private     getColumnWidth : function(col){         var w = this.cm.getColumnWidth(col);         if(typeof w == 'number'){             return (Ext.isBorderBox ? w : (w-this.borderWidth > 0 ? w-this.borderWidth:0)) + 'px';         }         return w;     },     // private     getTotalWidth : function(){         return this.cm.getTotalWidth()+'px';     },     // private     fitColumns : function(preventRefresh, onlyExpand, omitColumn){         var cm = this.cm, i;         var tw = cm.getTotalWidth(false);         var aw = this.grid.getGridEl().getWidth(true)-this.scrollOffset;         if(aw < 20){ // not initialized, so don't screw up the default widths             return;         }         var extra = aw - tw;         if(extra === 0){             return false;         }         var vc = cm.getColumnCount(true);         var ac = vc-(typeof omitColumn == 'number' ? 1 : 0);         if(ac === 0){             ac = 1;             omitColumn = undefined;         }         var colCount = cm.getColumnCount();         var cols = [];         var extraCol = 0;         var width = 0;         var w;         for (i = 0; i < colCount; i++){             if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){                 w = cm.getColumnWidth(i);                 cols.push(i);                 extraCol = i;                 cols.push(w);                 width += w;             }         }         var frac = (aw - cm.getTotalWidth())/width;         while (cols.length){             w = cols.pop();             i = cols.pop();             cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);         }         if((tw = cm.getTotalWidth(false)) > aw){             var adjustCol = ac != vc ? omitColumn : extraCol;              cm.setColumnWidth(adjustCol, Math.max(1,                      cm.getColumnWidth(adjustCol)- (tw-aw)), true);         }         if(preventRefresh !== true){             this.updateAllColumnWidths();         }         return true;     },     // private     autoExpand : function(preventUpdate){         var g = this.grid, cm = this.cm;         if(!this.userResized && g.autoExpandColumn){             var tw = cm.getTotalWidth(false);             var aw = this.grid.getGridEl().getWidth(true)-this.scrollOffset;             if(tw != aw){                 var ci = cm.getIndexById(g.autoExpandColumn);                 var currentWidth = cm.getColumnWidth(ci);                 var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);                 if(cw != currentWidth){                     cm.setColumnWidth(ci, cw, true);                     if(preventUpdate !== true){                         this.updateColumnWidth(ci, cw);                     }                 }             }         }     },     // private     getColumnData : function(){         // build a map for all the columns         var cs = [], cm = this.cm, colCount = cm.getColumnCount();         for(var i = 0; i < colCount; i++){             var name = cm.getDataIndex(i);             cs[i] = {                 name : (typeof name == 'undefined' ? this.ds.fields.get(i).name : name),                 renderer : cm.getRenderer(i),                 id : cm.getColumnId(i),                 style : this.getColumnStyle(i)             };         }         return cs;     },     // private     renderRows : function(startRow, endRow){         // pull in all the crap needed to render rows         var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;         var colCount = cm.getColumnCount();         if(ds.getCount() < 1){             return "";         }         var cs = this.getColumnData();         startRow = startRow || 0;         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;         // records to render         var rs = ds.getRange(startRow, endRow);         return this.doRender(cs, rs, ds, startRow, colCount, stripe);     },     // private     renderBody : function(){         var markup = this.renderRows() || '&#160;';         return this.templates.body.apply({rows: markup});     },     // private     refreshRow : function(record){         var ds = this.ds, index;         if(typeof record == 'number'){             index = record;             record = ds.getAt(index);             if(!record){                 return;             }         }else{             index = ds.indexOf(record);             if(index < 0){                 return;             }         }         this.insertRows(ds, index, index, true);         this.getRow(index).rowIndex = index;         this.onRemove(ds, record, index+1, true);         this.fireEvent("rowupdated", this, index, record);     },     /**      * Refreshs the grid UI      * @param {Boolean} headersToo (optional) True to also refresh the headers      */     refresh : function(headersToo){         this.fireEvent("beforerefresh", this);         this.grid.stopEditing(true);         var result = this.renderBody();         this.mainBody.update(result).setWidth(this.getTotalWidth());         if(headersToo === true){             this.updateHeaders();             this.updateHeaderSortState();         }         this.processRows(0, true);         this.layout();         this.applyEmptyText();         this.fireEvent("refresh", this);     },     // private     applyEmptyText : function(){         if(this.emptyText && !this.hasRows()){             this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');         }     },     // private     updateHeaderSortState : function(){         var state = this.ds.getSortState();         if(!state){             return;         }         if(!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)){             this.grid.fireEvent('sortchange', this.grid, state);         }         this.sortState = state;         var sortColumn = this.cm.findColumnIndex(state.field);         if(sortColumn != -1){             var sortDir = state.direction;             this.updateSortIcon(sortColumn, sortDir);         }     },     // private     destroy : function(){         if(this.colMenu){             Ext.menu.MenuMgr.unregister(this.colMenu);             this.colMenu.destroy();             delete this.colMenu;         }         if(this.hmenu){             Ext.menu.MenuMgr.unregister(this.hmenu);             this.hmenu.destroy();             delete this.hmenu;         }         if(this.grid.enableColumnMove){             var dds = Ext.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];             if(dds){                 for(var dd in dds){                     if(!dds[dd].config.isTarget && dds[dd].dragElId){                         var elid = dds[dd].dragElId;                         dds[dd].unreg();                         Ext.get(elid).remove();                     } else if(dds[dd].config.isTarget){                         dds[dd].proxyTop.remove();                         dds[dd].proxyBottom.remove();                         dds[dd].unreg();                     }                     if(Ext.dd.DDM.locationCache[dd]){                         delete Ext.dd.DDM.locationCache[dd];                     }                 }                 delete Ext.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];             }         }         if(this.dragZone){             this.dragZone.unreg();         }                  Ext.fly(this.innerHd).removeAllListeners();         Ext.removeNode(this.innerHd);                  Ext.destroy(this.resizeMarker, this.resizeProxy, this.focusEl, this.mainBody,                      this.scroller, this.mainHd, this.mainWrap, this.dragZone,                      this.splitZone, this.columnDrag, this.columnDrop);         this.initData(null, null);         Ext.EventManager.removeResizeListener(this.onWindowResize, this);         this.purgeListeners();     },     // private     onDenyColumnHide : function(){     },     // private     render : function(){         if(this.autoFill){             var ct = this.grid.ownerCt;             if (ct && ct.getLayout()){                 ct.on('afterlayout', function(){                      this.fitColumns(true, true);                     this.updateHeaders();                  }, this, {single: true});              }else{                  this.fitColumns(true, true);              }         }else if(this.forceFit){             this.fitColumns(true, false);         }else if(this.grid.autoExpandColumn){             this.autoExpand(true);         }         this.renderUI();     },     /* --------------------------------- Model Events and Handlers --------------------------------*/     // private     initData : function(ds, cm){         if(this.ds){             this.ds.un("load", this.onLoad, this);             this.ds.un("datachanged", this.onDataChange, this);             this.ds.un("add", this.onAdd, this);             this.ds.un("remove", this.onRemove, this);             this.ds.un("update", this.onUpdate, this);             this.ds.un("clear", this.onClear, this);             if(this.ds !== ds && this.ds.autoDestroy){                 this.ds.destroy();             }         }         if(ds){             ds.on({                 scope: this,                 load: this.onLoad,                 datachanged: this.onDataChange,                 add: this.onAdd,                 remove: this.onRemove,                 update: this.onUpdate,                 clear: this.onClear             });         }         this.ds = ds;         if(this.cm){             this.cm.un("configchange", this.onColConfigChange, this);             this.cm.un("widthchange", this.onColWidthChange, this);             this.cm.un("headerchange", this.onHeaderChange, this);             this.cm.un("hiddenchange", this.onHiddenChange, this);             this.cm.un("columnmoved", this.onColumnMove, this);         }         if(cm){             delete this.lastViewWidth;             cm.on({                 scope: this,                 configchange: this.onColConfigChange,                 widthchange: this.onColWidthChange,                 headerchange: this.onHeaderChange,                 hiddenchange: this.onHiddenChange,                 columnmoved: this.onColumnMove             });         }         this.cm = cm;     },     // private     onDataChange : function(){         this.refresh();         this.updateHeaderSortState();         this.syncFocusEl(0);     },     // private     onClear : function(){         this.refresh();         this.syncFocusEl(0);     },     // private     onUpdate : function(ds, record){         this.refreshRow(record);     },     // private     onAdd : function(ds, records, index){         this.insertRows(ds, index, index + (records.length-1));     },     // private     onRemove : function(ds, record, index, isUpdate){         if(isUpdate !== true){             this.fireEvent("beforerowremoved", this, index, record);         }         this.removeRow(index);         if(isUpdate !== true){             this.processRows(index);             this.applyEmptyText();             this.fireEvent("rowremoved", this, index, record);         }     },     // private     onLoad : function(){         this.scrollToTop();     },     // private     onColWidthChange : function(cm, col, width){         this.updateColumnWidth(col, width);     },     // private     onHeaderChange : function(cm, col, text){         this.updateHeaders();     },     // private     onHiddenChange : function(cm, col, hidden){         this.updateColumnHidden(col, hidden);     },     // private     onColumnMove : function(cm, oldIndex, newIndex){         this.indexMap = null;         var s = this.getScrollState();         this.refresh(true);         this.restoreScroll(s);         this.afterMove(newIndex);         this.grid.fireEvent('columnmove', oldIndex, newIndex);     },     // private     onColConfigChange : function(){         delete this.lastViewWidth;         this.indexMap = null;         this.refresh(true);     },     /* -------------------- UI Events and Handlers ------------------------------ */     // private     initUI : function(grid){         grid.on("headerclick", this.onHeaderClick, this);     },     // private     initEvents : function(){     },     // private     onHeaderClick : function(g, index){         if(this.headersDisabled || !this.cm.isSortable(index)){             return;         }         g.stopEditing(true);         g.store.sort(this.cm.getDataIndex(index));     },     // private     onRowOver : function(e, t){         var row;         if((row = this.findRowIndex(t)) !== false){             this.addRowClass(row, "x-grid3-row-over");         }     },     // private     onRowOut : function(e, t){         var row;         if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){             this.removeRowClass(row, "x-grid3-row-over");         }     },     // private     handleWheel : function(e){         e.stopPropagation();     },     // private     onRowSelect : function(row){         this.addRowClass(row, this.selectedRowClass);     },     // private     onRowDeselect : function(row){         this.removeRowClass(row, this.selectedRowClass);     },     // private     onCellSelect : function(row, col){         var cell = this.getCell(row, col);         if(cell){             this.fly(cell).addClass("x-grid3-cell-selected");         }     },     // private     onCellDeselect : function(row, col){         var cell = this.getCell(row, col);         if(cell){             this.fly(cell).removeClass("x-grid3-cell-selected");         }     },     // private     onColumnSplitterMoved : function(i, w){         this.userResized = true;         var cm = this.grid.colModel;         cm.setColumnWidth(i, w, true);         if(this.forceFit){             this.fitColumns(true, false, i);             this.updateAllColumnWidths();         }else{             this.updateColumnWidth(i, w);             this.syncHeaderScroll();         }         this.grid.fireEvent("columnresize", i, w);     },     // private     handleHdMenuClick : function(item){         var index = this.hdCtxIndex;         var cm = this.cm, ds = this.ds;         switch(item.itemId){             case "asc":                 ds.sort(cm.getDataIndex(index), "ASC");                 break;             case "desc":                 ds.sort(cm.getDataIndex(index), "DESC");                 break;             default:                 index = cm.getIndexById(item.itemId.substr(4));                 if(index != -1){                     if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){                         this.onDenyColumnHide();                         return false;                     }                     cm.setHidden(index, item.checked);                 }         }         return true;     },     // private     isHideableColumn : function(c){         return !c.hidden && !c.fixed;     },     // private     beforeColMenuShow : function(){         var cm = this.cm,  colCount = cm.getColumnCount();         this.colMenu.removeAll();         for(var i = 0; i < colCount; i++){             if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){                 this.colMenu.add(new Ext.menu.CheckItem({                     itemId: "col-"+cm.getColumnId(i),                     text: cm.getColumnHeader(i),                     checked: !cm.isHidden(i),                     hideOnClick:false,                     disabled: cm.config[i].hideable === false                 }));             }         }     },     // private     handleHdDown : function(e, t){         if(Ext.fly(t).hasClass('x-grid3-hd-btn')){             e.stopEvent();             var hd = this.findHeaderCell(t);             Ext.fly(hd).addClass('x-grid3-hd-menu-open');             var index = this.getCellIndex(hd);             this.hdCtxIndex = index;             var ms = this.hmenu.items, cm = this.cm;             ms.get("asc").setDisabled(!cm.isSortable(index));             ms.get("desc").setDisabled(!cm.isSortable(index));             this.hmenu.on("hide", function(){                 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');             }, this, {single:true});             this.hmenu.show(t, "tl-bl?");         }     },     // private     handleHdOver : function(e, t){         var hd = this.findHeaderCell(t);         if(hd && !this.headersDisabled){             this.activeHd = hd;             this.activeHdIndex = this.getCellIndex(hd);             var fly = this.fly(hd);             this.activeHdRegion = fly.getRegion();             if(!this.cm.isMenuDisabled(this.activeHdIndex)){                 fly.addClass("x-grid3-hd-over");                 this.activeHdBtn = fly.child('.x-grid3-hd-btn');                 if(this.activeHdBtn){                     this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';                 }             }         }     },     // private     handleHdMove : function(e, t){         if(this.activeHd && !this.headersDisabled){             var hw = this.splitHandleWidth || 5;             var r = this.activeHdRegion;             var x = e.getPageX();             var ss = this.activeHd.style;             if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){                 ss.cursor = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize not always supported             }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){                 ss.cursor = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';             }else{                 ss.cursor = '';             }         }     },     // private     handleHdOut : function(e, t){         var hd = this.findHeaderCell(t);         if(hd && (!Ext.isIE || !e.within(hd, true))){             this.activeHd = null;             this.fly(hd).removeClass("x-grid3-hd-over");             hd.style.cursor = '';         }     },     // private     hasRows : function(){         var fc = this.mainBody.dom.firstChild;         return fc && fc.nodeType == 1 && fc.className != 'x-grid-empty';     },     // back compat     bind : function(d, c){         this.initData(d, c);     } }); // private // This is a support class used internally by the Grid components Ext.grid.GridView.SplitDragZone = function(grid, hd){     this.grid = grid;     this.view = grid.getView();     this.marker = this.view.resizeMarker;     this.proxy = this.view.resizeProxy;     Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,         "gridSplitters" + this.grid.getGridEl().id, {         dragElId : Ext.id(this.proxy.dom), resizeFrame:false     });     this.scroll = false;     this.hw = this.view.splitHandleWidth || 5; }; Ext.extend(Ext.grid.GridView.SplitDragZone, Ext.dd.DDProxy, {     b4StartDrag : function(x, y){         this.view.headersDisabled = true;         var h = this.view.mainWrap.getHeight();         this.marker.setHeight(h);         this.marker.show();         this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);         this.proxy.setHeight(h);         var w = this.cm.getColumnWidth(this.cellIndex);         var minw = Math.max(w-this.grid.minColumnWidth, 0);         this.resetConstraints();         this.setXConstraint(minw, 1000);         this.setYConstraint(0, 0);         this.minX = x - minw;         this.maxX = x + 1000;         this.startPos = x;         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);     },     handleMouseDown : function(e){         var t = this.view.findHeaderCell(e.getTarget());         if(t){             var xy = this.view.fly(t).getXY(), x = xy[0], y = xy[1];             var exy = e.getXY(), ex = exy[0];             var w = t.offsetWidth, adjust = false;             if((ex - x) <= this.hw){                 adjust = -1;             }else if((x+w) - ex <= this.hw){                 adjust = 0;             }             if(adjust !== false){                 this.cm = this.grid.colModel;                 var ci = this.view.getCellIndex(t);                 if(adjust == -1){                   if (ci + adjust < 0) {                     return;                   }                     while(this.cm.isHidden(ci+adjust)){                         --adjust;                         if(ci+adjust < 0){                             return;                         }                     }                 }                 this.cellIndex = ci+adjust;                 this.split = t.dom;                 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){                     Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);                 }             }else if(this.view.columnDrag){                 this.view.columnDrag.callHandleMouseDown(e);             }         }     },     endDrag : function(e){         this.marker.hide();         var v = this.view;         var endX = Math.max(this.minX, e.getPageX());         var diff = endX - this.startPos;         v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);         setTimeout(function(){             v.headersDisabled = false;         }, 50);     },     autoOffset : function(){         this.setDelta(0,0);     } }); // private
  373. // This is a support class used internally by the Grid components
  374. Ext.grid.HeaderDragZone = function(grid, hd, hd2){
  375.     this.grid = grid;
  376.     this.view = grid.getView();
  377.     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
  378.     Ext.grid.HeaderDragZone.superclass.constructor.call(this, hd);
  379.     if(hd2){
  380.         this.setHandleElId(Ext.id(hd));
  381.         this.setOuterHandleElId(Ext.id(hd2));
  382.     }
  383.     this.scroll = false;
  384. };
  385. Ext.extend(Ext.grid.HeaderDragZone, Ext.dd.DragZone, {
  386.     maxDragWidth: 120,
  387.     getDragData : function(e){
  388.         var t = Ext.lib.Event.getTarget(e);
  389.         var h = this.view.findHeaderCell(t);
  390.         if(h){
  391.             return {ddel: h.firstChild, header:h};
  392.         }
  393.         return false;
  394.     },
  395.     onInitDrag : function(e){
  396.         this.view.headersDisabled = true;
  397.         var clone = this.dragData.ddel.cloneNode(true);
  398.         clone.id = Ext.id();
  399.         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
  400.         this.proxy.update(clone);
  401.         return true;
  402.     },
  403.     afterValidDrop : function(){
  404.         var v = this.view;
  405.         setTimeout(function(){
  406.             v.headersDisabled = false;
  407.         }, 50);
  408.     },
  409.     afterInvalidDrop : function(){
  410.         var v = this.view;
  411.         setTimeout(function(){
  412.             v.headersDisabled = false;
  413.         }, 50);
  414.     }
  415. });
  416. // private
  417. // This is a support class used internally by the Grid components
  418. Ext.grid.HeaderDropZone = function(grid, hd, hd2){
  419.     this.grid = grid;
  420.     this.view = grid.getView();
  421.     // split the proxies so they don't interfere with mouse events
  422.     this.proxyTop = Ext.DomHelper.append(document.body, {
  423.         cls:"col-move-top", html:"&#160;"
  424.     }, true);
  425.     this.proxyBottom = Ext.DomHelper.append(document.body, {
  426.         cls:"col-move-bottom", html:"&#160;"
  427.     }, true);
  428.     this.proxyTop.hide = this.proxyBottom.hide = function(){
  429.         this.setLeftTop(-100,-100);
  430.         this.setStyle("visibility", "hidden");
  431.     };
  432.     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
  433.     // temporarily disabled
  434.     //Ext.dd.ScrollManager.register(this.view.scroller.dom);
  435.     Ext.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
  436. };
  437. Ext.extend(Ext.grid.HeaderDropZone, Ext.dd.DropZone, {
  438.     proxyOffsets : [-4, -9],
  439.     fly: Ext.Element.fly,
  440.     getTargetFromEvent : function(e){
  441.         var t = Ext.lib.Event.getTarget(e);
  442.         var cindex = this.view.findCellIndex(t);
  443.         if(cindex !== false){
  444.             return this.view.getHeaderCell(cindex);
  445.         }
  446.     },
  447.     nextVisible : function(h){
  448.         var v = this.view, cm = this.grid.colModel;
  449.         h = h.nextSibling;
  450.         while(h){
  451.             if(!cm.isHidden(v.getCellIndex(h))){
  452.                 return h;
  453.             }
  454.             h = h.nextSibling;
  455.         }
  456.         return null;
  457.     },
  458.     prevVisible : function(h){
  459.         var v = this.view, cm = this.grid.colModel;
  460.         h = h.prevSibling;
  461.         while(h){
  462.             if(!cm.isHidden(v.getCellIndex(h))){
  463.                 return h;
  464.             }
  465.             h = h.prevSibling;
  466.         }
  467.         return null;
  468.     },
  469.     positionIndicator : function(h, n, e){
  470.         var x = Ext.lib.Event.getPageX(e);
  471.         var r = Ext.lib.Dom.getRegion(n.firstChild);
  472.         var px, pt, py = r.top + this.proxyOffsets[1];
  473.         if((r.right - x) <= (r.right-r.left)/2){
  474.             px = r.right+this.view.borderWidth;
  475.             pt = "after";
  476.         }else{
  477.             px = r.left;
  478.             pt = "before";
  479.         }
  480.         if(this.grid.colModel.isFixed(this.view.getCellIndex(n))){
  481.             return false;
  482.         }
  483.         px +=  this.proxyOffsets[0];
  484.         this.proxyTop.setLeftTop(px, py);
  485.         this.proxyTop.show();
  486.         if(!this.bottomOffset){
  487.             this.bottomOffset = this.view.mainHd.getHeight();
  488.         }
  489.         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
  490.         this.proxyBottom.show();
  491.         return pt;
  492.     },
  493.     onNodeEnter : function(n, dd, e, data){
  494.         if(data.header != n){
  495.             this.positionIndicator(data.header, n, e);
  496.         }
  497.     },
  498.     onNodeOver : function(n, dd, e, data){
  499.         var result = false;
  500.         if(data.header != n){
  501.             result = this.positionIndicator(data.header, n, e);
  502.         }
  503.         if(!result){
  504.             this.proxyTop.hide();
  505.             this.proxyBottom.hide();
  506.         }
  507.         return result ? this.dropAllowed : this.dropNotAllowed;
  508.     },
  509.     onNodeOut : function(n, dd, e, data){
  510.         this.proxyTop.hide();
  511.         this.proxyBottom.hide();
  512.     },
  513.     onNodeDrop : function(n, dd, e, data){
  514.         var h = data.header;
  515.         if(h != n){
  516.             var cm = this.grid.colModel;
  517.             var x = Ext.lib.Event.getPageX(e);
  518.             var r = Ext.lib.Dom.getRegion(n.firstChild);
  519.             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
  520.             var oldIndex = this.view.getCellIndex(h);
  521.             var newIndex = this.view.getCellIndex(n);
  522.             if(pt == "after"){
  523.                 newIndex++;
  524.             }
  525.             if(oldIndex < newIndex){
  526.                 newIndex--;
  527.             }
  528.             cm.moveColumn(oldIndex, newIndex);
  529.             this.grid.fireEvent("columnmove", oldIndex, newIndex);
  530.             return true;
  531.         }
  532.         return false;
  533.     }
  534. });
  535. Ext.grid.GridView.ColumnDragZone = function(grid, hd){
  536.     Ext.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
  537.     this.proxy.el.addClass('x-grid3-col-dd');
  538. };
  539. Ext.extend(Ext.grid.GridView.ColumnDragZone, Ext.grid.HeaderDragZone, {
  540.     handleMouseDown : function(e){
  541.     },
  542.     callHandleMouseDown : function(e){
  543.         Ext.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
  544.     }
  545. });// private // This is a support class used internally by the Grid components Ext.grid.SplitDragZone = function(grid, hd, hd2){     this.grid = grid;     this.view = grid.getView();     this.proxy = this.view.resizeProxy;     Ext.grid.SplitDragZone.superclass.constructor.call(this, hd,         "gridSplitters" + this.grid.getGridEl().id, {         dragElId : Ext.id(this.proxy.dom), resizeFrame:false     });     this.setHandleElId(Ext.id(hd));     this.setOuterHandleElId(Ext.id(hd2));     this.scroll = false; }; Ext.extend(Ext.grid.SplitDragZone, Ext.dd.DDProxy, {     fly: Ext.Element.fly,     b4StartDrag : function(x, y){         this.view.headersDisabled = true;         this.proxy.setHeight(this.view.mainWrap.getHeight());         var w = this.cm.getColumnWidth(this.cellIndex);         var minw = Math.max(w-this.grid.minColumnWidth, 0);         this.resetConstraints();         this.setXConstraint(minw, 1000);         this.setYConstraint(0, 0);         this.minX = x - minw;         this.maxX = x + 1000;         this.startPos = x;         Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);     },     handleMouseDown : function(e){         var ev = Ext.EventObject.setEvent(e);         var t = this.fly(ev.getTarget());         if(t.hasClass("x-grid-split")){             this.cellIndex = this.view.getCellIndex(t.dom);             this.split = t.dom;             this.cm = this.grid.colModel;             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){                 Ext.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);             }         }     },     endDrag : function(e){         this.view.headersDisabled = false;         var endX = Math.max(this.minX, Ext.lib.Event.getPageX(e));         var diff = endX - this.startPos;         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);     },     autoOffset : function(){         this.setDelta(0,0);     } });/**  * @class Ext.grid.GridDragZone  * @extends Ext.dd.DragZone  * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of two of the  * template methods of DragZone to enable dragging of the selected rows of a GridPanel.</p>  * <p>A cooperating {@link Ext.dd.DropZone DropZone} must be created who's template method implementations of  * {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},  * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop}</p> are able  * to process the {@link #getDragData data} which is provided.  */ Ext.grid.GridDragZone = function(grid, config){     this.view = grid.getView();     Ext.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);     this.scroll = false;     this.grid = grid;     this.ddel = document.createElement('div');     this.ddel.className = 'x-grid-dd-wrap'; }; Ext.extend(Ext.grid.GridDragZone, Ext.dd.DragZone, {     ddGroup : "GridDD",     /**      * <p>The provided implementation of the getDragData method which collects the data to be dragged from the GridPanel on mousedown.</p>      * <p>This data is available for processing in the {@link Ext.dd.DropZone#onNodeEnter onNodeEnter}, {@link Ext.dd.DropZone#onNodeOver onNodeOver},      * {@link Ext.dd.DropZone#onNodeOut onNodeOut} and {@link Ext.dd.DropZone#onNodeDrop onNodeDrop} methods of a cooperating {@link Ext.dd.DropZone DropZone}.</p>      * <p>The data object contains the following properties:<ul>      * <li><b>grid</b> : Ext.Grid.GridPanel<div class="sub-desc">The GridPanel from which the data is being dragged.</div></li>      * <li><b>ddel</b> : htmlElement<div class="sub-desc">An htmlElement which provides the "picture" of the data being dragged.</div></li>      * <li><b>rowIndex</b> : Number<div class="sub-desc">The index of the row which receieved the mousedown gesture which triggered the drag.</div></li>      * <li><b>selections</b> : Array<div class="sub-desc">An Array of the selected Records which are being dragged from the GridPanel.</div></li>      * </ul></p>      */     getDragData : function(e){         var t = Ext.lib.Event.getTarget(e);         var rowIndex = this.view.findRowIndex(t);         if(rowIndex !== false){             var sm = this.grid.selModel;             if(!sm.isSelected(rowIndex) || e.hasModifier()){                 sm.handleMouseDown(this.grid, rowIndex, e);             }             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};         }         return false;     },     /**      * <p>The provided implementation of the onInitDrag method. Sets the <tt>innerHTML</tt> of the drag proxy which provides the "picture"      * of the data being dragged.</p>      * <p>The <tt>innerHTML</tt> data is found by calling the owning GridPanel's {@link Ext.grid.GridPanel#getDragDropText getDragDropText}.</p>      */     onInitDrag : function(e){         var data = this.dragData;         this.ddel.innerHTML = this.grid.getDragDropText();         this.proxy.update(this.ddel);         // fire start drag?     },     /**      * An empty immplementation. Implement this to provide behaviour after a repair of an invalid drop. An implementation might highlight      * the selected rows to show that they have not been dragged.      */     afterRepair : function(){         this.dragging = false;     },     /**      * <p>An empty implementation. Implement this to provide coordinates for the drag proxy to slide back to after an invalid drop.</p>      * <p>Called before a repair of an invalid drop to get the XY to animate to.</p>      * @param {EventObject} e The mouse up event      * @return {Array} The xy location (e.g. [100, 200])      */     getRepairXY : function(e, data){         return false;     },     onEndDrag : function(data, e){         // fire end drag?     },     onValidDrop : function(dd, e, id){         // fire drag drop?         this.hideProxy();     },     beforeInvalidDrop : function(e, id){     } }); /**  * @class Ext.grid.ColumnModel  * @extends Ext.util.Observable  * <p>After the data has been read into the client side cache (<b>{@link Ext.data.Store Store}</b>),  * the ColumnModel is used to configure how and what parts of that data will be displayed in the  * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation  * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.</p>  * <p>Data is mapped into the store's records and then indexed into the ColumnModel using the  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt>:</p>  * <pre><code> {data source} == mapping ==> {data store} == <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> ==> {ColumnModel}  * </code></pre>  * <p>Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a  * <tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt> to specify how the data within  * each record in the store is indexed into the ColumnModel.</p>  * <p>There are two ways to initialize the ColumnModel class:</p>  * <p><u>Initialization Method 1: an Array</u></p> <pre><code>  var colModel = new Ext.grid.ColumnModel([     { header: "Ticker", width: 60, sortable: true},     { header: "Company Name", width: 150, sortable: true, id: 'company'},     { header: "Market Cap.", width: 100, sortable: true},     { header: "$ Sales", width: 100, sortable: true, renderer: money},     { header: "Employees", width: 100, sortable: true, resizable: false}  ]);  </code></pre>  * <p>The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration  * objects to define the initial layout / display of the columns in the Grid. The order of each  * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial  * order of the column display.  A Column's display may be initially hidden using the  * <tt>{@link Ext.grid.Column#hidden hidden}</tt></b> config property (and then shown using the column  * header menu).  Field's that are not included in the ColumnModel will not be displayable at all.</p>  * <p>How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the  * {@link Ext.data.Store Store} the column draws its data from is configured through the  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b>.  If the  * <b><tt>{@link Ext.grid.Column#dataIndex dataIndex}</tt></b> is not explicitly defined (as shown in the  * example above) it will use the column configuration's index in the Array as the index.</p>  * <p>See <b><tt>{@link Ext.grid.Column}</tt></b> for additional configuration options for each column.</p>  * <p><u>Initialization Method 2: an Object</u></p>  * <p>In order to use configuration options from <tt>Ext.grid.ColumnModel</tt>, an Object may be used to  * initialize the ColumnModel.  The column configuration Array will be specified in the <tt><b>{@link #columns}</b></tt>  * config property. The <tt><b>{@link #defaults}</b></tt> config property can be used to apply defaults  * for all columns, e.g.:</p><pre><code>  var colModel = new Ext.grid.ColumnModel({     columns: [         { header: "Ticker", width: 60, menuDisabled: false},         { header: "Company Name", width: 150, id: 'company'},         { header: "Market Cap."},         { header: "$ Sales", renderer: money},         { header: "Employees", resizable: false}     ],     defaults: {         sortable: true,         menuDisabled: true,         width: 100     },     listeners: {         {@link #hiddenchange}: function(cm, colIndex, hidden) {             saveConfig(colIndex, hidden);         }     } });  </code></pre>  * <p>In both examples above, the ability to apply a CSS class to all cells in a column (including the  * header) is demonstrated through the use of the <b><tt>{@link Ext.grid.Column#id id}</tt></b> config  * option. This column could be styled by including the following css:</p><pre><code>  //add this css *after* the core css is loaded .x-grid3-td-company {     color: red; // entire column will have red font } // modify the header row only, adding an icon to the column header .x-grid3-hd-company {     background: transparent         url(../../resources/images/icons/silk/building.png)         no-repeat 3px 3px ! important;         padding-left:20px; }  </code></pre>  * Note that the "Company Name" column could be specified as the  * <b><tt>{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></b>.  * @constructor  * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify  * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).  */ Ext.grid.ColumnModel = function(config){     /**      * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration      * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may      * be specified.      * @property config      * @type Array      */     if(config.columns){         Ext.apply(this, config);         this.setConfig(config.columns, true);     }else{         this.setConfig(config, true);     }     this.addEvents(         /**          * @event widthchange          * Fires when the width of a column is programmaticially changed using          * <code>{@link #setColumnWidth}</code>.          * Note internal resizing suppresses the event from firing. See also          * {@link Ext.grid.GridPanel}.<code>{@link #columnresize}</code>.          * @param {ColumnModel} this          * @param {Number} columnIndex The column index          * @param {Number} newWidth The new width          */         "widthchange",         /**          * @event headerchange          * Fires when the text of a header changes.          * @param {ColumnModel} this          * @param {Number} columnIndex The column index          * @param {String} newText The new header text          */         "headerchange",         /**          * @event hiddenchange          * Fires when a column is hidden or "unhidden".          * @param {ColumnModel} this          * @param {Number} columnIndex The column index          * @param {Boolean} hidden true if hidden, false otherwise          */         "hiddenchange",         /**          * @event columnmoved          * Fires when a column is moved.          * @param {ColumnModel} this          * @param {Number} oldIndex          * @param {Number} newIndex          */         "columnmoved",         /**          * @event configchange          * Fires when the configuration is changed          * @param {ColumnModel} this          */         "configchange"     );     Ext.grid.ColumnModel.superclass.constructor.call(this); }; Ext.extend(Ext.grid.ColumnModel, Ext.util.Observable, {     /**      * @cfg {Number} defaultWidth (optional) The width of columns which have no <tt>{@link #width}</tt>      * specified (defaults to <tt>100</tt>).  This property shall preferably be configured through the      * <tt><b>{@link #defaults}</b></tt> config property.      */     defaultWidth: 100,     /**      * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no      * sortable specified (defaults to <tt>false</tt>).  This property shall preferably be configured      * through the <tt><b>{@link #defaults}</b></tt> config property.      */     defaultSortable: false,     /**      * @cfg {Array} columns An Array of object literals.  The config options defined by      * <b>{@link Ext.grid.Column}</b> are the options which may appear in the object literal for each      * individual column definition.      */     /**      * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}      * configuration options to all <tt><b>{@link #columns}</b></tt>.  Configuration options specified with      * individual {@link Ext.grid.Column column} configs will supersede these <tt><b>{@link #defaults}</b></tt>.      */     /**      * Returns the id of the column at the specified index.      * @param {Number} index The column index      * @return {String} the id      */     getColumnId : function(index){         return this.config[index].id;     },     getColumnAt : function(index){         return this.config[index];     },     /**      * <p>Reconfigures this column model according to the passed Array of column definition objects.      * For a description of the individual properties of a column definition object, see the      * <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}      * using this ColumnModel will listen for this event and refresh its UI automatically.</p>      * @param {Array} config Array of Column definition objects.      * @param {Boolean} initial Specify <tt>true</tt> to bypass cleanup which deletes the <tt>totalWidth</tt>      * and destroys existing editors.      */     setConfig : function(config, initial){         var i, c, len;         if(!initial){ // cleanup             delete this.totalWidth;             for(i = 0, len = this.config.length; i < len; i++){                 c = this.config[i];                 if(c.editor){                     c.editor.destroy();                 }             }         }         // backward compatibility         this.defaults = Ext.apply({             width: this.defaultWidth,             sortable: this.defaultSortable         }, this.defaults);         this.config = config;         this.lookup = {};         // if no id, create one         for(i = 0, len = config.length; i < len; i++){             c = Ext.applyIf(config[i], this.defaults);             if(!c.isColumn){                 var cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];                 c = new cls(c);                 config[i] = c;             }             this.lookup[c.id] = c;         }         if(!initial){             this.fireEvent('configchange', this);         }     },     /**      * Returns the column for a specified id.      * @param {String} id The column id      * @return {Object} the column      */     getColumnById : function(id){         return this.lookup[id];     },     /**      * Returns the index for a specified column id.      * @param {String} id The column id      * @return {Number} the index, or -1 if not found      */     getIndexById : function(id){         for(var i = 0, len = this.config.length; i < len; i++){             if(this.config[i].id == id){                 return i;             }         }         return -1;     },     /**      * Moves a column from one position to another.      * @param {Number} oldIndex The index of the column to move.      * @param {Number} newIndex The position at which to reinsert the coolumn.      */     moveColumn : function(oldIndex, newIndex){         var c = this.config[oldIndex];         this.config.splice(oldIndex, 1);         this.config.splice(newIndex, 0, c);         this.dataMap = null;         this.fireEvent("columnmoved", this, oldIndex, newIndex);     },     /**      * Returns the number of columns.      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.      * @return {Number}      */     getColumnCount : function(visibleOnly){         if(visibleOnly === true){             var c = 0;             for(var i = 0, len = this.config.length; i < len; i++){                 if(!this.isHidden(i)){                     c++;                 }             }             return c;         }         return this.config.length;     },     /**      * Returns the column configs that return true by the passed function that is called      * with (columnConfig, index) <pre><code> // returns an array of column config objects for all hidden columns var columns = grid.getColumnModel().getColumnsBy(function(c){   return c.hidden; }); </code></pre>      * @param {Function} fn      * @param {Object} scope (optional)      * @return {Array} result      */     getColumnsBy : function(fn, scope){         var r = [];         for(var i = 0, len = this.config.length; i < len; i++){             var c = this.config[i];             if(fn.call(scope||this, c, i) === true){                 r[r.length] = c;             }         }         return r;     },     /**      * Returns true if the specified column is sortable.      * @param {Number} col The column index      * @return {Boolean}      */     isSortable : function(col){         return this.config[col].sortable;     },     /**      * Returns true if the specified column menu is disabled.      * @param {Number} col The column index      * @return {Boolean}      */     isMenuDisabled : function(col){         return !!this.config[col].menuDisabled;     },     /**      * Returns the rendering (formatting) function defined for the column.      * @param {Number} col The column index.      * @return {Function} The function used to render the cell. See {@link #setRenderer}.      */     getRenderer : function(col){         if(!this.config[col].renderer){             return Ext.grid.ColumnModel.defaultRenderer;         }         return this.config[col].renderer;     },     /**      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some      * default formatting functions.      * @param {Number} col The column index      * @param {Function} fn The function to use to process the cell's raw data      * to return HTML markup for the grid view. The render function is called with      * the following parameters:<ul>      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell      * (e.g. 'style="color:red;"').</p></li></ul></p></li>      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>      */     setRenderer : function(col, fn){         this.config[col].renderer = fn;     },     /**      * Returns the width for the specified column.      * @param {Number} col The column index      * @return {Number}      */     getColumnWidth : function(col){         return this.config[col].width;     },     /**      * Sets the width for a column.      * @param {Number} col The column index      * @param {Number} width The new width      * @param {Boolean} suppressEvent True to suppress firing the <code>{@link #widthchange}</code>      * event. Defaults to false.      */     setColumnWidth : function(col, width, suppressEvent){         this.config[col].width = width;         this.totalWidth = null;         if(!suppressEvent){              this.fireEvent("widthchange", this, col, width);         }     },     /**      * Returns the total width of all columns.      * @param {Boolean} includeHidden True to include hidden column widths      * @return {Number}      */     getTotalWidth : function(includeHidden){         if(!this.totalWidth){             this.totalWidth = 0;             for(var i = 0, len = this.config.length; i < len; i++){                 if(includeHidden || !this.isHidden(i)){                     this.totalWidth += this.getColumnWidth(i);                 }             }         }         return this.totalWidth;     },     /**      * Returns the header for the specified column.      * @param {Number} col The column index      * @return {String}      */     getColumnHeader : function(col){         return this.config[col].header;     },     /**      * Sets the header for a column.      * @param {Number} col The column index      * @param {String} header The new header      */     setColumnHeader : function(col, header){         this.config[col].header = header;         this.fireEvent("headerchange", this, col, header);     },     /**      * Returns the tooltip for the specified column.      * @param {Number} col The column index      * @return {String}      */     getColumnTooltip : function(col){             return this.config[col].tooltip;     },     /**      * Sets the tooltip for a column.      * @param {Number} col The column index      * @param {String} tooltip The new tooltip      */     setColumnTooltip : function(col, tooltip){             this.config[col].tooltip = tooltip;     },     /**      * Returns the dataIndex for the specified column. <pre><code> // Get field name for the column var fieldName = grid.getColumnModel().getDataIndex(columnIndex); </code></pre>      * @param {Number} col The column index      * @return {String} The column's dataIndex      */     getDataIndex : function(col){         return this.config[col].dataIndex;     },     /**      * Sets the dataIndex for a column.      * @param {Number} col The column index      * @param {String} dataIndex The new dataIndex      */     setDataIndex : function(col, dataIndex){         this.config[col].dataIndex = dataIndex;     },     /**      * Finds the index of the first matching column for the given dataIndex.      * @param {String} col The dataIndex to find      * @return {Number} The column index, or -1 if no match was found      */     findColumnIndex : function(dataIndex){         var c = this.config;         for(var i = 0, len = c.length; i < len; i++){             if(c[i].dataIndex == dataIndex){                 return i;             }         }         return -1;     },     /**      * Returns true if the cell is editable. <pre><code> var store = new Ext.data.Store({...}); var colModel = new Ext.grid.ColumnModel({   columns: [...],   isCellEditable: function(col, row) {     var record = store.getAt(row);     if (record.get('readonly')) { // replace with your condition       return false;     }     return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);   } }); var grid = new Ext.grid.GridPanel({   store: store,   colModel: colModel,   ... }); </code></pre>      * @param {Number} colIndex The column index      * @param {Number} rowIndex The row index      * @return {Boolean}      */     isCellEditable : function(colIndex, rowIndex){         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;     },     /**      * Returns the editor defined for the cell/column.      * @param {Number} colIndex The column index      * @param {Number} rowIndex The row index      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap      * the {@link Ext.form.Field Field} used to edit the cell.      */     getCellEditor : function(colIndex, rowIndex){         return this.config[colIndex].getCellEditor(rowIndex);     },     /**      * Sets if a column is editable.      * @param {Number} col The column index      * @param {Boolean} editable True if the column is editable      */     setEditable : function(col, editable){         this.config[col].editable = editable;     },     /**      * Returns true if the column is hidden.      * @param {Number} colIndex The column index      * @return {Boolean}      */     isHidden : function(colIndex){         return this.config[colIndex].hidden;     },     /**      * Returns true if the column width cannot be changed      */     isFixed : function(colIndex){         return this.config[colIndex].fixed;     },     /**      * Returns true if the column can be resized      * @return {Boolean}      */     isResizable : function(colIndex){         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;     },     /**      * Sets if a column is hidden. <pre><code> myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column). </code></pre>      * @param {Number} colIndex The column index      * @param {Boolean} hidden True if the column is hidden      */     setHidden : function(colIndex, hidden){         var c = this.config[colIndex];         if(c.hidden !== hidden){             c.hidden = hidden;             this.totalWidth = null;             this.fireEvent("hiddenchange", this, colIndex, hidden);         }     },     /**      * Sets the editor for a column and destroys the prior editor.      * @param {Number} col The column index      * @param {Object} editor The editor object      */     setEditor : function(col, editor){         Ext.destroy(this.config[col].editor);         this.config[col].editor = editor;     },     /**      * Destroys this column model by purging any event listeners, and removing any editors.      */     destroy : function(){         for(var i = 0, c = this.config, len = c.length; i < len; i++){             Ext.destroy(c[i].editor);         }         this.purgeListeners();     } }); // private Ext.grid.ColumnModel.defaultRenderer = function(value){     if(typeof value == "string" && value.length < 1){         return "&#160;";     }     return value; };/**
  546.  * @class Ext.grid.AbstractSelectionModel
  547.  * @extends Ext.util.Observable
  548.  * Abstract base class for grid SelectionModels.  It provides the interface that should be
  549.  * implemented by descendant classes.  This class should not be directly instantiated.
  550.  * @constructor
  551.  */
  552. Ext.grid.AbstractSelectionModel = function(){
  553.     this.locked = false;
  554.     Ext.grid.AbstractSelectionModel.superclass.constructor.call(this);
  555. };
  556. Ext.extend(Ext.grid.AbstractSelectionModel, Ext.util.Observable,  {
  557.     /**
  558.      * The GridPanel for which this SelectionModel is handling selection. Read-only.
  559.      * @type Object
  560.      * @property grid
  561.      */
  562.     /** @ignore Called by the grid automatically. Do not call directly. */
  563.     init : function(grid){
  564.         this.grid = grid;
  565.         this.initEvents();
  566.     },
  567.     /**
  568.      * Locks the selections.
  569.      */
  570.     lock : function(){
  571.         this.locked = true;
  572.     },
  573.     /**
  574.      * Unlocks the selections.
  575.      */
  576.     unlock : function(){
  577.         this.locked = false;
  578.     },
  579.     /**
  580.      * Returns true if the selections are locked.
  581.      * @return {Boolean}
  582.      */
  583.     isLocked : function(){
  584.         return this.locked;
  585.     },
  586.     
  587.     destroy: function(){
  588.         this.purgeListeners();
  589.     }
  590. });/**  * @class Ext.grid.RowSelectionModel  * @extends Ext.grid.AbstractSelectionModel  * The default SelectionModel used by {@link Ext.grid.GridPanel}.  * It supports multiple selections and keyboard selection/navigation. The objects stored  * as selections and returned by {@link #getSelected}, and {@link #getSelections} are  * the {@link Ext.data.Record Record}s which provide the data for the selected rows.  * @constructor  * @param {Object} config  */ Ext.grid.RowSelectionModel = function(config){     Ext.apply(this, config);     this.selections = new Ext.util.MixedCollection(false, function(o){         return o.id;     });     this.last = false;     this.lastActive = false;     this.addEvents(         /**          * @event selectionchange          * Fires when the selection changes          * @param {SelectionModel} this          */         "selectionchange",         /**          * @event beforerowselect          * Fires before a row is selected, return false to cancel the selection.          * @param {SelectionModel} this          * @param {Number} rowIndex The index to be selected          * @param {Boolean} keepExisting False if other selections will be cleared          * @param {Record} record The record to be selected          */         "beforerowselect",         /**          * @event rowselect          * Fires when a row is selected.          * @param {SelectionModel} this          * @param {Number} rowIndex The selected index          * @param {Ext.data.Record} r The selected record          */         "rowselect",         /**          * @event rowdeselect          * Fires when a row is deselected.  To prevent deselection          * {@link Ext.grid.AbstractSelectionModel#lock lock the selections}.           * @param {SelectionModel} this          * @param {Number} rowIndex          * @param {Record} record          */         "rowdeselect"     );     Ext.grid.RowSelectionModel.superclass.constructor.call(this); }; Ext.extend(Ext.grid.RowSelectionModel, Ext.grid.AbstractSelectionModel,  {     /**      * @cfg {Boolean} singleSelect      * <tt>true</tt> to allow selection of only one row at a time (defaults to <tt>false</tt>      * allowing multiple selections)      */     singleSelect : false,     /**      * @cfg {Boolean} moveEditorOnEnter      * <tt>false</tt> to turn off moving the editor to the next row down when the enter key is pressed      * or the next row up when shift + enter keys are pressed.      */     // private     initEvents : function(){         if(!this.grid.enableDragDrop && !this.grid.enableDrag){             this.grid.on("rowmousedown", this.handleMouseDown, this);         }else{ // allow click to work like normal             this.grid.on("rowclick", function(grid, rowIndex, e) {                 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {                     this.selectRow(rowIndex, false);                     grid.view.focusRow(rowIndex);                 }             }, this);         }         this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {             "up" : function(e){                 if(!e.shiftKey || this.singleSelect){                     this.selectPrevious(false);                 }else if(this.last !== false && this.lastActive !== false){                     var last = this.last;                     this.selectRange(this.last,  this.lastActive-1);                     this.grid.getView().focusRow(this.lastActive);                     if(last !== false){                         this.last = last;                     }                 }else{                     this.selectFirstRow();                 }             },             "down" : function(e){                 if(!e.shiftKey || this.singleSelect){                     this.selectNext(false);                 }else if(this.last !== false && this.lastActive !== false){                     var last = this.last;                     this.selectRange(this.last,  this.lastActive+1);                     this.grid.getView().focusRow(this.lastActive);                     if(last !== false){                         this.last = last;                     }                 }else{                     this.selectFirstRow();                 }             },             scope: this         });         var view = this.grid.view;         view.on("refresh", this.onRefresh, this);         view.on("rowupdated", this.onRowUpdated, this);         view.on("rowremoved", this.onRemove, this);     },     // private     onRefresh : function(){         var ds = this.grid.store, index;         var s = this.getSelections();         this.clearSelections(true);         for(var i = 0, len = s.length; i < len; i++){             var r = s[i];             if((index = ds.indexOfId(r.id)) != -1){                 this.selectRow(index, true);             }         }         if(s.length != this.selections.getCount()){             this.fireEvent("selectionchange", this);         }     },     // private     onRemove : function(v, index, r){         if(this.selections.remove(r) !== false){             this.fireEvent('selectionchange', this);         }     },     // private     onRowUpdated : function(v, index, r){         if(this.isSelected(r)){             v.onRowSelect(index);         }     },     /**      * Select records.      * @param {Array} records The records to select      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections      */     selectRecords : function(records, keepExisting){         if(!keepExisting){             this.clearSelections();         }         var ds = this.grid.store;         for(var i = 0, len = records.length; i < len; i++){             this.selectRow(ds.indexOf(records[i]), true);         }     },     /**      * Gets the number of selected rows.      * @return {Number}      */     getCount : function(){         return this.selections.length;     },     /**      * Selects the first row in the grid.      */     selectFirstRow : function(){         this.selectRow(0);     },     /**      * Select the last row.      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections      */     selectLastRow : function(keepExisting){         this.selectRow(this.grid.store.getCount() - 1, keepExisting);     },     /**      * Selects the row immediately following the last selected row.      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections      * @return {Boolean} <tt>true</tt> if there is a next row, else <tt>false</tt>      */     selectNext : function(keepExisting){         if(this.hasNext()){             this.selectRow(this.last+1, keepExisting);             this.grid.getView().focusRow(this.last);             return true;         }         return false;     },     /**      * Selects the row that precedes the last selected row.      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections      * @return {Boolean} <tt>true</tt> if there is a previous row, else <tt>false</tt>      */     selectPrevious : function(keepExisting){         if(this.hasPrevious()){             this.selectRow(this.last-1, keepExisting);             this.grid.getView().focusRow(this.last);             return true;         }         return false;     },     /**      * Returns true if there is a next record to select      * @return {Boolean}      */     hasNext : function(){         return this.last !== false && (this.last+1) < this.grid.store.getCount();     },     /**      * Returns true if there is a previous record to select      * @return {Boolean}      */     hasPrevious : function(){         return !!this.last;     },     /**      * Returns the selected records      * @return {Array} Array of selected records      */     getSelections : function(){         return [].concat(this.selections.items);     },     /**      * Returns the first selected record.      * @return {Record}      */     getSelected : function(){         return this.selections.itemAt(0);     },     /**      * Calls the passed function with each selection. If the function returns      * <tt>false</tt>, iteration is stopped and this function returns      * <tt>false</tt>. Otherwise it returns <tt>true</tt>.      * @param {Function} fn      * @param {Object} scope (optional)      * @return {Boolean} true if all selections were iterated      */     each : function(fn, scope){         var s = this.getSelections();         for(var i = 0, len = s.length; i < len; i++){             if(fn.call(scope || this, s[i], i) === false){                 return false;             }         }         return true;     },     /**      * Clears all selections if the selection model      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.      * @param {Boolean} fast (optional) <tt>true</tt> to bypass the      * conditional checks and events described in {@link #deselectRow}.      */     clearSelections : function(fast){         if(this.isLocked()){             return;         }         if(fast !== true){             var ds = this.grid.store;             var s = this.selections;             s.each(function(r){                 this.deselectRow(ds.indexOfId(r.id));             }, this);             s.clear();         }else{             this.selections.clear();         }         this.last = false;     },     /**      * Selects all rows if the selection model      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.       */     selectAll : function(){         if(this.isLocked()){             return;         }         this.selections.clear();         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){             this.selectRow(i, true);         }     },     /**      * Returns <tt>true</tt> if there is a selection.      * @return {Boolean}      */     hasSelection : function(){         return this.selections.length > 0;     },     /**      * Returns <tt>true</tt> if the specified row is selected.      * @param {Number/Record} index The record or index of the record to check      * @return {Boolean}      */     isSelected : function(index){         var r = typeof index == "number" ? this.grid.store.getAt(index) : index;         return (r && this.selections.key(r.id) ? true : false);     },     /**      * Returns <tt>true</tt> if the specified record id is selected.      * @param {String} id The id of record to check      * @return {Boolean}      */     isIdSelected : function(id){         return (this.selections.key(id) ? true : false);     },     // private     handleMouseDown : function(g, rowIndex, e){         if(e.button !== 0 || this.isLocked()){             return;         }         var view = this.grid.getView();         if(e.shiftKey && !this.singleSelect && this.last !== false){             var last = this.last;             this.selectRange(last, rowIndex, e.ctrlKey);             this.last = last; // reset the last             view.focusRow(rowIndex);         }else{             var isSelected = this.isSelected(rowIndex);             if(e.ctrlKey && isSelected){                 this.deselectRow(rowIndex);             }else if(!isSelected || this.getCount() > 1){                 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);                 view.focusRow(rowIndex);             }         }     },     /**      * Selects multiple rows.      * @param {Array} rows Array of the indexes of the row to select      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep      * existing selections (defaults to <tt>false</tt>)      */     selectRows : function(rows, keepExisting){         if(!keepExisting){             this.clearSelections();         }         for(var i = 0, len = rows.length; i < len; i++){             this.selectRow(rows[i], true);         }     },     /**      * Selects a range of rows if the selection model      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.      * All rows in between startRow and endRow are also selected.      * @param {Number} startRow The index of the first row in the range      * @param {Number} endRow The index of the last row in the range      * @param {Boolean} keepExisting (optional) True to retain existing selections      */     selectRange : function(startRow, endRow, keepExisting){         var i;         if(this.isLocked()){             return;         }         if(!keepExisting){             this.clearSelections();         }         if(startRow <= endRow){             for(i = startRow; i <= endRow; i++){                 this.selectRow(i, true);             }         }else{             for(i = startRow; i >= endRow; i--){                 this.selectRow(i, true);             }         }     },     /**      * Deselects a range of rows if the selection model      * {@link Ext.grid.AbstractSelectionModel#isLocked is not locked}.        * All rows in between startRow and endRow are also deselected.      * @param {Number} startRow The index of the first row in the range      * @param {Number} endRow The index of the last row in the range      */     deselectRange : function(startRow, endRow, preventViewNotify){         if(this.isLocked()){             return;         }         for(var i = startRow; i <= endRow; i++){             this.deselectRow(i, preventViewNotify);         }     },     /**      * Selects a row.  Before selecting a row, checks if the selection model      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked} and fires the      * {@link #beforerowselect} event.  If these checks are satisfied the row      * will be selected and followed up by  firing the {@link #rowselect} and      * {@link #selectionchange} events.      * @param {Number} row The index of the row to select      * @param {Boolean} keepExisting (optional) <tt>true</tt> to keep existing selections      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to      * prevent notifying the view (disables updating the selected appearance)      */     selectRow : function(index, keepExisting, preventViewNotify){         if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || (keepExisting && this.isSelected(index))){             return;         }         var r = this.grid.store.getAt(index);         if(r && this.fireEvent("beforerowselect", this, index, keepExisting, r) !== false){             if(!keepExisting || this.singleSelect){                 this.clearSelections();             }             this.selections.add(r);             this.last = this.lastActive = index;             if(!preventViewNotify){                 this.grid.getView().onRowSelect(index);             }             this.fireEvent("rowselect", this, index, r);             this.fireEvent("selectionchange", this);         }     },     /**      * Deselects a row.  Before deselecting a row, checks if the selection model      * {@link Ext.grid.AbstractSelectionModel#isLocked is locked}.      * If this check is satisfied the row will be deselected and followed up by      * firing the {@link #rowdeselect} and {@link #selectionchange} events.      * @param {Number} row The index of the row to deselect      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to      * prevent notifying the view (disables updating the selected appearance)      */     deselectRow : function(index, preventViewNotify){         if(this.isLocked()){             return;         }         if(this.last == index){             this.last = false;         }         if(this.lastActive == index){             this.lastActive = false;         }         var r = this.grid.store.getAt(index);         if(r){             this.selections.remove(r);             if(!preventViewNotify){                 this.grid.getView().onRowDeselect(index);             }             this.fireEvent("rowdeselect", this, index, r);             this.fireEvent("selectionchange", this);         }     },     // private     restoreLast : function(){         if(this._last){             this.last = this._last;         }     },     // private     acceptsNav : function(row, col, cm){         return !cm.isHidden(col) && cm.isCellEditable(col, row);     },     // private     onEditorKey : function(field, e){         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;         var shift = e.shiftKey;         if(k == e.TAB){             e.stopEvent();             ed.completeEdit();             if(shift){                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);             }else{                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);             }         }else if(k == e.ENTER){             e.stopEvent();             ed.completeEdit();             if(this.moveEditorOnEnter !== false){                 if(shift){                     newCell = g.walkCells(ed.row - 1, ed.col, -1, this.acceptsNav, this);                 }else{                     newCell = g.walkCells(ed.row + 1, ed.col, 1, this.acceptsNav, this);                 }             }         }else if(k == e.ESC){             ed.cancelEdit();         }         if(newCell){             g.startEditing(newCell[0], newCell[1]);         }     },          destroy: function(){         if(this.rowNav){             this.rowNav.disable();             this.rowNav = null;         }         Ext.grid.RowSelectionModel.superclass.destroy.call(this);     } });/**
  591.  * @class Ext.grid.Column
  592.  * <p>This class encapsulates column configuration data to be used in the initialization of a
  593.  * {@link Ext.grid.ColumnModel ColumnModel}.</p>
  594.  * <p>While subclasses are provided to render data in different ways, this class renders a passed
  595.  * data field unchanged and is usually used for textual columns.</p>
  596.  */
  597. Ext.grid.Column = function(config){
  598.     Ext.apply(this, config);
  599.     if(typeof this.renderer == 'string'){
  600.         this.renderer = Ext.util.Format[this.renderer];
  601.     } else if(Ext.isObject(this.renderer)){
  602.         this.scope = this.renderer.scope;
  603.         this.renderer = this.renderer.fn;
  604.     }
  605.     this.renderer = this.renderer.createDelegate(this.scope || config);
  606.     if(this.id === undefined){
  607.         this.id = ++Ext.grid.Column.AUTO_ID;
  608.     }
  609.     if(this.editor){
  610.         this.editor = Ext.create(this.editor, 'textfield');
  611.     }
  612. };
  613. Ext.grid.Column.AUTO_ID = 0;
  614. Ext.grid.Column.prototype = {
  615.     /**
  616.      * @cfg {Boolean} editable Optional. Defaults to <tt>true</tt>, enabling the configured
  617.      * <tt>{@link #editor}</tt>.  Set to <tt>false</tt> to initially disable editing on this column.
  618.      * The initial configuration may be dynamically altered using
  619.      * {@link Ext.grid.ColumnModel}.{@link Ext.grid.ColumnModel#setEditable setEditable()}.
  620.      */
  621.     /**
  622.      * @cfg {String} id Optional. A name which identifies this column (defaults to the column's initial
  623.      * ordinal position.) The <tt>id</tt> is used to create a CSS <b>class</b> name which is applied to all
  624.      * table cells (including headers) in that column (in this context the <tt>id</tt> does not need to be
  625.      * unique). The class name takes the form of <pre>x-grid3-td-<b>id</b></pre>
  626.      * Header cells will also receive this class name, but will also have the class <pre>x-grid3-hd</pre>
  627.      * So, to target header cells, use CSS selectors such as:<pre>.x-grid3-hd-row .x-grid3-td-<b>id</b></pre>
  628.      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column via this
  629.      * unique identifier.
  630.      */
  631.     /**
  632.      * @cfg {String} header Optional. The header text to be used as innerHTML
  633.      * (html tags are accepted) to display in the Grid view.  <b>Note</b>: to
  634.      * have a clickable header with no text displayed use <tt>'&#160;'</tt>.
  635.      */
  636.     /**
  637.      * @cfg {Boolean} groupable Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  638.      * may be used to disable the header menu item to group by the column selected. Defaults to <tt>true</tt>,
  639.      * which enables the header menu group option.  Set to <tt>false</tt> to disable (but still show) the
  640.      * group option in the header menu for the column. See also <code>{@link #groupName}</code>.
  641.      */
  642.     /**
  643.      * @cfg {String} groupName Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  644.      * may be used to specify the text with which to prefix the group field value in the group header line.
  645.      * See also {@link #groupRenderer} and
  646.      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#showGroupName showGroupName}.
  647.      */
  648.     /**
  649.      * @cfg {Function} groupRenderer <p>Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  650.      * may be used to specify the function used to format the grouping field value for display in the group
  651.      * {@link #groupName header}.  If a <tt><b>groupRenderer</b></tt> is not specified, the configured
  652.      * <tt><b>{@link #renderer}</b></tt> will be called; if a <tt><b>{@link #renderer}</b></tt> is also not specified
  653.      * the new value of the group field will be used.</p>
  654.      * <p>The called function (either the <tt><b>groupRenderer</b></tt> or <tt><b>{@link #renderer}</b></tt>) will be
  655.      * passed the following parameters:
  656.      * <div class="mdetail-params"><ul>
  657.      * <li><b>v</b> : Object<p class="sub-desc">The new value of the group field.</p></li>
  658.      * <li><b>unused</b> : undefined<p class="sub-desc">Unused parameter.</p></li>
  659.      * <li><b>r</b> : Ext.data.Record<p class="sub-desc">The Record providing the data
  660.      * for the row which caused group change.</p></li>
  661.      * <li><b>rowIndex</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
  662.      * <li><b>colIndex</b> : Number<p class="sub-desc">The column index of the group field.</p></li>
  663.      * <li><b>ds</b> : Ext.data.Store<p class="sub-desc">The Store which is providing the data Model.</p></li>
  664.      * </ul></div></p>
  665.      * <p>The function should return a string value.</p>
  666.      */
  667.     /**
  668.      * @cfg {String} emptyGroupText Optional. If the grid is being rendered by an {@link Ext.grid.GroupingView}, this option
  669.      * may be used to specify the text to display when there is an empty group value. Defaults to the
  670.      * {@link Ext.grid.GroupingView}.{@link Ext.grid.GroupingView#emptyGroupText emptyGroupText}.
  671.      */
  672.     /**
  673.      * @cfg {String} dataIndex <p><b>Required</b>. The name of the field in the
  674.      * grid's {@link Ext.data.Store}'s {@link Ext.data.Record} definition from
  675.      * which to draw the column's value.</p>
  676.      */
  677.     /**
  678.      * @cfg {Number} width
  679.      * Optional. The initial width in pixels of the column.
  680.      * The width of each column can also be affected if any of the following are configured:
  681.      * <div class="mdetail-params"><ul>
  682.      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}</tt></li>
  683.      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#forceFit forceFit}</tt>
  684.      * <div class="sub-desc">
  685.      * <p>By specifying <tt>forceFit:true</tt>, {@link #fixed non-fixed width} columns will be
  686.      * re-proportioned (based on the relative initial widths) to fill the width of the grid so
  687.      * that no horizontal scrollbar is shown.</p>
  688.      * </div></li>
  689.      * <li>{@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#autoFill autoFill}</tt></li>
  690.      * <li>{@link Ext.grid.GridPanel}.<tt>{@link Ext.grid.GridPanel#minColumnWidth minColumnWidth}</tt></li>
  691.      * <br><p><b>Note</b>: when the width of each column is determined, a space on the right side
  692.      * is reserved for the vertical scrollbar.  The
  693.      * {@link Ext.grid.GridView}.<tt>{@link Ext.grid.GridView#scrollOffset scrollOffset}</tt>
  694.      * can be modified to reduce or eliminate the reserved offset.</p>
  695.      */
  696.     /**
  697.      * @cfg {Boolean} sortable Optional. <tt>true</tt> if sorting is to be allowed on this column.
  698.      * Defaults to the value of the {@link #defaultSortable} property.
  699.      * Whether local/remote sorting is used is specified in {@link Ext.data.Store#remoteSort}.
  700.      */
  701.     /**
  702.      * @cfg {Boolean} fixed Optional. <tt>true</tt> if the column width cannot be changed.  Defaults to <tt>false</tt>.
  703.      */
  704.     /**
  705.      * @cfg {Boolean} resizable Optional. <tt>false</tt> to disable column resizing. Defaults to <tt>true</tt>.
  706.      */
  707.     /**
  708.      * @cfg {Boolean} menuDisabled Optional. <tt>true</tt> to disable the column menu. Defaults to <tt>false</tt>.
  709.      */
  710.     /**
  711.      * @cfg {Boolean} hidden Optional. <tt>true</tt> to hide the column. Defaults to <tt>false</tt>.
  712.      */
  713.     /**
  714.      * @cfg {String} tooltip Optional. A text string to use as the column header's tooltip.  If Quicktips
  715.      * are enabled, this value will be used as the text of the quick tip, otherwise it will be set as the
  716.      * header's HTML title attribute. Defaults to ''.
  717.      */
  718.     /**
  719.      * @cfg {Mixed} renderer
  720.      * <p>For an alternative to specifying a renderer see <code>{@link #xtype}</code></p>
  721.      * <p>Optional. A renderer is an 'interceptor' method which can be used transform data (value,
  722.      * appearance, etc.) before it is rendered). This may be specified in either of three ways:
  723.      * <div class="mdetail-params"><ul>
  724.      * <li>A renderer function used to return HTML markup for a cell given the cell's data value.</li>
  725.      * <li>A string which references a property name of the {@link Ext.util.Format} class which
  726.      * provides a renderer function.</li>
  727.      * <li>An object specifying both the renderer function, and its execution scope (<tt><b>this</b></tt>
  728.      * reference) e.g.:<pre style="margin-left:1.2em"><code>
  729. {
  730.     fn: this.gridRenderer,
  731.     scope: this
  732. }
  733. </code></pre></li></ul></div>
  734.      * If not specified, the default renderer uses the raw data value.</p>
  735.      * <p>For information about the renderer function (passed parameters, etc.), see
  736.      * {@link Ext.grid.ColumnModel#setRenderer}. An example of specifying renderer function inline:</p><pre><code>
  737. var companyColumn = {
  738.    header: 'Company Name',
  739.    dataIndex: 'company',
  740.    renderer: function(value, metaData, record, rowIndex, colIndex, store) {
  741.       // provide the logic depending on business rules
  742.       // name of your own choosing to manipulate the cell depending upon
  743.       // the data in the underlying Record object.
  744.       if (value == 'whatever') {
  745.           //metaData.css : String : A CSS class name to add to the TD element of the cell.
  746.           //metaData.attr : String : An html attribute definition string to apply to
  747.           //                         the data container element within the table
  748.           //                         cell (e.g. 'style="color:red;"').
  749.           metaData.css = 'name-of-css-class-you-will-define';
  750.       }
  751.       return value;
  752.    }
  753. }
  754.      * </code></pre>
  755.      * See also {@link #scope}.
  756.      */
  757.     /**
  758.      * @cfg {String} xtype Optional. A String which references a predefined {@link Ext.grid.Column} subclass
  759.      * type which is preconfigured with an appropriate <code>{@link #renderer}</code> to be easily
  760.      * configured into a ColumnModel. The predefined {@link Ext.grid.Column} subclass types are:
  761.      * <div class="mdetail-params"><ul>
  762.      * <li><b><tt>gridcolumn</tt></b> : {@link Ext.grid.Column} (<b>Default</b>)<p class="sub-desc"></p></li>
  763.      * <li><b><tt>booleancolumn</tt></b> : {@link Ext.grid.BooleanColumn}<p class="sub-desc"></p></li>
  764.      * <li><b><tt>numbercolumn</tt></b> : {@link Ext.grid.NumberColumn}<p class="sub-desc"></p></li>
  765.      * <li><b><tt>datecolumn</tt></b> : {@link Ext.grid.DateColumn}<p class="sub-desc"></p></li>
  766.      * <li><b><tt>templatecolumn</tt></b> : {@link Ext.grid.TemplateColumn}<p class="sub-desc"></p></li>
  767.      * </ul></div>
  768.      * <p>Configuration properties for the specified <code>xtype</code> may be specified with
  769.      * the Column configuration properties, for example:</p>
  770.      * <pre><code>
  771. var grid = new Ext.grid.GridPanel({
  772.     ...
  773.     columns: [{
  774.         header: 'Last Updated',
  775.         dataIndex: 'lastChange',
  776.         width: 85,
  777.         sortable: true,
  778.         //renderer: Ext.util.Format.dateRenderer('m/d/Y'),
  779.         xtype: 'datecolumn', // use xtype instead of renderer
  780.         format: 'M/d/Y' // configuration property for {@link Ext.grid.DateColumn}
  781.     }, {
  782.         ...
  783.     }]
  784. });
  785.      * </code></pre>
  786.      */
  787.     /**
  788.      * @cfg {Object} scope Optional. The scope (<tt><b>this</b></tt> reference) in which to execute the
  789.      * renderer.  Defaults to the Column configuration object.
  790.      */
  791.     /**
  792.      * @cfg {String} align Optional. Set the CSS text-align property of the column.  Defaults to undefined.
  793.      */
  794.     /**
  795.      * @cfg {String} css Optional. An inline style definition string which is applied to all table cells in the column
  796.      * (excluding headers). Defaults to undefined.
  797.      */
  798.     /**
  799.      * @cfg {Boolean} hideable Optional. Specify as <tt>false</tt> to prevent the user from hiding this column
  800.      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use
  801.      * {@link Ext.grid.GridPanel#enableColumnHide} instead.
  802.      */
  803.     /**
  804.      * @cfg {Ext.form.Field} editor Optional. The {@link Ext.form.Field} to use when editing values in this column
  805.      * if editing is supported by the grid. See <tt>{@link #editable}</tt> also.
  806.      */
  807.     // private. Used by ColumnModel to avoid reprocessing
  808.     isColumn : true,
  809.     /**
  810.      * Optional. A function which returns displayable data when passed the following parameters:
  811.      * <div class="mdetail-params"><ul>
  812.      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>
  813.      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>
  814.      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>
  815.      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container
  816.      * element <i>within</i> the table cell (e.g. 'style="color:red;"').</p></li></ul></p></li>
  817.      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was
  818.      * extracted.</p></li>
  819.      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>
  820.      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>
  821.      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record
  822.      * was extracted.</p></li>
  823.      * </ul></div>
  824.      * @property renderer
  825.      * @type Function
  826.      */
  827.     renderer : function(value){
  828.         if(typeof value == 'string' && value.length < 1){
  829.             return '&#160;';
  830.         }
  831.         return value;
  832.     },
  833.     // private
  834.     getEditor: function(rowIndex){
  835.         return this.editable !== false ? this.editor : null;
  836.     },
  837.     /**
  838.      * Returns the {@link Ext.Editor editor} defined for this column that was created to wrap the {@link Ext.form.Field Field}
  839.      * used to edit the cell.
  840.      * @param {Number} rowIndex The row index
  841.      * @return {Ext.Editor}
  842.      */
  843.     getCellEditor: function(rowIndex){
  844.         var editor = this.getEditor(rowIndex);
  845.         if(editor){
  846.             if(!editor.startEdit){
  847.                 if(!editor.gridEditor){
  848.                     editor.gridEditor = new Ext.grid.GridEditor(editor);
  849.                 }
  850.                 return editor.gridEditor;
  851.             }else if(editor.startEdit){
  852.                 return editor;
  853.             }
  854.         }
  855.         return null;
  856.     }
  857. };
  858. /**
  859.  * @class Ext.grid.BooleanColumn
  860.  * @extends Ext.grid.Column
  861.  * <p>A Column definition class which renders boolean data fields.  See the {@link Ext.grid.ColumnModel#xtype xtype}
  862.  * config option of {@link Ext.grid.ColumnModel} for more details.</p>
  863.  */
  864. Ext.grid.BooleanColumn = Ext.extend(Ext.grid.Column, {
  865.     /**
  866.      * @cfg {String} trueText
  867.      * The string returned by the renderer when the column value is not falsey (defaults to <tt>'true'</tt>).
  868.      */
  869.     trueText: 'true',
  870.     /**
  871.      * @cfg {String} falseText
  872.      * The string returned by the renderer when the column value is falsey (but not undefined) (defaults to
  873.      * <tt>'false'</tt>).
  874.      */
  875.     falseText: 'false',
  876.     /**
  877.      * @cfg {String} undefinedText
  878.      * The string returned by the renderer when the column value is undefined (defaults to <tt>'&#160;'</tt>).
  879.      */
  880.     undefinedText: '&#160;',
  881.     constructor: function(cfg){
  882.         Ext.grid.BooleanColumn.superclass.constructor.call(this, cfg);
  883.         var t = this.trueText, f = this.falseText, u = this.undefinedText;
  884.         this.renderer = function(v){
  885.             if(v === undefined){
  886.                 return u;
  887.             }
  888.             if(!v || v === 'false'){
  889.                 return f;
  890.             }
  891.             return t;
  892.         };
  893.     }
  894. });
  895. /**
  896.  * @class Ext.grid.NumberColumn
  897.  * @extends Ext.grid.Column
  898.  * <p>A Column definition class which renders a numeric data field according to a {@link #format} string.  See the
  899.  * {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel} for more details.</p>
  900.  */
  901. Ext.grid.NumberColumn = Ext.extend(Ext.grid.Column, {
  902.     /**
  903.      * @cfg {String} format
  904.      * A formatting string as used by {@link Ext.util.Format#number} to format a numeric value for this Column
  905.      * (defaults to <tt>'0,000.00'</tt>).
  906.      */
  907.     format : '0,000.00',
  908.     constructor: function(cfg){
  909.         Ext.grid.NumberColumn.superclass.constructor.call(this, cfg);
  910.         this.renderer = Ext.util.Format.numberRenderer(this.format);
  911.     }
  912. });
  913. /**
  914.  * @class Ext.grid.DateColumn
  915.  * @extends Ext.grid.Column
  916.  * <p>A Column definition class which renders a passed date according to the default locale, or a configured
  917.  * {@link #format}. See the {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel}
  918.  * for more details.</p>
  919.  */
  920. Ext.grid.DateColumn = Ext.extend(Ext.grid.Column, {
  921.     /**
  922.      * @cfg {String} format
  923.      * A formatting string as used by {@link Date#format} to format a Date for this Column
  924.      * (defaults to <tt>'m/d/Y'</tt>).
  925.      */
  926.     format : 'm/d/Y',
  927.     constructor: function(cfg){
  928.         Ext.grid.DateColumn.superclass.constructor.call(this, cfg);
  929.         this.renderer = Ext.util.Format.dateRenderer(this.format);
  930.     }
  931. });
  932. /**
  933.  * @class Ext.grid.TemplateColumn
  934.  * @extends Ext.grid.Column
  935.  * <p>A Column definition class which renders a value by processing a {@link Ext.data.Record Record}'s
  936.  * {@link Ext.data.Record#data data} using a {@link #tpl configured} {@link Ext.XTemplate XTemplate}.
  937.  * See the {@link Ext.grid.ColumnModel#xtype xtype} config option of {@link Ext.grid.ColumnModel} for more
  938.  * details.</p>
  939.  */
  940. Ext.grid.TemplateColumn = Ext.extend(Ext.grid.Column, {
  941.     /**
  942.      * @cfg {String/XTemplate} tpl
  943.      * An {@link Ext.XTemplate XTemplate}, or an XTemplate <i>definition string</i> to use to process a
  944.      * {@link Ext.data.Record Record}'s {@link Ext.data.Record#data data} to produce a column's rendered value.
  945.      */
  946.     constructor: function(cfg){
  947.         Ext.grid.TemplateColumn.superclass.constructor.call(this, cfg);
  948.         var tpl = typeof Ext.isObject(this.tpl) ? this.tpl : new Ext.XTemplate(this.tpl);
  949.         this.renderer = function(value, p, r){
  950.             return tpl.apply(r.data);
  951.         };
  952.         this.tpl = tpl;
  953.     }
  954. });
  955. /*
  956.  * @property types
  957.  * @type Object
  958.  * @member Ext.grid.Column
  959.  * @static
  960.  * <p>An object containing predefined Column classes keyed by a mnemonic code which may be referenced
  961.  * by the {@link Ext.grid.ColumnModel#xtype xtype} config option of ColumnModel.</p>
  962.  * <p>This contains the following properties</p><div class="mdesc-details"><ul>
  963.  * <li>gridcolumn : <b>{@link Ext.grid.Column Column constructor}</b></li>
  964.  * <li>booleancolumn : <b>{@link Ext.grid.BooleanColumn BooleanColumn constructor}</b></li>
  965.  * <li>numbercolumn : <b>{@link Ext.grid.NumberColumn NumberColumn constructor}</b></li>
  966.  * <li>datecolumn : <b>{@link Ext.grid.DateColumn DateColumn constructor}</b></li>
  967.  * <li>templatecolumn : <b>{@link Ext.grid.TemplateColumn TemplateColumn constructor}</b></li>
  968.  * </ul></div>
  969.  */
  970. Ext.grid.Column.types = {
  971.     gridcolumn : Ext.grid.Column,
  972.     booleancolumn: Ext.grid.BooleanColumn,
  973.     numbercolumn: Ext.grid.NumberColumn,
  974.     datecolumn: Ext.grid.DateColumn,
  975.     templatecolumn: Ext.grid.TemplateColumn
  976. };/**  * @class Ext.grid.RowNumberer  * This is a utility class that can be passed into a {@link Ext.grid.ColumnModel} as a column config that provides  * an automatic row numbering column.  * <br>Usage:<br>  <pre><code>  // This is a typical column config with the first column providing row numbers  var colModel = new Ext.grid.ColumnModel([     new Ext.grid.RowNumberer(),     {header: "Name", width: 80, sortable: true},     {header: "Code", width: 50, sortable: true},     {header: "Description", width: 200, sortable: true}  ]);  </code></pre>  * @constructor  * @param {Object} config The configuration options  */ Ext.grid.RowNumberer = function(config){     Ext.apply(this, config);     if(this.rowspan){         this.renderer = this.renderer.createDelegate(this);     } }; Ext.grid.RowNumberer.prototype = {     /**      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the row      * number column (defaults to '').      */     header: "",     /**      * @cfg {Number} width The default width in pixels of the row number column (defaults to 23).      */     width: 23,     /**      * @cfg {Boolean} sortable True if the row number column is sortable (defaults to false).      * @hide      */     sortable: false,     // private     fixed:true,     menuDisabled:true,     dataIndex: '',     id: 'numberer',     rowspan: undefined,     // private     renderer : function(v, p, record, rowIndex){         if(this.rowspan){             p.cellAttr = 'rowspan="'+this.rowspan+'"';         }         return rowIndex+1;     } };/**
  977.  * @class Ext.grid.CheckboxSelectionModel
  978.  * @extends Ext.grid.RowSelectionModel
  979.  * A custom selection model that renders a column of checkboxes that can be toggled to select or deselect rows.
  980.  * @constructor
  981.  * @param {Object} config The configuration options
  982.  */
  983. Ext.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.RowSelectionModel, {
  984.     /**
  985.      * @cfg {Boolean} checkOnly <tt>true</tt> if rows can only be selected by clicking on the
  986.      * checkbox column (defaults to <tt>false</tt>).
  987.      */
  988.     /**
  989.      * @cfg {String} header Any valid text or HTML fragment to display in the header cell for the
  990.      * checkbox column.  Defaults to:<pre><code>
  991.      * '&lt;div class="x-grid3-hd-checker">&#38;#160;&lt;/div>'</tt>
  992.      * </code></pre>
  993.      * The default CSS class of <tt>'x-grid3-hd-checker'</tt> displays a checkbox in the header
  994.      * and provides support for automatic check all/none behavior on header click. This string
  995.      * can be replaced by any valid HTML fragment, including a simple text string (e.g.,
  996.      * <tt>'Select Rows'</tt>), but the automatic check all/none behavior will only work if the
  997.      * <tt>'x-grid3-hd-checker'</tt> class is supplied.
  998.      */
  999.     header: '<div class="x-grid3-hd-checker">&#160;</div>',
  1000.     /**
  1001.      * @cfg {Number} width The default width in pixels of the checkbox column (defaults to <tt>20</tt>).
  1002.      */
  1003.     width: 20,
  1004.     /**
  1005.      * @cfg {Boolean} sortable <tt>true</tt> if the checkbox column is sortable (defaults to
  1006.      * <tt>false</tt>).
  1007.      */
  1008.     sortable: false,
  1009.     // private
  1010.     menuDisabled:true,
  1011.     fixed:true,
  1012.     dataIndex: '',
  1013.     id: 'checker',
  1014.     constructor: function(){
  1015.         Ext.grid.CheckboxSelectionModel.superclass.constructor.apply(this, arguments);
  1016.         if(this.checkOnly){
  1017.             this.handleMouseDown = Ext.emptyFn;
  1018.         }
  1019.     },
  1020.     // private
  1021.     initEvents : function(){
  1022.         Ext.grid.CheckboxSelectionModel.superclass.initEvents.call(this);
  1023.         this.grid.on('render', function(){
  1024.             var view = this.grid.getView();
  1025.             view.mainBody.on('mousedown', this.onMouseDown, this);
  1026.             Ext.fly(view.innerHd).on('mousedown', this.onHdMouseDown, this);
  1027.         }, this);
  1028.     },
  1029.     // private
  1030.     onMouseDown : function(e, t){
  1031.         if(e.button === 0 && t.className == 'x-grid3-row-checker'){ // Only fire if left-click
  1032.             e.stopEvent();
  1033.             var row = e.getTarget('.x-grid3-row');
  1034.             if(row){
  1035.                 var index = row.rowIndex;
  1036.                 if(this.isSelected(index)){
  1037.                     this.deselectRow(index);
  1038.                 }else{
  1039.                     this.selectRow(index, true);
  1040.                 }
  1041.             }
  1042.         }
  1043.     },
  1044.     // private
  1045.     onHdMouseDown : function(e, t){
  1046.         if(t.className == 'x-grid3-hd-checker'){
  1047.             e.stopEvent();
  1048.             var hd = Ext.fly(t.parentNode);
  1049.             var isChecked = hd.hasClass('x-grid3-hd-checker-on');
  1050.             if(isChecked){
  1051.                 hd.removeClass('x-grid3-hd-checker-on');
  1052.                 this.clearSelections();
  1053.             }else{
  1054.                 hd.addClass('x-grid3-hd-checker-on');
  1055.                 this.selectAll();
  1056.             }
  1057.         }
  1058.     },
  1059.     // private
  1060.     renderer : function(v, p, record){
  1061.         return '<div class="x-grid3-row-checker">&#160;</div>';
  1062.     }
  1063. });/**  * @class Ext.grid.CellSelectionModel  * @extends Ext.grid.AbstractSelectionModel  * This class provides the basic implementation for <i>single</i> <b>cell</b> selection in a grid.  * The object stored as the selection contains the following properties:  * <div class="mdetail-params"><ul>  * <li><b>cell</b> : see {@link #getSelectedCell}   * <li><b>record</b> : Ext.data.record The {@link Ext.data.Record Record}  * which provides the data for the row containing the selection</li>  * </ul></div>  * @constructor  * @param {Object} config The object containing the configuration of this model.  */ Ext.grid.CellSelectionModel = function(config){     Ext.apply(this, config);     this.selection = null;     this.addEvents(         /**      * @event beforecellselect      * Fires before a cell is selected, return false to cancel the selection.      * @param {SelectionModel} this      * @param {Number} rowIndex The selected row index      * @param {Number} colIndex The selected cell index      */     "beforecellselect",         /**      * @event cellselect      * Fires when a cell is selected.      * @param {SelectionModel} this      * @param {Number} rowIndex The selected row index      * @param {Number} colIndex The selected cell index      */     "cellselect",         /**      * @event selectionchange      * Fires when the active selection changes.      * @param {SelectionModel} this      * @param {Object} selection null for no selection or an object with two properties          * <div class="mdetail-params"><ul>          * <li><b>cell</b> : see {@link #getSelectedCell}           * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record Record}          * which provides the data for the row containing the selection</p></li>          * </ul></div>      */     "selectionchange"     );     Ext.grid.CellSelectionModel.superclass.constructor.call(this); }; Ext.extend(Ext.grid.CellSelectionModel, Ext.grid.AbstractSelectionModel,  {     /** @ignore */     initEvents : function(){         this.grid.on("cellmousedown", this.handleMouseDown, this);         this.grid.getGridEl().on(Ext.EventManager.useKeydown ? "keydown" : "keypress", this.handleKeyDown, this);         var view = this.grid.view;         view.on("refresh", this.onViewChange, this);         view.on("rowupdated", this.onRowUpdated, this);         view.on("beforerowremoved", this.clearSelections, this);         view.on("beforerowsinserted", this.clearSelections, this);         if(this.grid.isEditor){             this.grid.on("beforeedit", this.beforeEdit,  this);         }     }, //private     beforeEdit : function(e){         this.select(e.row, e.column, false, true, e.record);     }, //private     onRowUpdated : function(v, index, r){         if(this.selection && this.selection.record == r){             v.onCellSelect(index, this.selection.cell[1]);         }     }, //private     onViewChange : function(){         this.clearSelections(true);     }, /**      * Returns an array containing the row and column indexes of the currently selected cell      * (e.g., [0, 0]), or null if none selected. The array has elements:      * <div class="mdetail-params"><ul>      * <li><b>rowIndex</b> : Number<p class="sub-desc">The index of the selected row</p></li>      * <li><b>cellIndex</b> : Number<p class="sub-desc">The index of the selected cell.       * Due to possible column reordering, the cellIndex should <b>not</b> be used as an      * index into the Record's data. Instead, use the cellIndex to determine the <i>name</i>      * of the selected cell and use the field name to retrieve the data value from the record:<pre><code> // get name var fieldName = grid.getColumnModel().getDataIndex(cellIndex); // get data value based on name var data = record.get(fieldName);      * </code></pre></p></li>      * </ul></div>      * @return {Array} An array containing the row and column indexes of the selected cell, or null if none selected.  */     getSelectedCell : function(){         return this.selection ? this.selection.cell : null;     },     /**      * If anything is selected, clears all selections and fires the selectionchange event.      * @param {Boolean} preventNotify <tt>true</tt> to prevent the gridview from      * being notified about the change.      */     clearSelections : function(preventNotify){         var s = this.selection;         if(s){             if(preventNotify !== true){                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);             }             this.selection = null;             this.fireEvent("selectionchange", this, null);         }     },     /**      * Returns <tt>true</tt> if there is a selection.      * @return {Boolean}      */     hasSelection : function(){         return this.selection ? true : false;     },     /** @ignore */     handleMouseDown : function(g, row, cell, e){         if(e.button !== 0 || this.isLocked()){             return;         }         this.select(row, cell);     },     /**      * Selects a cell.  Before selecting a cell, fires the      * {@link #beforecellselect} event.  If this check is satisfied the cell      * will be selected and followed up by  firing the {@link #cellselect} and      * {@link #selectionchange} events.      * @param {Number} rowIndex The index of the row to select      * @param {Number} colIndex The index of the column to select      * @param {Boolean} preventViewNotify (optional) Specify <tt>true</tt> to      * prevent notifying the view (disables updating the selected appearance)      * @param {Boolean} preventFocus (optional) Whether to prevent the cell at      * the specified rowIndex / colIndex from being focused.      * @param {Ext.data.Record} r (optional) The record to select      */     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){             this.clearSelections();             r = r || this.grid.store.getAt(rowIndex);             this.selection = {                 record : r,                 cell : [rowIndex, colIndex]             };             if(!preventViewNotify){                 var v = this.grid.getView();                 v.onCellSelect(rowIndex, colIndex);                 if(preventFocus !== true){                     v.focusCell(rowIndex, colIndex);                 }             }             this.fireEvent("cellselect", this, rowIndex, colIndex);             this.fireEvent("selectionchange", this, this.selection);         }     }, //private     isSelectable : function(rowIndex, colIndex, cm){         return !cm.isHidden(colIndex);     },     /** @ignore */     handleKeyDown : function(e){         if(!e.isNavKeyPress()){             return;         }         var g = this.grid, s = this.selection;         if(!s){             e.stopEvent();             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);             if(cell){                 this.select(cell[0], cell[1]);             }             return;         }         var sm = this;         var walk = function(row, col, step){             return g.walkCells(row, col, step, sm.isSelectable,  sm);         };         var k = e.getKey(), r = s.cell[0], c = s.cell[1];         var newCell;         switch(k){              case e.TAB:                  if(e.shiftKey){                      newCell = walk(r, c-1, -1);                  }else{                      newCell = walk(r, c+1, 1);                  }              break;              case e.DOWN:                  newCell = walk(r+1, c, 1);              break;              case e.UP:                  newCell = walk(r-1, c, -1);              break;              case e.RIGHT:                  newCell = walk(r, c+1, 1);              break;              case e.LEFT:                  newCell = walk(r, c-1, -1);              break;              case e.ENTER:                  if(g.isEditor && !g.editing){                     g.startEditing(r, c);                     e.stopEvent();                     return;                 }              break;         }         if(newCell){             this.select(newCell[0], newCell[1]);             e.stopEvent();         }     },     acceptsNav : function(row, col, cm){         return !cm.isHidden(col) && cm.isCellEditable(col, row);     },     onEditorKey : function(field, e){         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;         if(k == e.TAB){             if(e.shiftKey){                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);             }else{                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);             }             e.stopEvent();         }else if(k == e.ENTER){             ed.completeEdit();             e.stopEvent();         }else if(k == e.ESC){          e.stopEvent();             ed.cancelEdit();         }         if(newCell){             g.startEditing(newCell[0], newCell[1]);         }     } });/**  * @class Ext.grid.EditorGridPanel  * @extends Ext.grid.GridPanel  * <p>This class extends the {@link Ext.grid.GridPanel GridPanel Class} to provide cell editing  * on selected {@link Ext.grid.Column columns}. The editable columns are specified by providing  * an {@link Ext.grid.ColumnModel#editor editor} in the {@link Ext.grid.Column column configuration}.</p>  * <p>Editability of columns may be controlled programatically by inserting an implementation  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into the  * {@link Ext.grid.ColumnModel ColumnModel}.</p>  * <p>Editing is performed on the value of the <i>field</i> specified by the column's  * <tt>{@link Ext.grid.ColumnModel#dataIndex dataIndex}</tt> in the backing {@link Ext.data.Store Store}  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display  * transformed data, this must be accounted for).</p>  * <p>If a value-to-description mapping is used to render a column, then a {@link Ext.form.Field#ComboBox ComboBox}  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}  * mapping would be an appropriate editor.</p>  * If there is a more complex mismatch between the visible data in the grid, and the editable data in  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be  * injected using the {@link #beforeedit} and {@link #afteredit} events.  * @constructor  * @param {Object} config The config object  * @xtype editorgrid  */ Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {     /**      * @cfg {Number} clicksToEdit      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts      * editing that cell.</p>      */     clicksToEdit: 2,          /**     * @cfg {Boolean} forceValidation     * True to force validation even if the value is unmodified (defaults to false)     */     forceValidation: false,     // private     isEditor : true,     // private     detectEdit: false, /**  * @cfg {Boolean} autoEncode  * True to automatically HTML encode and decode values pre and post edit (defaults to false)  */ autoEncode : false, /**  * @cfg {Boolean} trackMouseOver @hide  */     // private     trackMouseOver: false, // causes very odd FF errors     // private     initComponent : function(){         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);         if(!this.selModel){             /**              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified).              */             this.selModel = new Ext.grid.CellSelectionModel();         }         this.activeEditor = null;     this.addEvents(             /**              * @event beforeedit              * Fires before cell editing is triggered. The edit event object has the following properties <br />              * <ul style="padding:5px;padding-left:16px;">              * <li>grid - This grid</li>              * <li>record - The record being edited</li>              * <li>field - The field name being edited</li>              * <li>value - The value for the field being edited.</li>              * <li>row - The grid row index</li>              * <li>column - The grid column index</li>              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>              * </ul>              * @param {Object} e An edit event (see above for description)              */             "beforeedit",             /**              * @event afteredit              * Fires after a cell is edited. The edit event object has the following properties <br />              * <ul style="padding:5px;padding-left:16px;">              * <li>grid - This grid</li>              * <li>record - The record being edited</li>              * <li>field - The field name being edited</li>              * <li>value - The value being set</li>              * <li>originalValue - The original value for the field, before the edit.</li>              * <li>row - The grid row index</li>              * <li>column - The grid column index</li>              * </ul>              *              * <pre><code>  grid.on('afteredit', afterEdit, this ); function afterEdit(e) {     // execute an XHR to send/commit data to the server, in callback do (if successful):     e.record.commit(); };               * </code></pre>              * @param {Object} e An edit event (see above for description)              */             "afteredit",             /**              * @event validateedit              * Fires after a cell is edited, but before the value is set in the record. Return false              * to cancel the change. The edit event object has the following properties <br />              * <ul style="padding:5px;padding-left:16px;">              * <li>grid - This grid</li>              * <li>record - The record being edited</li>              * <li>field - The field name being edited</li>              * <li>value - The value being set</li>              * <li>originalValue - The original value for the field, before the edit.</li>              * <li>row - The grid row index</li>              * <li>column - The grid column index</li>              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>              * </ul>              * Usage example showing how to remove the red triangle (dirty record indicator) from some              * records (not all).  By observing the grid's validateedit event, it can be cancelled if              * the edit occurs on a targeted row (for example) and then setting the field's new value              * in the Record directly:              * <pre><code>  grid.on('validateedit', function(e) {   var myTargetRow = 6;     if (e.row == myTargetRow) {     e.cancel = true;     e.record.data[e.field] = e.value;   } });              * </code></pre>              * @param {Object} e An edit event (see above for description)              */             "validateedit"         );     },     // private     initEvents : function(){         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);         this.on("bodyscroll", this.stopEditing, this, [true]);         this.on("columnresize", this.stopEditing, this, [true]);         if(this.clicksToEdit == 1){             this.on("cellclick", this.onCellDblClick, this);         }else {             if(this.clicksToEdit == 'auto' && this.view.mainBody){                 this.view.mainBody.on("mousedown", this.onAutoEditClick, this);             }             this.on("celldblclick", this.onCellDblClick, this);         }     },     // private     onCellDblClick : function(g, row, col){         this.startEditing(row, col);     },     // private     onAutoEditClick : function(e, t){         if(e.button !== 0){             return;         }         var row = this.view.findRowIndex(t);         var col = this.view.findCellIndex(t);         if(row !== false && col !== false){             this.stopEditing();             if(this.selModel.getSelectedCell){ // cell sm                 var sc = this.selModel.getSelectedCell();                 if(sc && sc[0] === row && sc[1] === col){                     this.startEditing(row, col);                 }             }else{                 if(this.selModel.isSelected(row)){                     this.startEditing(row, col);                 }             }         }     },     // private     onEditComplete : function(ed, value, startValue){         this.editing = false;         this.activeEditor = null;         ed.un("specialkey", this.selModel.onEditorKey, this.selModel); var r = ed.record;         var field = this.colModel.getDataIndex(ed.col);         value = this.postEditValue(value, startValue, r, field);         if(this.forceValidation === true || String(value) !== String(startValue)){             var e = {                 grid: this,                 record: r,                 field: field,                 originalValue: startValue,                 value: value,                 row: ed.row,                 column: ed.col,                 cancel:false             };             if(this.fireEvent("validateedit", e) !== false && !e.cancel && String(value) !== String(startValue)){                 r.set(field, e.value);                 delete e.cancel;                 this.fireEvent("afteredit", e);             }         }         this.view.focusCell(ed.row, ed.col);     },     /**      * Starts editing the specified for the specified row/column      * @param {Number} rowIndex      * @param {Number} colIndex      */     startEditing : function(row, col){         this.stopEditing();         if(this.colModel.isCellEditable(col, row)){             this.view.ensureVisible(row, col, true);             var r = this.store.getAt(row);             var field = this.colModel.getDataIndex(col);             var e = {                 grid: this,                 record: r,                 field: field,                 value: r.data[field],                 row: row,                 column: col,                 cancel:false             };             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){                 this.editing = true;                 var ed = this.colModel.getCellEditor(col, row);                 if(!ed){                     return;                 }                 if(!ed.rendered){                     ed.render(this.view.getEditorParent(ed));                 }                 (function(){ // complex but required for focus issues in safari, ie and opera                     ed.row = row;                     ed.col = col;                     ed.record = r;                     ed.on("complete", this.onEditComplete, this, {single: true});                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);                     /**                      * The currently active editor or null                       * @type Ext.Editor                      */                     this.activeEditor = ed;                     var v = this.preEditValue(r, field);                     ed.startEdit(this.view.getCell(row, col).firstChild, v === undefined ? '' : v);                 }).defer(50, this);             }         }     },     // private     preEditValue : function(r, field){         var value = r.data[field];         return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlDecode(value) : value;     },     // private postEditValue : function(value, originalValue, r, field){ return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value; },     /**      * Stops any active editing      * @param {Boolean} cancel (optional) True to cancel any changes      */     stopEditing : function(cancel){         if(this.activeEditor){             this.activeEditor[cancel === true ? 'cancelEdit' : 'completeEdit']();         }         this.activeEditor = null;     } }); Ext.reg('editorgrid', Ext.grid.EditorGridPanel);// private // This is a support class used internally by the Grid components Ext.grid.GridEditor = function(field, config){     Ext.grid.GridEditor.superclass.constructor.call(this, field, config);     field.monitorTab = false; }; Ext.extend(Ext.grid.GridEditor, Ext.Editor, {     alignment: "tl-tl",     autoSize: "width",     hideEl : false,     cls: "x-small-editor x-grid-editor",     shim:false,     shadow:false });/**  * @class Ext.grid.PropertyRecord  * A specific {@link Ext.data.Record} type that represents a name/value pair and is made to work with the  * {@link Ext.grid.PropertyGrid}.  Typically, PropertyRecords do not need to be created directly as they can be  * created implicitly by simply using the appropriate data configs either via the {@link Ext.grid.PropertyGrid#source}  * config property or by calling {@link Ext.grid.PropertyGrid#setSource}.  However, if the need arises, these records  * can also be created explicitly as shwon below.  Example usage:  * <pre><code> var rec = new Ext.grid.PropertyRecord({     name: 'Birthday',     value: new Date(Date.parse('05/26/1972')) }); // Add record to an already populated grid grid.store.addSorted(rec); </code></pre>  * @constructor  * @param {Object} config A data object in the format: {name: [name], value: [value]}.  The specified value's type  * will be read automatically by the grid to determine the type of editor to use when displaying it.  */ Ext.grid.PropertyRecord = Ext.data.Record.create([     {name:'name',type:'string'}, 'value' ]); /**  * @class Ext.grid.PropertyStore  * @extends Ext.util.Observable  * A custom wrapper for the {@link Ext.grid.PropertyGrid}'s {@link Ext.data.Store}. This class handles the mapping  * between the custom data source objects supported by the grid and the {@link Ext.grid.PropertyRecord} format  * required for compatibility with the underlying store. Generally this class should not need to be used directly --  * the grid's data should be accessed from the underlying store via the {@link #store} property.  * @constructor  * @param {Ext.grid.Grid} grid The grid this store will be bound to  * @param {Object} source The source data config object  */ Ext.grid.PropertyStore = function(grid, source){     this.grid = grid;     this.store = new Ext.data.Store({         recordType : Ext.grid.PropertyRecord     });     this.store.on('update', this.onUpdate,  this);     if(source){         this.setSource(source);     }     Ext.grid.PropertyStore.superclass.constructor.call(this); }; Ext.extend(Ext.grid.PropertyStore, Ext.util.Observable, {     // protected - should only be called by the grid.  Use grid.setSource instead.     setSource : function(o){         this.source = o;         this.store.removeAll();         var data = [];         for(var k in o){             if(this.isEditableValue(o[k])){                 data.push(new Ext.grid.PropertyRecord({name: k, value: o[k]}, k));             }         }         this.store.loadRecords({records: data}, {}, true);     },     // private     onUpdate : function(ds, record, type){         if(type == Ext.data.Record.EDIT){             var v = record.data.value;             var oldValue = record.modified.value;             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){                 this.source[record.id] = v;                 record.commit();                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);             }else{                 record.reject();             }         }     },     // private     getProperty : function(row){        return this.store.getAt(row);     },     // private     isEditableValue: function(val){         if(Ext.isDate(val)){             return true;         }         return !(Ext.isObject(val) || Ext.isFunction(val));     },     // private     setValue : function(prop, value){         this.source[prop] = value;         this.store.getById(prop).set('value', value);     },     // protected - should only be called by the grid.  Use grid.getSource instead.     getSource : function(){         return this.source;     } }); /**  * @class Ext.grid.PropertyColumnModel  * @extends Ext.grid.ColumnModel  * A custom column model for the {@link Ext.grid.PropertyGrid}.  Generally it should not need to be used directly.  * @constructor  * @param {Ext.grid.Grid} grid The grid this store will be bound to  * @param {Object} source The source data config object  */ Ext.grid.PropertyColumnModel = function(grid, store){     var g = Ext.grid,         f = Ext.form;              this.grid = grid;     g.PropertyColumnModel.superclass.constructor.call(this, [         {header: this.nameText, width:50, sortable: true, dataIndex:'name', id: 'name', menuDisabled:true},         {header: this.valueText, width:50, resizable:false, dataIndex: 'value', id: 'value', menuDisabled:true}     ]);     this.store = store;     var bfield = new f.Field({         autoCreate: {tag: 'select', children: [             {tag: 'option', value: 'true', html: 'true'},             {tag: 'option', value: 'false', html: 'false'}         ]},         getValue : function(){             return this.el.value == 'true';         }     });     this.editors = {         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),         'boolean' : new g.GridEditor(bfield)     };     this.renderCellDelegate = this.renderCell.createDelegate(this);     this.renderPropDelegate = this.renderProp.createDelegate(this); }; Ext.extend(Ext.grid.PropertyColumnModel, Ext.grid.ColumnModel, {     // private - strings used for locale support     nameText : 'Name',     valueText : 'Value',     dateFormat : 'm/j/Y',     // private     renderDate : function(dateVal){         return dateVal.dateFormat(this.dateFormat);     },     // private     renderBool : function(bVal){         return bVal ? 'true' : 'false';     },     // private     isCellEditable : function(colIndex, rowIndex){         return colIndex == 1;     },     // private     getRenderer : function(col){         return col == 1 ?             this.renderCellDelegate : this.renderPropDelegate;     },     // private     renderProp : function(v){         return this.getPropertyName(v);     },     // private     renderCell : function(val){         var rv = val;         if(Ext.isDate(val)){             rv = this.renderDate(val);         }else if(typeof val == 'boolean'){             rv = this.renderBool(val);         }         return Ext.util.Format.htmlEncode(rv);     },     // private     getPropertyName : function(name){         var pn = this.grid.propertyNames;         return pn && pn[name] ? pn[name] : name;     },     // private     getCellEditor : function(colIndex, rowIndex){         var p = this.store.getProperty(rowIndex),             n = p.data.name,              val = p.data.value;         if(this.grid.customEditors[n]){             return this.grid.customEditors[n];         }         if(Ext.isDate(val)){             return this.editors.date;         }else if(typeof val == 'number'){             return this.editors.number;         }else if(typeof val == 'boolean'){             return this.editors['boolean'];         }else{             return this.editors.string;         }     },     // inherit docs     destroy : function(){         Ext.grid.PropertyColumnModel.superclass.destroy.call(this);         for(var ed in this.editors){             Ext.destroy(ed);         }     } }); /**  * @class Ext.grid.PropertyGrid  * @extends Ext.grid.EditorGridPanel  * A specialized grid implementation intended to mimic the traditional property grid as typically seen in  * development IDEs.  Each row in the grid represents a property of some object, and the data is stored  * as a set of name/value pairs in {@link Ext.grid.PropertyRecord}s.  Example usage:  * <pre><code> var grid = new Ext.grid.PropertyGrid({     title: 'Properties Grid',     autoHeight: true,     width: 300,     renderTo: 'grid-ct',     source: {         "(name)": "My Object",         "Created": new Date(Date.parse('10/15/2006')),         "Available": false,         "Version": .01,         "Description": "A test object"     } }); </code></pre>  * @constructor  * @param {Object} config The grid config object  */ Ext.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {     /**     * @cfg {Object} propertyNames An object containing property name/display name pairs.     * If specified, the display name will be shown in the name column instead of the property name.     */     /**     * @cfg {Object} source A data object to use as the data source of the grid (see {@link #setSource} for details).     */     /**     * @cfg {Object} customEditors An object containing name/value pairs of custom editor type definitions that allow     * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing     * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and     * associated with a custom input control by specifying a custom editor.  The name of the editor     * type should correspond with the name of the property that will use the editor.  Example usage:     * <pre><code> var grid = new Ext.grid.PropertyGrid({     ...     customEditors: {         'Start Time': new Ext.grid.GridEditor(new Ext.form.TimeField({selectOnFocus:true}))     },     source: {         'Start Time': '10:00 AM'     } }); </code></pre>     */     // private config overrides     enableColumnMove:false,     stripeRows:false,     trackMouseOver: false,     clicksToEdit:1,     enableHdMenu : false,     viewConfig : {         forceFit:true     },     // private     initComponent : function(){         this.customEditors = this.customEditors || {};         this.lastEditRow = null;         var store = new Ext.grid.PropertyStore(this);         this.propStore = store;         var cm = new Ext.grid.PropertyColumnModel(this, store);         store.store.sort('name', 'ASC');         this.addEvents(             /**              * @event beforepropertychange              * Fires before a property value changes.  Handlers can return false to cancel the property change              * (this will internally call {@link Ext.data.Record#reject} on the property's record).              * @param {Object} source The source data object for the grid (corresponds to the same object passed in              * as the {@link #source} config property).              * @param {String} recordId The record's id in the data store              * @param {Mixed} value The current edited property value              * @param {Mixed} oldValue The original property value prior to editing              */             'beforepropertychange',             /**              * @event propertychange              * Fires after a property value has changed.              * @param {Object} source The source data object for the grid (corresponds to the same object passed in              * as the {@link #source} config property).              * @param {String} recordId The record's id in the data store              * @param {Mixed} value The current edited property value              * @param {Mixed} oldValue The original property value prior to editing              */             'propertychange'         );         this.cm = cm;         this.ds = store.store;         Ext.grid.PropertyGrid.superclass.initComponent.call(this); this.mon(this.selModel, 'beforecellselect', function(sm, rowIndex, colIndex){             if(colIndex === 0){                 this.startEditing.defer(200, this, [rowIndex, 1]);                 return false;             }         }, this);     },     // private     onRender : function(){         Ext.grid.PropertyGrid.superclass.onRender.apply(this, arguments);         this.getGridEl().addClass('x-props-grid');     },     // private     afterRender: function(){         Ext.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);         if(this.source){             this.setSource(this.source);         }     },     /**      * Sets the source data object containing the property data.  The data object can contain one or more name/value      * pairs representing all of the properties of an object to display in the grid, and this data will automatically      * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,      * otherwise string type will be assumed.  If the grid already contains data, this method will replace any      * existing data.  See also the {@link #source} config value.  Example usage:      * <pre><code> grid.setSource({     "(name)": "My Object",     "Created": new Date(Date.parse('10/15/2006')),  // date type     "Available": false,  // boolean type     "Version": .01,      // decimal type     "Description": "A test object" }); </code></pre>      * @param {Object} source The data object      */     setSource : function(source){         this.propStore.setSource(source);     },     /**      * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the      * format of the data object.      * @return {Object} The data object      */     getSource : function(){         return this.propStore.getSource();     } }); Ext.reg("propertygrid", Ext.grid.PropertyGrid); /**
  1064.  * @class Ext.grid.GroupingView
  1065.  * @extends Ext.grid.GridView
  1066.  * Adds the ability for single level grouping to the grid. A {@link Ext.data.GroupingStore GroupingStore}
  1067.  * must be used to enable grouping.  Some grouping characteristics may also be configured at the
  1068.  * {@link Ext.grid.Column Column level}<div class="mdetail-params"><ul>
  1069.  * <li><code>{@link Ext.grid.Column#emptyGroupText emptyGroupText}</li>
  1070.  * <li><code>{@link Ext.grid.Column#groupable groupable}</li>
  1071.  * <li><code>{@link Ext.grid.Column#groupName groupName}</li>
  1072.  * <li><code>{@link Ext.grid.Column#groupRender groupRender}</li>
  1073.  * </ul></div>
  1074.  * <p>Sample usage:</p>
  1075.  * <pre><code>
  1076. var grid = new Ext.grid.GridPanel({
  1077.     // A groupingStore is required for a GroupingView
  1078.     store: new {@link Ext.data.GroupingStore}({
  1079.         autoDestroy: true,
  1080.         reader: reader,
  1081.         data: xg.dummyData,
  1082.         sortInfo: {field: 'company', direction: 'ASC'},
  1083.         {@link Ext.data.GroupingStore#groupOnSort groupOnSort}: true,
  1084.         {@link Ext.data.GroupingStore#remoteGroup remoteGroup}: true,
  1085.         {@link Ext.data.GroupingStore#groupField groupField}: 'industry'
  1086.     }),
  1087.     colModel: new {@link Ext.grid.ColumnModel}({
  1088.         columns:[
  1089.             {id:'company',header: 'Company', width: 60, dataIndex: 'company'},
  1090.             // {@link Ext.grid.Column#groupable groupable}, {@link Ext.grid.Column#groupName groupName}, {@link Ext.grid.Column#groupRender groupRender} are also configurable at column level
  1091.             {header: 'Price', renderer: Ext.util.Format.usMoney, dataIndex: 'price', {@link Ext.grid.Column#groupable groupable}: false},
  1092.             {header: 'Change', dataIndex: 'change', renderer: Ext.util.Format.usMoney},
  1093.             {header: 'Industry', dataIndex: 'industry'},
  1094.             {header: 'Last Updated', renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
  1095.         ],
  1096.         defaults: {
  1097.             sortable: true,
  1098.             menuDisabled: false,
  1099.             width: 20
  1100.         }
  1101.     }),
  1102.     view: new Ext.grid.GroupingView({
  1103.         {@link Ext.grid.GridView#forceFit forceFit}: true,
  1104.         // custom grouping text template to display the number of items per group
  1105.         {@link #groupTextTpl}: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
  1106.     }),
  1107.     frame:true,
  1108.     width: 700,
  1109.     height: 450,
  1110.     collapsible: true,
  1111.     animCollapse: false,
  1112.     title: 'Grouping Example',
  1113.     iconCls: 'icon-grid',
  1114.     renderTo: document.body
  1115. });
  1116.  * </code></pre>
  1117.  * @constructor
  1118.  * @param {Object} config
  1119.  */
  1120. Ext.grid.GroupingView = Ext.extend(Ext.grid.GridView, {
  1121.     /**
  1122.      * @cfg {String} groupByText Text displayed in the grid header menu for grouping by a column
  1123.      * (defaults to 'Group By This Field').
  1124.      */
  1125.     groupByText : 'Group By This Field',
  1126.     /**
  1127.      * @cfg {String} showGroupsText Text displayed in the grid header for enabling/disabling grouping
  1128.      * (defaults to 'Show in Groups').
  1129.      */
  1130.     showGroupsText : 'Show in Groups',
  1131.     /**
  1132.      * @cfg {Boolean} hideGroupedColumn <tt>true</tt> to hide the column that is currently grouped (defaults to <tt>false</tt>)
  1133.      */
  1134.     hideGroupedColumn : false,
  1135.     /**
  1136.      * @cfg {Boolean} showGroupName If <tt>true</tt> will display a prefix plus a ': ' before the group field value
  1137.      * in the group header line.  The prefix will consist of the <tt><b>{@link Ext.grid.Column#groupName groupName}</b></tt>
  1138.      * (or the configured <tt><b>{@link Ext.grid.Column#header header}</b></tt> if not provided) configured in the
  1139.      * {@link Ext.grid.Column} for each set of grouped rows (defaults to <tt>true</tt>).
  1140.      */
  1141.     showGroupName : true,
  1142.     /**
  1143.      * @cfg {Boolean} startCollapsed <tt>true</tt> to start all groups collapsed (defaults to <tt>false</tt>)
  1144.      */
  1145.     startCollapsed : false,
  1146.     /**
  1147.      * @cfg {Boolean} enableGrouping <tt>false</tt> to disable grouping functionality (defaults to <tt>true</tt>)
  1148.      */
  1149.     enableGrouping : true,
  1150.     /**
  1151.      * @cfg {Boolean} enableGroupingMenu <tt>true</tt> to enable the grouping control in the column menu (defaults to <tt>true</tt>)
  1152.      */
  1153.     enableGroupingMenu : true,
  1154.     /**
  1155.      * @cfg {Boolean} enableNoGroups <tt>true</tt> to allow the user to turn off grouping (defaults to <tt>true</tt>)
  1156.      */
  1157.     enableNoGroups : true,
  1158.     /**
  1159.      * @cfg {String} emptyGroupText The text to display when there is an empty group value (defaults to <tt>'(None)'</tt>).
  1160.      * May also be specified per column, see {@link Ext.grid.Column}.{@link Ext.grid.Column#emptyGroupText emptyGroupText}.
  1161.      */
  1162.     emptyGroupText : '(None)',
  1163.     /**
  1164.      * @cfg {Boolean} ignoreAdd <tt>true</tt> to skip refreshing the view when new rows are added (defaults to <tt>false</tt>)
  1165.      */
  1166.     ignoreAdd : false,
  1167.     /**
  1168.      * @cfg {String} groupTextTpl The template used to render the group header (defaults to <tt>'{text}'</tt>).
  1169.      * This is used to format an object which contains the following properties:
  1170.      * <div class="mdetail-params"><ul>
  1171.      * <li><b>group</b> : String<p class="sub-desc">The <i>rendered</i> value of the group field.
  1172.      * By default this is the unchanged value of the group field. If a <tt><b>{@link Ext.grid.Column#groupRenderer groupRenderer}</b></tt>
  1173.      * is specified, it is the result of a call to that function.</p></li>
  1174.      * <li><b>gvalue</b> : Object<p class="sub-desc">The <i>raw</i> value of the group field.</p></li>
  1175.      * <li><b>text</b> : String<p class="sub-desc">The configured header (as described in <tt>{@link #showGroupName})</tt>
  1176.      * if <tt>{@link #showGroupName}</tt> is <tt>true</tt>) plus the <i>rendered</i> group field value.</p></li>
  1177.      * <li><b>groupId</b> : String<p class="sub-desc">A unique, generated ID which is applied to the
  1178.      * View Element which contains the group.</p></li>
  1179.      * <li><b>startRow</b> : Number<p class="sub-desc">The row index of the Record which caused group change.</p></li>
  1180.      * <li><b>rs</b> : Array<p class="sub-desc">Contains a single element: The Record providing the data
  1181.      * for the row which caused group change.</p></li>
  1182.      * <li><b>cls</b> : String<p class="sub-desc">The generated class name string to apply to the group header Element.</p></li>
  1183.      * <li><b>style</b> : String<p class="sub-desc">The inline style rules to apply to the group header Element.</p></li>
  1184.      * </ul></div></p>
  1185.      * See {@link Ext.XTemplate} for information on how to format data using a template. Possible usage:<pre><code>
  1186. var grid = new Ext.grid.GridPanel({
  1187.     ...
  1188.     view: new Ext.grid.GroupingView({
  1189.         groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})'
  1190.     }),
  1191. });
  1192.      * </code></pre>
  1193.      */
  1194.     groupTextTpl : '{text}',
  1195.     /**
  1196.      * @cfg {Function} groupRenderer This property must be configured in the {@link Ext.grid.Column} for
  1197.      * each column.
  1198.      */
  1199.     // private
  1200.     gidSeed : 1000,
  1201.     // private
  1202.     initTemplates : function(){
  1203.         Ext.grid.GroupingView.superclass.initTemplates.call(this);
  1204.         this.state = {};
  1205.         var sm = this.grid.getSelectionModel();
  1206.         sm.on(sm.selectRow ? 'beforerowselect' : 'beforecellselect',
  1207.                 this.onBeforeRowSelect, this);
  1208.         if(!this.startGroup){
  1209.             this.startGroup = new Ext.XTemplate(
  1210.                 '<div id="{groupId}" class="x-grid-group {cls}">',
  1211.                     '<div id="{groupId}-hd" class="x-grid-group-hd" style="{style}"><div class="x-grid-group-title">', this.groupTextTpl ,'</div></div>',
  1212.                     '<div id="{groupId}-bd" class="x-grid-group-body">'
  1213.             );
  1214.         }
  1215.         this.startGroup.compile();
  1216.         this.endGroup = '</div></div>';
  1217.     },
  1218.     // private
  1219.     findGroup : function(el){
  1220.         return Ext.fly(el).up('.x-grid-group', this.mainBody.dom);
  1221.     },
  1222.     // private
  1223.     getGroups : function(){
  1224.         return this.hasRows() ? this.mainBody.dom.childNodes : [];
  1225.     },
  1226.     // private
  1227.     onAdd : function(){
  1228.         if(this.enableGrouping && !this.ignoreAdd){
  1229.             var ss = this.getScrollState();
  1230.             this.refresh();
  1231.             this.restoreScroll(ss);
  1232.         }else if(!this.enableGrouping){
  1233.             Ext.grid.GroupingView.superclass.onAdd.apply(this, arguments);
  1234.         }
  1235.     },
  1236.     // private
  1237.     onRemove : function(ds, record, index, isUpdate){
  1238.         Ext.grid.GroupingView.superclass.onRemove.apply(this, arguments);
  1239.         var g = document.getElementById(record._groupId);
  1240.         if(g && g.childNodes[1].childNodes.length < 1){
  1241.             Ext.removeNode(g);
  1242.         }
  1243.         this.applyEmptyText();
  1244.     },
  1245.     // private
  1246.     refreshRow : function(record){
  1247.         if(this.ds.getCount()==1){
  1248.             this.refresh();
  1249.         }else{
  1250.             this.isUpdating = true;
  1251.             Ext.grid.GroupingView.superclass.refreshRow.apply(this, arguments);
  1252.             this.isUpdating = false;
  1253.         }
  1254.     },
  1255.     // private
  1256.     beforeMenuShow : function(){
  1257.         var item, items = this.hmenu.items, disabled = this.cm.config[this.hdCtxIndex].groupable === false;
  1258.         if((item = items.get('groupBy'))){
  1259.             item.setDisabled(disabled);
  1260.         }
  1261.         if((item = items.get('showGroups'))){
  1262.             item.setDisabled(disabled);
  1263.     item.setChecked(!!this.getGroupField(), true);
  1264.         }
  1265.     },
  1266.     // private
  1267.     renderUI : function(){
  1268.         Ext.grid.GroupingView.superclass.renderUI.call(this);
  1269.         this.mainBody.on('mousedown', this.interceptMouse, this);
  1270.         if(this.enableGroupingMenu && this.hmenu){
  1271.             this.hmenu.add('-',{
  1272.                 itemId:'groupBy',
  1273.                 text: this.groupByText,
  1274.                 handler: this.onGroupByClick,
  1275.                 scope: this,
  1276.                 iconCls:'x-group-by-icon'
  1277.             });
  1278.             if(this.enableNoGroups){
  1279.                 this.hmenu.add({
  1280.                     itemId:'showGroups',
  1281.                     text: this.showGroupsText,
  1282.                     checked: true,
  1283.                     checkHandler: this.onShowGroupsClick,
  1284.                     scope: this
  1285.                 });
  1286.             }
  1287.             this.hmenu.on('beforeshow', this.beforeMenuShow, this);
  1288.         }
  1289.     },
  1290.     // private
  1291.     onGroupByClick : function(){
  1292.         this.grid.store.groupBy(this.cm.getDataIndex(this.hdCtxIndex));
  1293.         this.beforeMenuShow(); // Make sure the checkboxes get properly set when changing groups
  1294.     },
  1295.     // private
  1296.     onShowGroupsClick : function(mi, checked){
  1297.         if(checked){
  1298.             this.onGroupByClick();
  1299.         }else{
  1300.             this.grid.store.clearGrouping();
  1301.         }
  1302.     },
  1303.     /**
  1304.      * Toggles the specified group if no value is passed, otherwise sets the expanded state of the group to the value passed.
  1305.      * @param {String} groupId The groupId assigned to the group (see getGroupId)
  1306.      * @param {Boolean} expanded (optional)
  1307.      */
  1308.     toggleGroup : function(group, expanded){
  1309.         this.grid.stopEditing(true);
  1310.         group = Ext.getDom(group);
  1311.         var gel = Ext.fly(group);
  1312.         expanded = expanded !== undefined ?
  1313.                 expanded : gel.hasClass('x-grid-group-collapsed');
  1314.         this.state[gel.dom.id] = expanded;
  1315.         gel[expanded ? 'removeClass' : 'addClass']('x-grid-group-collapsed');
  1316.     },
  1317.     /**
  1318.      * Toggles all groups if no value is passed, otherwise sets the expanded state of all groups to the value passed.
  1319.      * @param {Boolean} expanded (optional)
  1320.      */
  1321.     toggleAllGroups : function(expanded){
  1322.         var groups = this.getGroups();
  1323.         for(var i = 0, len = groups.length; i < len; i++){
  1324.             this.toggleGroup(groups[i], expanded);
  1325.         }
  1326.     },
  1327.     /**
  1328.      * Expands all grouped rows.
  1329.      */
  1330.     expandAllGroups : function(){
  1331.         this.toggleAllGroups(true);
  1332.     },
  1333.     /**
  1334.      * Collapses all grouped rows.
  1335.      */
  1336.     collapseAllGroups : function(){
  1337.         this.toggleAllGroups(false);
  1338.     },
  1339.     // private
  1340.     interceptMouse : function(e){
  1341.         var hd = e.getTarget('.x-grid-group-hd', this.mainBody);
  1342.         if(hd){
  1343.             e.stopEvent();
  1344.             this.toggleGroup(hd.parentNode);
  1345.         }
  1346.     },
  1347.     // private
  1348.     getGroup : function(v, r, groupRenderer, rowIndex, colIndex, ds){
  1349.         var g = groupRenderer ? groupRenderer(v, {}, r, rowIndex, colIndex, ds) : String(v);
  1350.         if(g === ''){
  1351.             g = this.cm.config[colIndex].emptyGroupText || this.emptyGroupText;
  1352.         }
  1353.         return g;
  1354.     },
  1355.     // private
  1356.     getGroupField : function(){
  1357.         return this.grid.store.getGroupState();
  1358.     },
  1359.     
  1360.     // private
  1361.     afterRender : function(){
  1362.         Ext.grid.GroupingView.superclass.afterRender.call(this);
  1363.         if(this.grid.deferRowRender){
  1364.             this.updateGroupWidths();
  1365.         }
  1366.     },
  1367.     // private
  1368.     renderRows : function(){
  1369.         var groupField = this.getGroupField();
  1370.         var eg = !!groupField;
  1371.         // if they turned off grouping and the last grouped field is hidden
  1372.         if(this.hideGroupedColumn) {
  1373.             var colIndex = this.cm.findColumnIndex(groupField);
  1374.             if(!eg && this.lastGroupField !== undefined) {
  1375.                 this.mainBody.update('');
  1376.                 this.cm.setHidden(this.cm.findColumnIndex(this.lastGroupField), false);
  1377.                 delete this.lastGroupField;
  1378.             }else if (eg && this.lastGroupField === undefined) {
  1379.                 this.lastGroupField = groupField;
  1380.                 this.cm.setHidden(colIndex, true);
  1381.             }else if (eg && this.lastGroupField !== undefined && groupField !== this.lastGroupField) {
  1382.                 this.mainBody.update('');
  1383.                 var oldIndex = this.cm.findColumnIndex(this.lastGroupField);
  1384.                 this.cm.setHidden(oldIndex, false);
  1385.                 this.lastGroupField = groupField;
  1386.                 this.cm.setHidden(colIndex, true);
  1387.             }
  1388.         }
  1389.         return Ext.grid.GroupingView.superclass.renderRows.apply(
  1390.                     this, arguments);
  1391.     },
  1392.     // private
  1393.     doRender : function(cs, rs, ds, startRow, colCount, stripe){
  1394.         if(rs.length < 1){
  1395.             return '';
  1396.         }
  1397.         var groupField = this.getGroupField(),
  1398.             colIndex = this.cm.findColumnIndex(groupField),
  1399.             g;
  1400.         this.enableGrouping = !!groupField;
  1401.         if(!this.enableGrouping || this.isUpdating){
  1402.             return Ext.grid.GroupingView.superclass.doRender.apply(
  1403.                     this, arguments);
  1404.         }
  1405.         var gstyle = 'width:'+this.getTotalWidth()+';';
  1406.         var gidPrefix = this.grid.getGridEl().id;
  1407.         var cfg = this.cm.config[colIndex];
  1408.         var groupRenderer = cfg.groupRenderer || cfg.renderer;
  1409.         var prefix = this.showGroupName ?
  1410.                      (cfg.groupName || cfg.header)+': ' : '';
  1411.         var groups = [], curGroup, i, len, gid;
  1412.         for(i = 0, len = rs.length; i < len; i++){
  1413.             var rowIndex = startRow + i,
  1414.                 r = rs[i],
  1415.                 gvalue = r.data[groupField];
  1416.                 
  1417.                 g = this.getGroup(gvalue, r, groupRenderer, rowIndex, colIndex, ds);
  1418.             if(!curGroup || curGroup.group != g){
  1419.                 gid = gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(g);
  1420.                 // if state is defined use it, however state is in terms of expanded
  1421. // so negate it, otherwise use the default.
  1422. var isCollapsed  = typeof this.state[gid] !== 'undefined' ? !this.state[gid] : this.startCollapsed;
  1423. var gcls = isCollapsed ? 'x-grid-group-collapsed' : '';
  1424.                 curGroup = {
  1425.                     group: g,
  1426.                     gvalue: gvalue,
  1427.                     text: prefix + g,
  1428.                     groupId: gid,
  1429.                     startRow: rowIndex,
  1430.                     rs: [r],
  1431.                     cls: gcls,
  1432.                     style: gstyle
  1433.                 };
  1434.                 groups.push(curGroup);
  1435.             }else{
  1436.                 curGroup.rs.push(r);
  1437.             }
  1438.             r._groupId = gid;
  1439.         }
  1440.         var buf = [];
  1441.         for(i = 0, len = groups.length; i < len; i++){
  1442.             g = groups[i];
  1443.             this.doGroupStart(buf, g, cs, ds, colCount);
  1444.             buf[buf.length] = Ext.grid.GroupingView.superclass.doRender.call(
  1445.                     this, cs, g.rs, ds, g.startRow, colCount, stripe);
  1446.             this.doGroupEnd(buf, g, cs, ds, colCount);
  1447.         }
  1448.         return buf.join('');
  1449.     },
  1450.     /**
  1451.      * Dynamically tries to determine the groupId of a specific value
  1452.      * @param {String} value
  1453.      * @return {String} The group id
  1454.      */
  1455.     getGroupId : function(value){
  1456.         var gidPrefix = this.grid.getGridEl().id;
  1457.         var groupField = this.getGroupField();
  1458.         var colIndex = this.cm.findColumnIndex(groupField);
  1459.         var cfg = this.cm.config[colIndex];
  1460.         var groupRenderer = cfg.groupRenderer || cfg.renderer;
  1461.         var gtext = this.getGroup(value, {data:{}}, groupRenderer, 0, colIndex, this.ds);
  1462.         return gidPrefix + '-gp-' + groupField + '-' + Ext.util.Format.htmlEncode(value);
  1463.     },
  1464.     // private
  1465.     doGroupStart : function(buf, g, cs, ds, colCount){
  1466.         buf[buf.length] = this.startGroup.apply(g);
  1467.     },
  1468.     // private
  1469.     doGroupEnd : function(buf, g, cs, ds, colCount){
  1470.         buf[buf.length] = this.endGroup;
  1471.     },
  1472.     // private
  1473.     getRows : function(){
  1474.         if(!this.enableGrouping){
  1475.             return Ext.grid.GroupingView.superclass.getRows.call(this);
  1476.         }
  1477.         var r = [];
  1478.         var g, gs = this.getGroups();
  1479.         for(var i = 0, len = gs.length; i < len; i++){
  1480.             g = gs[i].childNodes[1].childNodes;
  1481.             for(var j = 0, jlen = g.length; j < jlen; j++){
  1482.                 r[r.length] = g[j];
  1483.             }
  1484.         }
  1485.         return r;
  1486.     },
  1487.     // private
  1488.     updateGroupWidths : function(){
  1489.         if(!this.enableGrouping || !this.hasRows()){
  1490.             return;
  1491.         }
  1492.         var tw = Math.max(this.cm.getTotalWidth(), this.el.dom.offsetWidth-this.scrollOffset) +'px';
  1493.         var gs = this.getGroups();
  1494.         for(var i = 0, len = gs.length; i < len; i++){
  1495.             gs[i].firstChild.style.width = tw;
  1496.         }
  1497.     },
  1498.     // private
  1499.     onColumnWidthUpdated : function(col, w, tw){
  1500.         Ext.grid.GroupingView.superclass.onColumnWidthUpdated.call(this, col, w, tw);
  1501.         this.updateGroupWidths();
  1502.     },
  1503.     // private
  1504.     onAllColumnWidthsUpdated : function(ws, tw){
  1505.         Ext.grid.GroupingView.superclass.onAllColumnWidthsUpdated.call(this, ws, tw);
  1506.         this.updateGroupWidths();
  1507.     },
  1508.     // private
  1509.     onColumnHiddenUpdated : function(col, hidden, tw){
  1510.         Ext.grid.GroupingView.superclass.onColumnHiddenUpdated.call(this, col, hidden, tw);
  1511.         this.updateGroupWidths();
  1512.     },
  1513.     // private
  1514.     onLayout : function(){
  1515.         this.updateGroupWidths();
  1516.     },
  1517.     // private
  1518.     onBeforeRowSelect : function(sm, rowIndex){
  1519.         if(!this.enableGrouping){
  1520.             return;
  1521.         }
  1522.         var row = this.getRow(rowIndex);
  1523.         if(row && !row.offsetParent){
  1524.             var g = this.findGroup(row);
  1525.             this.toggleGroup(g, true);
  1526.         }
  1527.     }
  1528. });
  1529. // private
  1530. Ext.grid.GroupingView.GROUP_ID = 1000;