Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / Time3.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-chart-axis-Time'>/**
19 </span> * @class Ext.chart.axis.Time
20  * @extends Ext.chart.axis.Axis
21  *
22  * A type of axis whose units are measured in time values. Use this axis
23  * for listing dates that you will want to group or dynamically change.
24  * If you just want to display dates as categories then use the
25  * Category class for axis instead.
26  *
27  * For example:
28  *
29  *     axes: [{
30  *         type: 'Time',
31  *         position: 'bottom',
32  *         fields: 'date',
33  *         title: 'Day',
34  *         dateFormat: 'M d',
35  *         groupBy: 'year,month,day',
36  *         aggregateOp: 'sum',
37  *     
38  *         constrain: true,
39  *         fromDate: new Date('1/1/11'),
40  *         toDate: new Date('1/7/11')
41  *     }]
42  *
43  * In this example we're creating a time axis that has as title *Day*.
44  * The field the axis is bound to is `date`.
45  * The date format to use to display the text for the axis labels is `M d`
46  * which is a three letter month abbreviation followed by the day number.
47  * The time axis will show values for dates between `fromDate` and `toDate`.
48  * Since `constrain` is set to true all other values for other dates not between
49  * the fromDate and toDate will not be displayed.
50  * 
51  */
52 Ext.define('Ext.chart.axis.Time', {
53
54     /* Begin Definitions */
55
56     extend: 'Ext.chart.axis.Category',
57
58     alternateClassName: 'Ext.chart.TimeAxis',
59
60     alias: 'axis.time',
61
62     requires: ['Ext.data.Store', 'Ext.data.JsonStore'],
63
64     /* End Definitions */
65
66 <span id='Ext-chart-axis-Time-property-calculateByLabelSize'>     /**
67 </span>      * The minimum value drawn by the axis. If not set explicitly, the axis
68       * minimum will be calculated automatically.
69       * @property calculateByLabelSize
70       * @type Boolean
71       */
72     calculateByLabelSize: true,
73     
74 <span id='Ext-chart-axis-Time-property-dateFormat'>     /**
75 </span>     * Indicates the format the date will be rendered on. 
76      * For example: 'M d' will render the dates as 'Jan 30', etc.
77       *
78      * @property dateFormat
79      * @type {String|Boolean}
80       */
81     dateFormat: false,
82     
83 <span id='Ext-chart-axis-Time-property-timeUnit'>     /**
84 </span>     * Indicates the time unit to use for each step. Can be 'day', 'month', 'year' or a comma-separated combination of all of them.
85      * Default's 'year,month,day'.
86      *
87      * @property timeUnit
88      * @type {String}
89      */
90     groupBy: 'year,month,day',
91     
92 <span id='Ext-chart-axis-Time-property-aggregateOp'>    /**
93 </span>     * Aggregation operation when grouping. Possible options are 'sum', 'avg', 'max', 'min'. Default's 'sum'.
94      * 
95      * @property aggregateOp
96      * @type {String}
97       */
98     aggregateOp: 'sum',
99     
100 <span id='Ext-chart-axis-Time-property-fromDate'>    /**
101 </span>     * The starting date for the time axis.
102      * @property fromDate
103      * @type Date
104      */
105     fromDate: false,
106     
107 <span id='Ext-chart-axis-Time-property-toDate'>    /**
108 </span>     * The ending date for the time axis.
109      * @property toDate
110      * @type Date
111      */
112     toDate: false,
113     
114 <span id='Ext-chart-axis-Time-property-step'>    /**
115 </span>     * An array with two components: The first is the unit of the step (day, month, year, etc). The second one is the number of units for the step (1, 2, etc.).
116      * Default's [Ext.Date.DAY, 1].
117      * 
118      * @property step 
119      * @type Array
120      */
121     step: [Ext.Date.DAY, 1],
122     
123 <span id='Ext-chart-axis-Time-property-constrain'>    /**
124 </span>     * If true, the values of the chart will be rendered only if they belong between the fromDate and toDate. 
125      * If false, the time axis will adapt to the new values by adding/removing steps.
126      * Default's [Ext.Date.DAY, 1].
127      * 
128      * @property constrain 
129      * @type Boolean
130      */
131     constrain: false,
132     
133     // @private a wrapper for date methods.
134     dateMethods: {
135         'year': function(date) {
136             return date.getFullYear();
137         },
138         'month': function(date) {
139             return date.getMonth() + 1;
140         },
141         'day': function(date) {
142             return date.getDate();
143         },
144         'hour': function(date) {
145             return date.getHours();
146         },
147         'minute': function(date) {
148             return date.getMinutes();
149         },
150         'second': function(date) {
151             return date.getSeconds();
152         },
153         'millisecond': function(date) {
154             return date.getMilliseconds();
155         }
156     },
157     
158     // @private holds aggregate functions.
159     aggregateFn: (function() {
160         var etype = (function() {
161             var rgxp = /^\[object\s(.*)\]$/,
162                 toString = Object.prototype.toString;
163             return function(e) {
164                 return toString.call(e).match(rgxp)[1];
165             };
166         })();
167         return {
168             'sum': function(list) {
169                 var i = 0, l = list.length, acum = 0;
170                 if (!list.length || etype(list[0]) != 'Number') {
171                     return list[0];
172                 }
173                 for (; i &lt; l; i++) {
174                     acum += list[i];
175                 }
176                 return acum;
177             },
178             'max': function(list) {
179                 if (!list.length || etype(list[0]) != 'Number') {
180                     return list[0];
181                 }
182                 return Math.max.apply(Math, list);
183             },
184             'min': function(list) {
185                 if (!list.length || etype(list[0]) != 'Number') {
186                     return list[0];
187                 }
188                 return Math.min.apply(Math, list);
189             },
190             'avg': function(list) {
191                 var i = 0, l = list.length, acum = 0;
192                 if (!list.length || etype(list[0]) != 'Number') {
193                     return list[0];
194                 }
195                 for (; i &lt; l; i++) {
196                     acum += list[i];
197                 }
198                 return acum / l;
199             }
200         };
201     })(),
202     
203     // @private normalized the store to fill date gaps in the time interval.
204     constrainDates: function() {
205         var fromDate = Ext.Date.clone(this.fromDate),
206             toDate = Ext.Date.clone(this.toDate),
207             step = this.step,
208             field = this.fields,
209             store = this.chart.store,
210             record, recObj, fieldNames = [],
211             newStore = Ext.create('Ext.data.Store', {
212                 model: store.model
213             });
214         
215         var getRecordByDate = (function() {
216             var index = 0, l = store.getCount();
217             return function(date) {
218                 var rec, recDate;
219                 for (; index &lt; l; index++) {
220                     rec = store.getAt(index);
221                     recDate = rec.get(field);
222                     if (+recDate &gt; +date) {
223                         return false;
224                     } else if (+recDate == +date) {
225                         return rec;
226                     }
227                 }
228                 return false;
229             };
230         })();
231         
232         if (!this.constrain) {
233             this.chart.filteredStore = this.chart.store;
234             return;
235         }
236
237         while(+fromDate &lt;= +toDate) {
238             record = getRecordByDate(fromDate);
239             recObj = {};
240             if (record) {
241                 newStore.add(record.data);
242             } else {
243                 newStore.model.prototype.fields.each(function(f) {
244                     recObj[f.name] = false;
245                 });
246                 recObj.date = fromDate;
247                 newStore.add(recObj);
248             }
249             fromDate = Ext.Date.add(fromDate, step[0], step[1]);
250         }
251          
252         this.chart.filteredStore = newStore;
253     },
254     
255     // @private aggregates values if multiple store elements belong to the same time step.
256     aggregate: function() {
257         var aggStore = {}, 
258             aggKeys = [], key, value,
259             op = this.aggregateOp,
260             field = this.fields, i,
261             fields = this.groupBy.split(','),
262             curField,
263             recFields = [],
264             recFieldsLen = 0,
265             obj,
266             dates = [],
267             json = [],
268             l = fields.length,
269             dateMethods = this.dateMethods,
270             aggregateFn = this.aggregateFn,
271             store = this.chart.filteredStore || this.chart.store;
272         
273         store.each(function(rec) {
274             //get all record field names in a simple array
275             if (!recFields.length) {
276                 rec.fields.each(function(f) {
277                     recFields.push(f.name);
278                 });
279                 recFieldsLen = recFields.length;
280             }
281             //get record date value
282             value = rec.get(field);
283             //generate key for grouping records
284             for (i = 0; i &lt; l; i++) {
285                 if (i == 0) {
286                     key = String(dateMethods[fields[i]](value));
287                 } else {
288                     key += '||' + dateMethods[fields[i]](value);
289                 }
290             }
291             //get aggregation record from hash
292             if (key in aggStore) {
293                 obj = aggStore[key];
294             } else {
295                 obj = aggStore[key] = {};
296                 aggKeys.push(key);
297                 dates.push(value);
298             }
299             //append record values to an aggregation record
300             for (i = 0; i &lt; recFieldsLen; i++) {
301                 curField = recFields[i];
302                 if (!obj[curField]) {
303                     obj[curField] = [];
304                 }
305                 if (rec.get(curField) !== undefined) {
306                     obj[curField].push(rec.get(curField));
307                 }
308             }
309         });
310         //perform aggregation operations on fields
311         for (key in aggStore) {
312             obj = aggStore[key];
313             for (i = 0; i &lt; recFieldsLen; i++) {
314                 curField = recFields[i];
315                 obj[curField] = aggregateFn[op](obj[curField]);
316             }
317             json.push(obj);
318         }
319         this.chart.substore = Ext.create('Ext.data.JsonStore', {
320             fields: recFields,
321             data: json
322         });
323         
324         this.dates = dates;
325     },
326     
327     // @private creates a label array to be used as the axis labels.
328      setLabels: function() {
329         var store = this.chart.substore,
330             fields = this.fields,
331             format = this.dateFormat,
332             labels, i, dates = this.dates,
333             formatFn = Ext.Date.format;
334         this.labels = labels = [];
335         store.each(function(record, i) {
336             if (!format) {
337                 labels.push(record.get(fields));
338             } else {
339                 labels.push(formatFn(dates[i], format));
340             }
341          }, this);
342      },
343
344     processView: function() {
345          //TODO(nico): fix this eventually...
346          if (this.constrain) {
347              this.constrainDates();
348              this.aggregate();
349              this.chart.substore = this.chart.filteredStore;
350          } else {
351              this.aggregate();
352          }
353     },
354
355      // @private modifies the store and creates the labels for the axes.
356      applyData: function() {
357         this.setLabels();
358         var count = this.chart.substore.getCount();
359          return {
360              from: 0,
361              to: count,
362              steps: count - 1,
363              step: 1
364          };
365      }
366  });
367
368 </pre>
369 </body>
370 </html>