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