GroupSummary.js 12 KB

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