1 | /*! |
---|
2 | * Ext JS Library 3.0.0 |
---|
3 | * Copyright(c) 2006-2009 Ext JS, LLC |
---|
4 | * licensing@extjs.com |
---|
5 | * http://www.extjs.com/license |
---|
6 | */ |
---|
7 | Ext.ns('Ext.ux.grid'); |
---|
8 | |
---|
9 | /** |
---|
10 | * @class Ext.ux.grid.GroupSummary |
---|
11 | * @extends Ext.util.Observable |
---|
12 | * A GridPanel plugin that enables dynamic column calculations and a dynamically |
---|
13 | * updated grouped summary row. |
---|
14 | */ |
---|
15 | Ext.ux.grid.GroupSummary = Ext.extend(Ext.util.Observable, { |
---|
16 | /** |
---|
17 | * @cfg {Function} summaryRenderer Renderer example:<pre><code> |
---|
18 | summaryRenderer: function(v, params, data){ |
---|
19 | return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)'); |
---|
20 | }, |
---|
21 | * </code></pre> |
---|
22 | */ |
---|
23 | /** |
---|
24 | * @cfg {String} summaryType (Optional) The type of |
---|
25 | * calculation to be used for the column. For options available see |
---|
26 | * {@link #Calculations}. |
---|
27 | */ |
---|
28 | |
---|
29 | constructor : function(config){ |
---|
30 | Ext.apply(this, config); |
---|
31 | Ext.ux.grid.GroupSummary.superclass.constructor.call(this); |
---|
32 | }, |
---|
33 | init : function(grid){ |
---|
34 | this.grid = grid; |
---|
35 | this.cm = grid.getColumnModel(); |
---|
36 | this.view = grid.getView(); |
---|
37 | |
---|
38 | var v = this.view; |
---|
39 | v.doGroupEnd = this.doGroupEnd.createDelegate(this); |
---|
40 | |
---|
41 | v.afterMethod('onColumnWidthUpdated', this.doWidth, this); |
---|
42 | v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this); |
---|
43 | v.afterMethod('onColumnHiddenUpdated', this.doHidden, this); |
---|
44 | v.afterMethod('onUpdate', this.doUpdate, this); |
---|
45 | v.afterMethod('onRemove', this.doRemove, this); |
---|
46 | |
---|
47 | if(!this.rowTpl){ |
---|
48 | this.rowTpl = new Ext.Template( |
---|
49 | '<div class="x-grid3-summary-row" style="{tstyle}">', |
---|
50 | '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">', |
---|
51 | '<tbody><tr>{cells}</tr></tbody>', |
---|
52 | '</table></div>' |
---|
53 | ); |
---|
54 | this.rowTpl.disableFormats = true; |
---|
55 | } |
---|
56 | this.rowTpl.compile(); |
---|
57 | |
---|
58 | if(!this.cellTpl){ |
---|
59 | this.cellTpl = new Ext.Template( |
---|
60 | '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">', |
---|
61 | '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>', |
---|
62 | "</td>" |
---|
63 | ); |
---|
64 | this.cellTpl.disableFormats = true; |
---|
65 | } |
---|
66 | this.cellTpl.compile(); |
---|
67 | }, |
---|
68 | |
---|
69 | /** |
---|
70 | * Toggle the display of the summary row on/off |
---|
71 | * @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> to hide the summary. |
---|
72 | */ |
---|
73 | toggleSummaries : function(visible){ |
---|
74 | var el = this.grid.getGridEl(); |
---|
75 | if(el){ |
---|
76 | if(visible === undefined){ |
---|
77 | visible = el.hasClass('x-grid-hide-summary'); |
---|
78 | } |
---|
79 | el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary'); |
---|
80 | } |
---|
81 | }, |
---|
82 | |
---|
83 | renderSummary : function(o, cs){ |
---|
84 | cs = cs || this.view.getColumnData(); |
---|
85 | var cfg = this.cm.config; |
---|
86 | |
---|
87 | var buf = [], c, p = {}, cf, last = cs.length-1; |
---|
88 | for(var i = 0, len = cs.length; i < len; i++){ |
---|
89 | c = cs[i]; |
---|
90 | cf = cfg[i]; |
---|
91 | p.id = c.id; |
---|
92 | p.style = c.style; |
---|
93 | p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : ''); |
---|
94 | if(cf.summaryType || cf.summaryRenderer){ |
---|
95 | p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o); |
---|
96 | }else{ |
---|
97 | p.value = ''; |
---|
98 | } |
---|
99 | if(p.value == undefined || p.value === "") p.value = " "; |
---|
100 | buf[buf.length] = this.cellTpl.apply(p); |
---|
101 | } |
---|
102 | |
---|
103 | return this.rowTpl.apply({ |
---|
104 | tstyle: 'width:'+this.view.getTotalWidth()+';', |
---|
105 | cells: buf.join('') |
---|
106 | }); |
---|
107 | }, |
---|
108 | |
---|
109 | /** |
---|
110 | * @private |
---|
111 | * @param {Object} rs |
---|
112 | * @param {Object} cs |
---|
113 | */ |
---|
114 | calculate : function(rs, cs){ |
---|
115 | var data = {}, r, c, cfg = this.cm.config, cf; |
---|
116 | for(var j = 0, jlen = rs.length; j < jlen; j++){ |
---|
117 | r = rs[j]; |
---|
118 | for(var i = 0, len = cs.length; i < len; i++){ |
---|
119 | c = cs[i]; |
---|
120 | cf = cfg[i]; |
---|
121 | if(cf.summaryType){ |
---|
122 | data[c.name] = Ext.ux.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data); |
---|
123 | } |
---|
124 | } |
---|
125 | } |
---|
126 | return data; |
---|
127 | }, |
---|
128 | |
---|
129 | doGroupEnd : function(buf, g, cs, ds, colCount){ |
---|
130 | var data = this.calculate(g.rs, cs); |
---|
131 | buf.push('</div>', this.renderSummary({data: data}, cs), '</div>'); |
---|
132 | }, |
---|
133 | |
---|
134 | doWidth : function(col, w, tw){ |
---|
135 | var gs = this.view.getGroups(), s; |
---|
136 | for(var i = 0, len = gs.length; i < len; i++){ |
---|
137 | s = gs[i].childNodes[2]; |
---|
138 | s.style.width = tw; |
---|
139 | s.firstChild.style.width = tw; |
---|
140 | s.firstChild.rows[0].childNodes[col].style.width = w; |
---|
141 | } |
---|
142 | }, |
---|
143 | |
---|
144 | doAllWidths : function(ws, tw){ |
---|
145 | var gs = this.view.getGroups(), s, cells, wlen = ws.length; |
---|
146 | for(var i = 0, len = gs.length; i < len; i++){ |
---|
147 | s = gs[i].childNodes[2]; |
---|
148 | s.style.width = tw; |
---|
149 | s.firstChild.style.width = tw; |
---|
150 | cells = s.firstChild.rows[0].childNodes; |
---|
151 | for(var j = 0; j < wlen; j++){ |
---|
152 | cells[j].style.width = ws[j]; |
---|
153 | } |
---|
154 | } |
---|
155 | }, |
---|
156 | |
---|
157 | doHidden : function(col, hidden, tw){ |
---|
158 | var gs = this.view.getGroups(), s, display = hidden ? 'none' : ''; |
---|
159 | for(var i = 0, len = gs.length; i < len; i++){ |
---|
160 | s = gs[i].childNodes[2]; |
---|
161 | s.style.width = tw; |
---|
162 | s.firstChild.style.width = tw; |
---|
163 | s.firstChild.rows[0].childNodes[col].style.display = display; |
---|
164 | } |
---|
165 | }, |
---|
166 | |
---|
167 | // Note: requires that all (or the first) record in the |
---|
168 | // group share the same group value. Returns false if the group |
---|
169 | // could not be found. |
---|
170 | refreshSummary : function(groupValue){ |
---|
171 | return this.refreshSummaryById(this.view.getGroupId(groupValue)); |
---|
172 | }, |
---|
173 | |
---|
174 | getSummaryNode : function(gid){ |
---|
175 | var g = Ext.fly(gid, '_gsummary'); |
---|
176 | if(g){ |
---|
177 | return g.down('.x-grid3-summary-row', true); |
---|
178 | } |
---|
179 | return null; |
---|
180 | }, |
---|
181 | |
---|
182 | refreshSummaryById : function(gid){ |
---|
183 | var g = document.getElementById(gid); |
---|
184 | if(!g){ |
---|
185 | return false; |
---|
186 | } |
---|
187 | var rs = []; |
---|
188 | this.grid.store.each(function(r){ |
---|
189 | if(r._groupId == gid){ |
---|
190 | rs[rs.length] = r; |
---|
191 | } |
---|
192 | }); |
---|
193 | var cs = this.view.getColumnData(); |
---|
194 | var data = this.calculate(rs, cs); |
---|
195 | var markup = this.renderSummary({data: data}, cs); |
---|
196 | |
---|
197 | var existing = this.getSummaryNode(gid); |
---|
198 | if(existing){ |
---|
199 | g.removeChild(existing); |
---|
200 | } |
---|
201 | Ext.DomHelper.append(g, markup); |
---|
202 | return true; |
---|
203 | }, |
---|
204 | |
---|
205 | doUpdate : function(ds, record){ |
---|
206 | this.refreshSummaryById(record._groupId); |
---|
207 | }, |
---|
208 | |
---|
209 | doRemove : function(ds, record, index, isUpdate){ |
---|
210 | if(!isUpdate){ |
---|
211 | this.refreshSummaryById(record._groupId); |
---|
212 | } |
---|
213 | }, |
---|
214 | |
---|
215 | /** |
---|
216 | * Show a message in the summary row. |
---|
217 | * <pre><code> |
---|
218 | grid.on('afteredit', function(){ |
---|
219 | var groupValue = 'Ext Forms: Field Anchoring'; |
---|
220 | summary.showSummaryMsg(groupValue, 'Updating Summary...'); |
---|
221 | }); |
---|
222 | * </code></pre> |
---|
223 | * @param {String} groupValue |
---|
224 | * @param {String} msg Text to use as innerHTML for the summary row. |
---|
225 | */ |
---|
226 | showSummaryMsg : function(groupValue, msg){ |
---|
227 | var gid = this.view.getGroupId(groupValue); |
---|
228 | var node = this.getSummaryNode(gid); |
---|
229 | if(node){ |
---|
230 | node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>'; |
---|
231 | } |
---|
232 | } |
---|
233 | }); |
---|
234 | |
---|
235 | //backwards compat |
---|
236 | Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary; |
---|
237 | |
---|
238 | |
---|
239 | /** |
---|
240 | * Calculation types for summary row:</p><div class="mdetail-params"><ul> |
---|
241 | * <li><b><tt>sum</tt></b> : <div class="sub-desc"></div></li> |
---|
242 | * <li><b><tt>count</tt></b> : <div class="sub-desc"></div></li> |
---|
243 | * <li><b><tt>max</tt></b> : <div class="sub-desc"></div></li> |
---|
244 | * <li><b><tt>min</tt></b> : <div class="sub-desc"></div></li> |
---|
245 | * <li><b><tt>average</tt></b> : <div class="sub-desc"></div></li> |
---|
246 | * </ul></div> |
---|
247 | * <p>Custom calculations may be implemented. An example of |
---|
248 | * custom <code>summaryType=totalCost</code>:</p><pre><code> |
---|
249 | // define a custom summary function |
---|
250 | Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){ |
---|
251 | return v + (record.data.estimate * record.data.rate); |
---|
252 | }; |
---|
253 | * </code></pre> |
---|
254 | * @property Calculations |
---|
255 | */ |
---|
256 | |
---|
257 | Ext.ux.grid.GroupSummary.Calculations = { |
---|
258 | 'sum' : function(v, record, field){ |
---|
259 | return v + (record.data[field]||0); |
---|
260 | }, |
---|
261 | |
---|
262 | 'count' : function(v, record, field, data){ |
---|
263 | return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1); |
---|
264 | }, |
---|
265 | |
---|
266 | 'max' : function(v, record, field, data){ |
---|
267 | var v = record.data[field]; |
---|
268 | var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max']; |
---|
269 | return v > max ? (data[field+'max'] = v) : max; |
---|
270 | }, |
---|
271 | |
---|
272 | 'min' : function(v, record, field, data){ |
---|
273 | var v = record.data[field]; |
---|
274 | var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min']; |
---|
275 | return v < min ? (data[field+'min'] = v) : min; |
---|
276 | }, |
---|
277 | |
---|
278 | 'average' : function(v, record, field, data){ |
---|
279 | var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1); |
---|
280 | var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0))); |
---|
281 | return t === 0 ? 0 : t / c; |
---|
282 | } |
---|
283 | }; |
---|
284 | Ext.grid.GroupSummary.Calculations = Ext.ux.grid.GroupSummary.Calculations; |
---|
285 | |
---|
286 | /** |
---|
287 | * @class Ext.ux.grid.HybridSummary |
---|
288 | * @extends Ext.ux.grid.GroupSummary |
---|
289 | * Adds capability to specify the summary data for the group via json as illustrated here: |
---|
290 | * <pre><code> |
---|
291 | { |
---|
292 | data: [ |
---|
293 | { |
---|
294 | projectId: 100, project: 'House', |
---|
295 | taskId: 112, description: 'Paint', |
---|
296 | estimate: 6, rate: 150, |
---|
297 | due:'06/24/2007' |
---|
298 | }, |
---|
299 | ... |
---|
300 | ], |
---|
301 | |
---|
302 | summaryData: { |
---|
303 | 'House': { |
---|
304 | description: 14, estimate: 9, |
---|
305 | rate: 99, due: new Date(2009, 6, 29), |
---|
306 | cost: 999 |
---|
307 | } |
---|
308 | } |
---|
309 | } |
---|
310 | * </code></pre> |
---|
311 | * |
---|
312 | */ |
---|
313 | Ext.ux.grid.HybridSummary = Ext.extend(Ext.ux.grid.GroupSummary, { |
---|
314 | /** |
---|
315 | * @private |
---|
316 | * @param {Object} rs |
---|
317 | * @param {Object} cs |
---|
318 | */ |
---|
319 | calculate : function(rs, cs){ |
---|
320 | var gcol = this.view.getGroupField(); |
---|
321 | var gvalue = rs[0].data[gcol]; |
---|
322 | var gdata = this.getSummaryData(gvalue); |
---|
323 | return gdata || Ext.ux.grid.HybridSummary.superclass.calculate.call(this, rs, cs); |
---|
324 | }, |
---|
325 | |
---|
326 | /** |
---|
327 | * <pre><code> |
---|
328 | grid.on('afteredit', function(){ |
---|
329 | var groupValue = 'Ext Forms: Field Anchoring'; |
---|
330 | summary.showSummaryMsg(groupValue, 'Updating Summary...'); |
---|
331 | setTimeout(function(){ // simulate server call |
---|
332 | // HybridSummary class implements updateSummaryData |
---|
333 | summary.updateSummaryData(groupValue, |
---|
334 | // create data object based on configured dataIndex |
---|
335 | {description: 22, estimate: 888, rate: 888, due: new Date(), cost: 8}); |
---|
336 | }, 2000); |
---|
337 | }); |
---|
338 | * </code></pre> |
---|
339 | * @param {String} groupValue |
---|
340 | * @param {Object} data data object |
---|
341 | * @param {Boolean} skipRefresh (Optional) Defaults to false |
---|
342 | */ |
---|
343 | updateSummaryData : function(groupValue, data, skipRefresh){ |
---|
344 | var json = this.grid.store.reader.jsonData; |
---|
345 | if(!json.summaryData){ |
---|
346 | json.summaryData = {}; |
---|
347 | } |
---|
348 | json.summaryData[groupValue] = data; |
---|
349 | if(!skipRefresh){ |
---|
350 | this.refreshSummary(groupValue); |
---|
351 | } |
---|
352 | }, |
---|
353 | |
---|
354 | /** |
---|
355 | * Returns the summaryData for the specified groupValue or null. |
---|
356 | * @param {String} groupValue |
---|
357 | * @return {Object} summaryData |
---|
358 | */ |
---|
359 | getSummaryData : function(groupValue){ |
---|
360 | var json = this.grid.store.reader.jsonData; |
---|
361 | if(json && json.summaryData){ |
---|
362 | return json.summaryData[groupValue]; |
---|
363 | } |
---|
364 | return null; |
---|
365 | } |
---|
366 | }); |
---|
367 | |
---|
368 | //backwards compat |
---|
369 | Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary; |
---|