~mortenoh/+junk/dhis2-detailed-import-export

« back to all changes in this revision

Viewing changes to gis/dhis-gis-geostat/mfbase/ext/source/widgets/grid/GridView.js

  • Committer: larshelge at gmail
  • Date: 2009-03-03 16:46:36 UTC
  • Revision ID: larshelge@gmail.com-20090303164636-2sjlrquo7ib1gf7r
Initial check-in

Show diffs side-by-side

added added

removed removed

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