Upgrade to ExtJS 3.2.1 - Released 04/27/2010
[extjs.git] / examples / ux / BufferView.js
1 /*!
2  * Ext JS Library 3.2.1
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 Ext.ns('Ext.ux.grid');
8
9 /**
10  * @class Ext.ux.grid.BufferView
11  * @extends Ext.grid.GridView
12  * A custom GridView which renders rows on an as-needed basis.
13  */
14 Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, {
15         /**
16          * @cfg {Number} rowHeight
17          * The height of a row in the grid.
18          */
19         rowHeight: 19,
20
21         /**
22          * @cfg {Number} borderHeight
23          * The combined height of border-top and border-bottom of a row.
24          */
25         borderHeight: 2,
26
27         /**
28          * @cfg {Boolean/Number} scrollDelay
29          * The number of milliseconds before rendering rows out of the visible
30          * viewing area. Defaults to 100. Rows will render immediately with a config
31          * of false.
32          */
33         scrollDelay: 100,
34
35         /**
36          * @cfg {Number} cacheSize
37          * The number of rows to look forward and backwards from the currently viewable
38          * area.  The cache applies only to rows that have been rendered already.
39          */
40         cacheSize: 20,
41
42         /**
43          * @cfg {Number} cleanDelay
44          * The number of milliseconds to buffer cleaning of extra rows not in the
45          * cache.
46          */
47         cleanDelay: 500,
48
49         initTemplates : function(){
50                 Ext.ux.grid.BufferView.superclass.initTemplates.call(this);
51                 var ts = this.templates;
52                 // empty div to act as a place holder for a row
53                 ts.rowHolder = new Ext.Template(
54                         '<div class="x-grid3-row {alt}" style="{tstyle}"></div>'
55                 );
56                 ts.rowHolder.disableFormats = true;
57                 ts.rowHolder.compile();
58
59                 ts.rowBody = new Ext.Template(
60                         '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
61                         '<tbody><tr>{cells}</tr>',
62                         (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>' : ''),
63                         '</tbody></table>'
64                 );
65                 ts.rowBody.disableFormats = true;
66                 ts.rowBody.compile();
67         },
68
69         getStyleRowHeight : function(){
70                 return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
71         },
72
73         getCalculatedRowHeight : function(){
74                 return this.rowHeight + this.borderHeight;
75         },
76
77         getVisibleRowCount : function(){
78                 var rh = this.getCalculatedRowHeight(),
79                     visibleHeight = this.scroller.dom.clientHeight;
80                 return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
81         },
82
83         getVisibleRows: function(){
84                 var count = this.getVisibleRowCount(),
85                     sc = this.scroller.dom.scrollTop,
86                     start = (sc === 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1);
87                 return {
88                         first: Math.max(start, 0),
89                         last: Math.min(start + count + 2, this.ds.getCount()-1)
90                 };
91         },
92
93         doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
94                 var ts = this.templates, 
95             ct = ts.cell, 
96             rt = ts.row, 
97             rb = ts.rowBody, 
98             last = colCount-1,
99                     rh = this.getStyleRowHeight(),
100                     vr = this.getVisibleRows(),
101                     tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;',
102                     // buffers
103                     buf = [], 
104             cb, 
105             c, 
106             p = {}, 
107             rp = {tstyle: tstyle}, 
108             r;
109                 for (var j = 0, len = rs.length; j < len; j++) {
110                         r = rs[j]; cb = [];
111                         var rowIndex = (j+startRow),
112                             visible = rowIndex >= vr.first && rowIndex <= vr.last;
113                         if (visible) {
114                                 for (var i = 0; i < colCount; i++) {
115                                         c = cs[i];
116                                         p.id = c.id;
117                                         p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
118                                         p.attr = p.cellAttr = "";
119                                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
120                                         p.style = c.style;
121                                         if (p.value === undefined || p.value === "") {
122                                                 p.value = "&#160;";
123                                         }
124                                         if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
125                                                 p.css += ' x-grid3-dirty-cell';
126                                         }
127                                         cb[cb.length] = ct.apply(p);
128                                 }
129                         }
130                         var alt = [];
131                         if(stripe && ((rowIndex+1) % 2 === 0)){
132                             alt[0] = "x-grid3-row-alt";
133                         }
134                         if(r.dirty){
135                             alt[1] = " x-grid3-dirty-row";
136                         }
137                         rp.cols = colCount;
138                         if(this.getRowClass){
139                             alt[2] = this.getRowClass(r, rowIndex, rp, ds);
140                         }
141                         rp.alt = alt.join(" ");
142                         rp.cells = cb.join("");
143                         buf[buf.length] =  !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
144                 }
145                 return buf.join("");
146         },
147
148         isRowRendered: function(index){
149                 var row = this.getRow(index);
150                 return row && row.childNodes.length > 0;
151         },
152
153         syncScroll: function(){
154                 Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments);
155                 this.update();
156         },
157
158         // a (optionally) buffered method to update contents of gridview
159         update: function(){
160                 if (this.scrollDelay) {
161                         if (!this.renderTask) {
162                                 this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this);
163                         }
164                         this.renderTask.delay(this.scrollDelay);
165                 }else{
166                         this.doUpdate();
167                 }
168         },
169     
170     onRemove : function(ds, record, index, isUpdate){
171         Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments);
172         if(isUpdate !== true){
173             this.update();
174         }
175     },
176
177         doUpdate: function(){
178                 if (this.getVisibleRowCount() > 0) {
179                         var g = this.grid, 
180                 cm = g.colModel, 
181                 ds = g.store,
182                 cs = this.getColumnData(),
183                         vr = this.getVisibleRows(),
184                 row;
185                         for (var i = vr.first; i <= vr.last; i++) {
186                                 // if row is NOT rendered and is visible, render it
187                                 if(!this.isRowRendered(i) && (row = this.getRow(i))){
188                                         var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true);
189                                         row.innerHTML = html;
190                                 }
191                         }
192                         this.clean();
193                 }
194         },
195
196         // a buffered method to clean rows
197         clean : function(){
198                 if(!this.cleanTask){
199                         this.cleanTask = new Ext.util.DelayedTask(this.doClean, this);
200                 }
201                 this.cleanTask.delay(this.cleanDelay);
202         },
203
204         doClean: function(){
205                 if (this.getVisibleRowCount() > 0) {
206                         var vr = this.getVisibleRows();
207                         vr.first -= this.cacheSize;
208                         vr.last += this.cacheSize;
209
210                         var i = 0, rows = this.getRows();
211                         // if first is less than 0, all rows have been rendered
212                         // so lets clean the end...
213                         if(vr.first <= 0){
214                                 i = vr.last + 1;
215                         }
216                         for(var len = this.ds.getCount(); i < len; i++){
217                                 // if current row is outside of first and last and
218                                 // has content, update the innerHTML to nothing
219                                 if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
220                                         rows[i].innerHTML = '';
221                                 }
222                         }
223                 }
224         },
225     
226     removeTask: function(name){
227         var task = this[name];
228         if(task && task.cancel){
229             task.cancel();
230             this[name] = null;
231         }
232     },
233     
234     destroy : function(){
235         this.removeTask('cleanTask');
236         this.removeTask('renderTask');  
237         Ext.ux.grid.BufferView.superclass.destroy.call(this);
238     },
239
240         layout: function(){
241                 Ext.ux.grid.BufferView.superclass.layout.call(this);
242                 this.update();
243         }
244 });