Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / examples / docs / source / LockingGridView.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 /*!
16  * Ext JS Library 3.3.0
17  * Copyright(c) 2006-2010 Ext JS, Inc.
18  * licensing@extjs.com
19  * http://www.extjs.com/license
20  */
21 Ext.ns('Ext.ux.grid');
22
23 Ext.ux.grid.LockingGridView = Ext.extend(Ext.grid.GridView, {
24     lockText : 'Lock',
25     unlockText : 'Unlock',
26     rowBorderWidth : 1,
27     lockedBorderWidth : 1,
28
29     /*
30      * This option ensures that height between the rows is synchronized
31      * between the locked and unlocked sides. This option only needs to be used
32      * when the row heights aren't predictable.
33      */
34     syncHeights: false,
35
36     initTemplates : function(){
37         var ts = this.templates || {};
38
39         if (!ts.masterTpl) {
40             ts.masterTpl = new Ext.Template(
41                 '<div class="x-grid3" hidefocus="true">',
42                     '<div class="x-grid3-locked">',
43                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>',
44                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
45                     '</div>',
46                     '<div class="x-grid3-viewport x-grid3-unlocked">',
47                         '<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>',
48                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
49                     '</div>',
50                     '<div class="x-grid3-resize-marker">&#160;</div>',
51                     '<div class="x-grid3-resize-proxy">&#160;</div>',
52                 '</div>'
53             );
54         }
55
56         this.templates = ts;
57
58         Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this);
59     },
60
61     getEditorParent : function(ed){
62         return this.el.dom;
63     },
64
65     initElements : function(){
66         var el             = Ext.get(this.grid.getGridEl().dom.firstChild),
67             lockedWrap     = el.child('div.x-grid3-locked'),
68             lockedHd       = lockedWrap.child('div.x-grid3-header'),
69             lockedScroller = lockedWrap.child('div.x-grid3-scroller'),
70             mainWrap       = el.child('div.x-grid3-viewport'),
71             mainHd         = mainWrap.child('div.x-grid3-header'),
72             scroller       = mainWrap.child('div.x-grid3-scroller');
73             
74         if (this.grid.hideHeaders) {
75             lockedHd.setDisplayed(false);
76             mainHd.setDisplayed(false);
77         }
78         
79         if(this.forceFit){
80             scroller.setStyle('overflow-x', 'hidden');
81         }
82         
83         Ext.apply(this, {
84             el      : el,
85             mainWrap: mainWrap,
86             mainHd  : mainHd,
87             innerHd : mainHd.dom.firstChild,
88             scroller: scroller,
89             mainBody: scroller.child('div.x-grid3-body'),
90             focusEl : scroller.child('a'),
91             resizeMarker: el.child('div.x-grid3-resize-marker'),
92             resizeProxy : el.child('div.x-grid3-resize-proxy'),
93             lockedWrap: lockedWrap,
94             lockedHd: lockedHd,
95             lockedScroller: lockedScroller,
96             lockedBody: lockedScroller.child('div.x-grid3-body'),
97             lockedInnerHd: lockedHd.child('div.x-grid3-header-inner', true)
98         });
99         
100         this.focusEl.swallowEvent('click', true);
101     },
102
103     getLockedRows : function(){
104         return this.hasRows() ? this.lockedBody.dom.childNodes : [];
105     },
106
107     getLockedRow : function(row){
108         return this.getLockedRows()[row];
109     },
110
111     getCell : function(row, col){
112         var lockedLen = this.cm.getLockedCount();
113         if(col < lockedLen){
114             return this.getLockedRow(row).getElementsByTagName('td')[col];
115         }
116         return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - lockedLen);
117     },
118
119     getHeaderCell : function(index){
120         var lockedLen = this.cm.getLockedCount();
121         if(index < lockedLen){
122             return this.lockedHd.dom.getElementsByTagName('td')[index];
123         }
124         return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - lockedLen);
125     },
126
127     addRowClass : function(row, cls){
128         var lockedRow = this.getLockedRow(row);
129         if(lockedRow){
130             this.fly(lockedRow).addClass(cls);
131         }
132         Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls);
133     },
134
135     removeRowClass : function(row, cls){
136         var lockedRow = this.getLockedRow(row);
137         if(lockedRow){
138             this.fly(lockedRow).removeClass(cls);
139         }
140         Ext.ux.grid.LockingGridView.superclass.removeRowClass.call(this, row, cls);
141     },
142
143     removeRow : function(row) {
144         Ext.removeNode(this.getLockedRow(row));
145         Ext.ux.grid.LockingGridView.superclass.removeRow.call(this, row);
146     },
147
148     removeRows : function(firstRow, lastRow){
149         var lockedBody = this.lockedBody.dom,
150             rowIndex = firstRow;
151         for(; rowIndex <= lastRow; rowIndex++){
152             Ext.removeNode(lockedBody.childNodes[firstRow]);
153         }
154         Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow);
155     },
156
157     syncScroll : function(e){
158         this.lockedScroller.dom.scrollTop = this.scroller.dom.scrollTop;
159         Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e);
160     },
161
162     updateSortIcon : function(col, dir){
163         var sortClasses = this.sortClasses,
164             lockedHeaders = this.lockedHd.select('td').removeClass(sortClasses),
165             headers = this.mainHd.select('td').removeClass(sortClasses),
166             lockedLen = this.cm.getLockedCount(),
167             cls = sortClasses[dir == 'DESC' ? 1 : 0];
168             
169         if(col < lockedLen){
170             lockedHeaders.item(col).addClass(cls);
171         }else{
172             headers.item(col - lockedLen).addClass(cls);
173         }
174     },
175
176     updateAllColumnWidths : function(){
177         var tw = this.getTotalWidth(),
178             clen = this.cm.getColumnCount(),
179             lw = this.getLockedWidth(),
180             llen = this.cm.getLockedCount(),
181             ws = [], len, i;
182         this.updateLockedWidth();
183         for(i = 0; i < clen; i++){
184             ws[i] = this.getColumnWidth(i);
185             var hd = this.getHeaderCell(i);
186             hd.style.width = ws[i];
187         }
188         var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;
189         for(i = 0, len = ns.length; i < len; i++){
190             row = lns[i];
191             row.style.width = lw;
192             if(row.firstChild){
193                 row.firstChild.style.width = lw;
194                 trow = row.firstChild.rows[0];
195                 for (j = 0; j < llen; j++) {
196                    trow.childNodes[j].style.width = ws[j];
197                 }
198             }
199             row = ns[i];
200             row.style.width = tw;
201             if(row.firstChild){
202                 row.firstChild.style.width = tw;
203                 trow = row.firstChild.rows[0];
204                 for (j = llen; j < clen; j++) {
205                    trow.childNodes[j - llen].style.width = ws[j];
206                 }
207             }
208         }
209         this.onAllColumnWidthsUpdated(ws, tw);
210         this.syncHeaderHeight();
211     },
212
213     updateColumnWidth : function(col, width){
214         var w = this.getColumnWidth(col),
215             llen = this.cm.getLockedCount(),
216             ns, rw, c, row;
217         this.updateLockedWidth();
218         if(col < llen){
219             ns = this.getLockedRows();
220             rw = this.getLockedWidth();
221             c = col;
222         }else{
223             ns = this.getRows();
224             rw = this.getTotalWidth();
225             c = col - llen;
226         }
227         var hd = this.getHeaderCell(col);
228         hd.style.width = w;
229         for(var i = 0, len = ns.length; i < len; i++){
230             row = ns[i];
231             row.style.width = rw;
232             if(row.firstChild){
233                 row.firstChild.style.width = rw;
234                 row.firstChild.rows[0].childNodes[c].style.width = w;
235             }
236         }
237         this.onColumnWidthUpdated(col, w, this.getTotalWidth());
238         this.syncHeaderHeight();
239     },
240
241     updateColumnHidden : function(col, hidden){
242         var llen = this.cm.getLockedCount(),
243             ns, rw, c, row,
244             display = hidden ? 'none' : '';
245         this.updateLockedWidth();
246         if(col < llen){
247             ns = this.getLockedRows();
248             rw = this.getLockedWidth();
249             c = col;
250         }else{
251             ns = this.getRows();
252             rw = this.getTotalWidth();
253             c = col - llen;
254         }
255         var hd = this.getHeaderCell(col);
256         hd.style.display = display;
257         for(var i = 0, len = ns.length; i < len; i++){
258             row = ns[i];
259             row.style.width = rw;
260             if(row.firstChild){
261                 row.firstChild.style.width = rw;
262                 row.firstChild.rows[0].childNodes[c].style.display = display;
263             }
264         }
265         this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());
266         delete this.lastViewWidth;
267         this.layout();
268     },
269
270     doRender : function(cs, rs, ds, startRow, colCount, stripe){
271         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1,
272             tstyle = 'width:'+this.getTotalWidth()+';',
273             lstyle = 'width:'+this.getLockedWidth()+';',
274             buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;
275         for(var j = 0, len = rs.length; j < len; j++){
276             r = rs[j]; cb = []; lcb = [];
277             var rowIndex = (j+startRow);
278             for(var i = 0; i < colCount; i++){
279                 c = cs[i];
280                 p.id = c.id;
281                 p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
282                     (this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');
283                 p.attr = p.cellAttr = '';
284                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
285                 p.style = c.style;
286                 if(Ext.isEmpty(p.value)){
287                     p.value = '&#160;';
288                 }
289                 if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
290                     p.css += ' x-grid3-dirty-cell';
291                 }
292                 if(c.locked){
293                     lcb[lcb.length] = ct.apply(p);
294                 }else{
295                     cb[cb.length] = ct.apply(p);
296                 }
297             }
298             var alt = [];
299             if(stripe && ((rowIndex+1) % 2 === 0)){
300                 alt[0] = 'x-grid3-row-alt';
301             }
302             if(r.dirty){
303                 alt[1] = ' x-grid3-dirty-row';
304             }
305             rp.cols = colCount;
306             if(this.getRowClass){
307                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
308             }
309             rp.alt = alt.join(' ');
310             rp.cells = cb.join('');
311             rp.tstyle = tstyle;
312             buf[buf.length] = rt.apply(rp);
313             rp.cells = lcb.join('');
314             rp.tstyle = lstyle;
315             lbuf[lbuf.length] = rt.apply(rp);
316         }
317         return [buf.join(''), lbuf.join('')];
318     },
319     processRows : function(startRow, skipStripe){
320         if(!this.ds || this.ds.getCount() < 1){
321             return;
322         }
323         var rows = this.getRows(),
324             lrows = this.getLockedRows(),
325             row, lrow;
326         skipStripe = skipStripe || !this.grid.stripeRows;
327         startRow = startRow || 0;
328         for(var i = 0, len = rows.length; i < len; ++i){
329             row = rows[i];
330             lrow = lrows[i];
331             row.rowIndex = i;
332             lrow.rowIndex = i;
333             if(!skipStripe){
334                 row.className = row.className.replace(this.rowClsRe, ' ');
335                 lrow.className = lrow.className.replace(this.rowClsRe, ' ');
336                 if ((i + 1) % 2 === 0){
337                     row.className += ' x-grid3-row-alt';
338                     lrow.className += ' x-grid3-row-alt';
339                 }
340             }
341             this.syncRowHeights(row, lrow);
342         }
343         if(startRow === 0){
344             Ext.fly(rows[0]).addClass(this.firstRowCls);
345             Ext.fly(lrows[0]).addClass(this.firstRowCls);
346         }
347         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
348         Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);
349     },
350     
351     syncRowHeights: function(row1, row2){
352         if(this.syncHeights){
353             var el1 = Ext.get(row1),
354                 el2 = Ext.get(row2),
355                 h1 = el1.getHeight(),
356                 h2 = el2.getHeight();
357
358             if(h1 > h2){
359                 el2.setHeight(h1);
360             }else if(h2 > h1){
361                 el1.setHeight(h2);
362             }
363         }
364     },
365
366     afterRender : function(){
367         if(!this.ds || !this.cm){
368             return;
369         }
370         var bd = this.renderRows() || ['&#160;', '&#160;'];
371         this.mainBody.dom.innerHTML = bd[0];
372         this.lockedBody.dom.innerHTML = bd[1];
373         this.processRows(0, true);
374         if(this.deferEmptyText !== true){
375             this.applyEmptyText();
376         }
377         this.grid.fireEvent('viewready', this.grid);
378     },
379
380     renderUI : function(){        
381         var templates = this.templates,
382             header = this.renderHeaders(),
383             body = templates.body.apply({rows:'&#160;'});
384
385         return templates.masterTpl.apply({
386             body  : body,
387             header: header[0],
388             ostyle: 'width:' + this.getOffsetWidth() + ';',
389             bstyle: 'width:' + this.getTotalWidth()  + ';',
390             lockedBody: body,
391             lockedHeader: header[1],
392             lstyle: 'width:'+this.getLockedWidth()+';'
393         });
394     },
395     
396     afterRenderUI: function(){
397         var g = this.grid;
398         this.initElements();
399         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
400         Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);
401         this.mainHd.on({
402             scope: this,
403             mouseover: this.handleHdOver,
404             mouseout: this.handleHdOut,
405             mousemove: this.handleHdMove
406         });
407         this.lockedHd.on({
408             scope: this,
409             mouseover: this.handleHdOver,
410             mouseout: this.handleHdOut,
411             mousemove: this.handleHdMove
412         });
413         this.scroller.on('scroll', this.syncScroll,  this);
414         if(g.enableColumnResize !== false){
415             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
416             this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));
417             this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));
418         }
419         if(g.enableColumnMove){
420             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
421             this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));
422             this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));
423             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
424         }
425         if(g.enableHdMenu !== false){
426             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
427             this.hmenu.add(
428                 {itemId: 'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
429                 {itemId: 'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
430             );
431             if(this.grid.enableColLock !== false){
432                 this.hmenu.add('-',
433                     {itemId: 'lock', text: this.lockText, cls: 'xg-hmenu-lock'},
434                     {itemId: 'unlock', text: this.unlockText, cls: 'xg-hmenu-unlock'}
435                 );
436             }
437             if(g.enableColumnHide !== false){
438                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
439                 this.colMenu.on({
440                     scope: this,
441                     beforeshow: this.beforeColMenuShow,
442                     itemclick: this.handleHdMenuClick
443                 });
444                 this.hmenu.add('-', {
445                     itemId:'columns',
446                     hideOnClick: false,
447                     text: this.columnsText,
448                     menu: this.colMenu,
449                     iconCls: 'x-cols-icon'
450                 });
451             }
452             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
453         }
454         if(g.trackMouseOver){
455             this.mainBody.on({
456                 scope: this,
457                 mouseover: this.onRowOver,
458                 mouseout: this.onRowOut
459             });
460             this.lockedBody.on({
461                 scope: this,
462                 mouseover: this.onRowOver,
463                 mouseout: this.onRowOut
464             });
465         }
466
467         if(g.enableDragDrop || g.enableDrag){
468             this.dragZone = new Ext.grid.GridDragZone(g, {
469                 ddGroup : g.ddGroup || 'GridDD'
470             });
471         }
472         this.updateHeaderSortState();    
473     },
474
475     layout : function(){
476         if(!this.mainBody){
477             return;
478         }
479         var g = this.grid;
480         var c = g.getGridEl();
481         var csize = c.getSize(true);
482         var vw = csize.width;
483         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){
484             return;
485         }
486         this.syncHeaderHeight();
487         if(g.autoHeight){
488             this.scroller.dom.style.overflow = 'visible';
489             this.lockedScroller.dom.style.overflow = 'visible';
490             if(Ext.isWebKit){
491                 this.scroller.dom.style.position = 'static';
492                 this.lockedScroller.dom.style.position = 'static';
493             }
494         }else{
495             this.el.setSize(csize.width, csize.height);
496             var hdHeight = this.mainHd.getHeight();
497             var vh = csize.height - (hdHeight);
498         }
499         this.updateLockedWidth();
500         if(this.forceFit){
501             if(this.lastViewWidth != vw){
502                 this.fitColumns(false, false);
503                 this.lastViewWidth = vw;
504             }
505         }else {
506             this.autoExpand();
507             this.syncHeaderScroll();
508         }
509         this.onLayout(vw, vh);
510     },
511
512     getOffsetWidth : function() {
513         return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset()) + 'px';
514     },
515
516     renderHeaders : function(){
517         var cm = this.cm,
518             ts = this.templates,
519             ct = ts.hcell,
520             cb = [], lcb = [],
521             p = {},
522             len = cm.getColumnCount(),
523             last = len - 1;
524         for(var i = 0; i < len; i++){
525             p.id = cm.getColumnId(i);
526             p.value = cm.getColumnHeader(i) || '';
527             p.style = this.getColumnStyle(i, true);
528             p.tooltip = this.getColumnTooltip(i);
529             p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
530                 (cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');
531             if(cm.config[i].align == 'right'){
532                 p.istyle = 'padding-right:16px';
533             } else {
534                 delete p.istyle;
535             }
536             if(cm.isLocked(i)){
537                 lcb[lcb.length] = ct.apply(p);
538             }else{
539                 cb[cb.length] = ct.apply(p);
540             }
541         }
542         return [ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'}),
543                 ts.header.apply({cells: lcb.join(''), tstyle:'width:'+this.getLockedWidth()+';'})];
544     },
545
546     updateHeaders : function(){
547         var hd = this.renderHeaders();
548         this.innerHd.firstChild.innerHTML = hd[0];
549         this.innerHd.firstChild.style.width = this.getOffsetWidth();
550         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
551         this.lockedInnerHd.firstChild.innerHTML = hd[1];
552         var lw = this.getLockedWidth();
553         this.lockedInnerHd.firstChild.style.width = lw;
554         this.lockedInnerHd.firstChild.firstChild.style.width = lw;
555     },
556
557     getResolvedXY : function(resolved){
558         if(!resolved){
559             return null;
560         }
561         var c = resolved.cell, r = resolved.row;
562         return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
563     },
564
565     syncFocusEl : function(row, col, hscroll){
566         Ext.ux.grid.LockingGridView.superclass.syncFocusEl.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
567     },
568
569     ensureVisible : function(row, col, hscroll){
570         return Ext.ux.grid.LockingGridView.superclass.ensureVisible.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
571     },
572
573     insertRows : function(dm, firstRow, lastRow, isUpdate){
574         var last = dm.getCount() - 1;
575         if(!isUpdate && firstRow === 0 && lastRow >= last){
576             this.refresh();
577         }else{
578             if(!isUpdate){
579                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
580             }
581             var html = this.renderRows(firstRow, lastRow),
582                 before = this.getRow(firstRow);
583             if(before){
584                 if(firstRow === 0){
585                     this.removeRowClass(0, this.firstRowCls);
586                 }
587                 Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);
588                 before = this.getLockedRow(firstRow);
589                 Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);
590             }else{
591                 this.removeRowClass(last - 1, this.lastRowCls);
592                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);
593                 Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);
594             }
595             if(!isUpdate){
596                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
597                 this.processRows(firstRow);
598             }else if(firstRow === 0 || firstRow >= last){
599                 this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);
600             }
601         }
602         this.syncFocusEl(firstRow);
603     },
604
605     getColumnStyle : function(col, isHeader){
606         var style = !isHeader ? this.cm.config[col].cellStyle || this.cm.config[col].css || '' : this.cm.config[col].headerStyle || '';
607         style += 'width:'+this.getColumnWidth(col)+';';
608         if(this.cm.isHidden(col)){
609             style += 'display:none;';
610         }
611         var align = this.cm.config[col].align;
612         if(align){
613             style += 'text-align:'+align+';';
614         }
615         return style;
616     },
617
618     getLockedWidth : function() {
619         return this.cm.getTotalLockedWidth() + 'px';
620     },
621
622     getTotalWidth : function() {
623         return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';
624     },
625
626     getColumnData : function(){
627         var cs = [], cm = this.cm, colCount = cm.getColumnCount();
628         for(var i = 0; i < colCount; i++){
629             var name = cm.getDataIndex(i);
630             cs[i] = {
631                 name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
632                 renderer : cm.getRenderer(i),
633                 id : cm.getColumnId(i),
634                 style : this.getColumnStyle(i),
635                 locked : cm.isLocked(i)
636             };
637         }
638         return cs;
639     },
640
641     renderBody : function(){
642         var markup = this.renderRows() || ['&#160;', '&#160;'];
643         return [this.templates.body.apply({rows: markup[0]}), this.templates.body.apply({rows: markup[1]})];
644     },
645     
646     refreshRow: function(record){
647         var store = this.ds, 
648             colCount = this.cm.getColumnCount(), 
649             columns = this.getColumnData(), 
650             last = colCount - 1, 
651             cls = ['x-grid3-row'], 
652             rowParams = {
653                 tstyle: String.format("width: {0};", this.getTotalWidth())
654             }, 
655             lockedRowParams = {
656                 tstyle: String.format("width: {0};", this.getLockedWidth())
657             }, 
658             colBuffer = [], 
659             lockedColBuffer = [], 
660             cellTpl = this.templates.cell, 
661             rowIndex, 
662             row, 
663             lockedRow, 
664             column, 
665             meta, 
666             css, 
667             i;
668         
669         if (Ext.isNumber(record)) {
670             rowIndex = record;
671             record = store.getAt(rowIndex);
672         } else {
673             rowIndex = store.indexOf(record);
674         }
675         
676         if (!record || rowIndex < 0) {
677             return;
678         }
679         
680         for (i = 0; i < colCount; i++) {
681             column = columns[i];
682             
683             if (i == 0) {
684                 css = 'x-grid3-cell-first';
685             } else {
686                 css = (i == last) ? 'x-grid3-cell-last ' : '';
687             }
688             
689             meta = {
690                 id: column.id,
691                 style: column.style,
692                 css: css,
693                 attr: "",
694                 cellAttr: ""
695             };
696             
697             meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);
698             
699             if (Ext.isEmpty(meta.value)) {
700                 meta.value = ' ';
701             }
702             
703             if (this.markDirty && record.dirty && typeof record.modified[column.name] != 'undefined') {
704                 meta.css += ' x-grid3-dirty-cell';
705             }
706             
707             if (column.locked) {
708                 lockedColBuffer[i] = cellTpl.apply(meta);
709             } else {
710                 colBuffer[i] = cellTpl.apply(meta);
711             }
712         }
713         
714         row = this.getRow(rowIndex);
715         row.className = '';
716         lockedRow = this.getLockedRow(rowIndex);
717         lockedRow.className = '';
718         
719         if (this.grid.stripeRows && ((rowIndex + 1) % 2 === 0)) {
720             cls.push('x-grid3-row-alt');
721         }
722         
723         if (this.getRowClass) {
724             rowParams.cols = colCount;
725             cls.push(this.getRowClass(record, rowIndex, rowParams, store));
726         }
727         
728         // Unlocked rows
729         this.fly(row).addClass(cls).setStyle(rowParams.tstyle);
730         rowParams.cells = colBuffer.join("");
731         row.innerHTML = this.templates.rowInner.apply(rowParams);
732         
733         // Locked rows
734         this.fly(lockedRow).addClass(cls).setStyle(lockedRowParams.tstyle);
735         lockedRowParams.cells = lockedColBuffer.join("");
736         lockedRow.innerHTML = this.templates.rowInner.apply(lockedRowParams);
737         lockedRow.rowIndex = rowIndex;
738         this.syncRowHeights(row, lockedRow);  
739         this.fireEvent('rowupdated', this, rowIndex, record);
740     },
741
742     refresh : function(headersToo){
743         this.fireEvent('beforerefresh', this);
744         this.grid.stopEditing(true);
745         var result = this.renderBody();
746         this.mainBody.update(result[0]).setWidth(this.getTotalWidth());
747         this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());
748         if(headersToo === true){
749             this.updateHeaders();
750             this.updateHeaderSortState();
751         }
752         this.processRows(0, true);
753         this.layout();
754         this.applyEmptyText();
755         this.fireEvent('refresh', this);
756     },
757
758     onDenyColumnLock : function(){
759
760     },
761
762     initData : function(ds, cm){
763         if(this.cm){
764             this.cm.un('columnlockchange', this.onColumnLock, this);
765         }
766         Ext.ux.grid.LockingGridView.superclass.initData.call(this, ds, cm);
767         if(this.cm){
768             this.cm.on('columnlockchange', this.onColumnLock, this);
769         }
770     },
771
772     onColumnLock : function(){
773         this.refresh(true);
774     },
775
776     handleHdMenuClick : function(item){
777         var index = this.hdCtxIndex,
778             cm = this.cm,
779             id = item.getItemId(),
780             llen = cm.getLockedCount();
781         switch(id){
782             case 'lock':
783                 if(cm.getColumnCount(true) <= llen + 1){
784                     this.onDenyColumnLock();
785                     return undefined;
786                 }
787                 cm.setLocked(index, true);
788                 if(llen != index){
789                     cm.moveColumn(index, llen);
790                     this.grid.fireEvent('columnmove', index, llen);
791                 }
792             break;
793             case 'unlock':
794                 if(llen - 1 != index){
795                     cm.setLocked(index, false, true);
796                     cm.moveColumn(index, llen - 1);
797                     this.grid.fireEvent('columnmove', index, llen - 1);
798                 }else{
799                     cm.setLocked(index, false);
800                 }
801             break;
802             default:
803                 return Ext.ux.grid.LockingGridView.superclass.handleHdMenuClick.call(this, item);
804         }
805         return true;
806     },
807
808     handleHdDown : function(e, t){
809         Ext.ux.grid.LockingGridView.superclass.handleHdDown.call(this, e, t);
810         if(this.grid.enableColLock !== false){
811             if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
812                 var hd = this.findHeaderCell(t),
813                     index = this.getCellIndex(hd),
814                     ms = this.hmenu.items, cm = this.cm;
815                 ms.get('lock').setDisabled(cm.isLocked(index));
816                 ms.get('unlock').setDisabled(!cm.isLocked(index));
817             }
818         }
819     },
820
821     syncHeaderHeight: function(){
822         var hrow = Ext.fly(this.innerHd).child('tr', true),
823             lhrow = Ext.fly(this.lockedInnerHd).child('tr', true);
824             
825         hrow.style.height = 'auto';
826         lhrow.style.height = 'auto';
827         var hd = hrow.offsetHeight,
828             lhd = lhrow.offsetHeight,
829             height = Math.max(lhd, hd) + 'px';
830             
831         hrow.style.height = height;
832         lhrow.style.height = height;
833
834     },
835
836     updateLockedWidth: function(){
837         var lw = this.cm.getTotalLockedWidth(),
838             tw = this.cm.getTotalWidth() - lw,
839             csize = this.grid.getGridEl().getSize(true),
840             lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth,
841             rp = Ext.isBorderBox ? 0 : this.rowBorderWidth,
842             vw = (csize.width - lw - lp - rp) + 'px',
843             so = this.getScrollOffset();
844         if(!this.grid.autoHeight){
845             var vh = (csize.height - this.mainHd.getHeight()) + 'px';
846             this.lockedScroller.dom.style.height = vh;
847             this.scroller.dom.style.height = vh;
848         }
849         this.lockedWrap.dom.style.width = (lw + rp) + 'px';
850         this.scroller.dom.style.width = vw;
851         this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';
852         if(this.innerHd){
853             this.lockedInnerHd.firstChild.style.width = lw + 'px';
854             this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';
855             this.innerHd.style.width = vw;
856             this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';
857             this.innerHd.firstChild.firstChild.style.width = tw + 'px';
858         }
859         if(this.mainBody){
860             this.lockedBody.dom.style.width = (lw + rp) + 'px';
861             this.mainBody.dom.style.width = (tw + rp) + 'px';
862         }
863     }
864 });
865
866 Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {
867     <div id="method-Ext.ux.grid.RowExpander-isLocked"></div>/**
868      * Returns true if the given column index is currently locked
869      * @param {Number} colIndex The column index
870      * @return {Boolean} True if the column is locked
871      */
872     isLocked : function(colIndex){
873         return this.config[colIndex].locked === true;
874     },
875
876     <div id="method-Ext.ux.grid.RowExpander-setLocked"></div>/**
877      * Locks or unlocks a given column
878      * @param {Number} colIndex The column index
879      * @param {Boolean} value True to lock, false to unlock
880      * @param {Boolean} suppressEvent Pass false to cause the columnlockchange event not to fire
881      */
882     setLocked : function(colIndex, value, suppressEvent){
883         if (this.isLocked(colIndex) == value) {
884             return;
885         }
886         this.config[colIndex].locked = value;
887         if (!suppressEvent) {
888             this.fireEvent('columnlockchange', this, colIndex, value);
889         }
890     },
891
892     <div id="method-Ext.ux.grid.RowExpander-getTotalLockedWidth"></div>/**
893      * Returns the total width of all locked columns
894      * @return {Number} The width of all locked columns
895      */
896     getTotalLockedWidth : function(){
897         var totalWidth = 0;
898         for (var i = 0, len = this.config.length; i < len; i++) {
899             if (this.isLocked(i) && !this.isHidden(i)) {
900                 totalWidth += this.getColumnWidth(i);
901             }
902         }
903
904         return totalWidth;
905     },
906
907     <div id="method-Ext.ux.grid.RowExpander-getLockedCount"></div>/**
908      * Returns the total number of locked columns
909      * @return {Number} The number of locked columns
910      */
911     getLockedCount : function() {
912         var len = this.config.length;
913
914         for (var i = 0; i < len; i++) {
915             if (!this.isLocked(i)) {
916                 return i;
917             }
918         }
919
920         //if we get to this point all of the columns are locked so we return the total
921         return len;
922     },
923
924     <div id="method-Ext.ux.grid.RowExpander-moveColumn"></div>/**
925      * Moves a column from one position to another
926      * @param {Number} oldIndex The current column index
927      * @param {Number} newIndex The destination column index
928      */
929     moveColumn : function(oldIndex, newIndex){
930         var oldLocked = this.isLocked(oldIndex),
931             newLocked = this.isLocked(newIndex);
932
933         if (oldIndex < newIndex && oldLocked && !newLocked) {
934             this.setLocked(oldIndex, false, true);
935         } else if (oldIndex > newIndex && !oldLocked && newLocked) {
936             this.setLocked(oldIndex, true, true);
937         }
938
939         Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);
940     }
941 });</pre>    
942 </body>
943 </html>