2 * @class Ext.chart.axis.Time
3 * @extends Ext.chart.axis.Axis
5 * A type of axis whose units are measured in time values. Use this axis
6 * for listing dates that you will want to group or dynamically change.
7 * If you just want to display dates as categories then use the
8 * Category class for axis instead.
19 groupBy: 'year,month,day',
23 fromDate: new Date('1/1/11'),
24 toDate: new Date('1/7/11')
28 * In this example we're creating a time axis that has as title <em>Day</em>.
29 * The field the axis is bound to is <em>date</em>.
30 * The date format to use to display the text for the axis labels is <em>M d</em>
31 * which is a three letter month abbreviation followed by the day number.
32 * The time axis will show values for dates betwee <em>fromDate</em> and <em>toDate</em>.
33 * Since <em>constrain</em> is set to true all other values for other dates not between
34 * the fromDate and toDate will not be displayed.
38 Ext.define('Ext.chart.axis.Time', {
40 /* Begin Definitions */
42 extend: 'Ext.chart.axis.Category',
44 alternateClassName: 'Ext.chart.TimeAxis',
48 requires: ['Ext.data.Store', 'Ext.data.JsonStore'],
53 * The minimum value drawn by the axis. If not set explicitly, the axis
54 * minimum will be calculated automatically.
55 * @property calculateByLabelSize
58 calculateByLabelSize: true,
61 * Indicates the format the date will be rendered on.
62 * For example: 'M d' will render the dates as 'Jan 30', etc.
64 * @property dateFormat
65 * @type {String|Boolean}
70 * Indicates the time unit to use for each step. Can be 'day', 'month', 'year' or a comma-separated combination of all of them.
71 * Default's 'year,month,day'.
76 groupBy: 'year,month,day',
79 * Aggregation operation when grouping. Possible options are 'sum', 'avg', 'max', 'min'. Default's 'sum'.
81 * @property aggregateOp
87 * The starting date for the time axis.
94 * The ending date for the time axis.
101 * 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.).
102 * Default's [Ext.Date.DAY, 1].
107 step: [Ext.Date.DAY, 1],
110 * If true, the values of the chart will be rendered only if they belong between the fromDate and toDate.
111 * If false, the time axis will adapt to the new values by adding/removing steps.
112 * Default's [Ext.Date.DAY, 1].
114 * @property constrain
119 // @private a wrapper for date methods.
121 'year': function(date) {
122 return date.getFullYear();
124 'month': function(date) {
125 return date.getMonth() + 1;
127 'day': function(date) {
128 return date.getDate();
130 'hour': function(date) {
131 return date.getHours();
133 'minute': function(date) {
134 return date.getMinutes();
136 'second': function(date) {
137 return date.getSeconds();
139 'millisecond': function(date) {
140 return date.getMilliseconds();
144 // @private holds aggregate functions.
145 aggregateFn: (function() {
146 var etype = (function() {
147 var rgxp = /^\[object\s(.*)\]$/,
148 toString = Object.prototype.toString;
150 return toString.call(e).match(rgxp)[1];
154 'sum': function(list) {
155 var i = 0, l = list.length, acum = 0;
156 if (!list.length || etype(list[0]) != 'Number') {
164 'max': function(list) {
165 if (!list.length || etype(list[0]) != 'Number') {
168 return Math.max.apply(Math, list);
170 'min': function(list) {
171 if (!list.length || etype(list[0]) != 'Number') {
174 return Math.min.apply(Math, list);
176 'avg': function(list) {
177 var i = 0, l = list.length, acum = 0;
178 if (!list.length || etype(list[0]) != 'Number') {
189 // @private normalized the store to fill date gaps in the time interval.
190 constrainDates: function() {
191 var fromDate = Ext.Date.clone(this.fromDate),
192 toDate = Ext.Date.clone(this.toDate),
195 store = this.chart.store,
196 record, recObj, fieldNames = [],
197 newStore = Ext.create('Ext.data.Store', {
201 var getRecordByDate = (function() {
202 var index = 0, l = store.getCount();
203 return function(date) {
205 for (; index < l; index++) {
206 rec = store.getAt(index);
207 recDate = rec.get(field);
208 if (+recDate > +date) {
210 } else if (+recDate == +date) {
218 if (!this.constrain) {
219 this.chart.filteredStore = this.chart.store;
223 while(+fromDate <= +toDate) {
224 record = getRecordByDate(fromDate);
227 newStore.add(record.data);
229 newStore.model.prototype.fields.each(function(f) {
230 recObj[f.name] = false;
232 recObj.date = fromDate;
233 newStore.add(recObj);
235 fromDate = Ext.Date.add(fromDate, step[0], step[1]);
238 this.chart.filteredStore = newStore;
241 // @private aggregates values if multiple store elements belong to the same time step.
242 aggregate: function() {
244 aggKeys = [], key, value,
245 op = this.aggregateOp,
246 field = this.fields, i,
247 fields = this.groupBy.split(','),
255 dateMethods = this.dateMethods,
256 aggregateFn = this.aggregateFn,
257 store = this.chart.filteredStore || this.chart.store;
259 store.each(function(rec) {
260 //get all record field names in a simple array
261 if (!recFields.length) {
262 rec.fields.each(function(f) {
263 recFields.push(f.name);
265 recFieldsLen = recFields.length;
267 //get record date value
268 value = rec.get(field);
269 //generate key for grouping records
270 for (i = 0; i < l; i++) {
272 key = String(dateMethods[fields[i]](value));
274 key += '||' + dateMethods[fields[i]](value);
277 //get aggregation record from hash
278 if (key in aggStore) {
281 obj = aggStore[key] = {};
285 //append record values to an aggregation record
286 for (i = 0; i < recFieldsLen; i++) {
287 curField = recFields[i];
288 if (!obj[curField]) {
291 if (rec.get(curField) !== undefined) {
292 obj[curField].push(rec.get(curField));
296 //perform aggregation operations on fields
297 for (key in aggStore) {
299 for (i = 0; i < recFieldsLen; i++) {
300 curField = recFields[i];
301 obj[curField] = aggregateFn[op](obj[curField]);
305 this.chart.substore = Ext.create('Ext.data.JsonStore', {
313 // @private creates a label array to be used as the axis labels.
314 setLabels: function() {
315 var store = this.chart.substore,
316 fields = this.fields,
317 format = this.dateFormat,
318 labels, i, dates = this.dates,
319 formatFn = Ext.Date.format;
320 this.labels = labels = [];
321 store.each(function(record, i) {
323 labels.push(record.get(fields));
325 labels.push(formatFn(dates[i], format));
330 processView: function() {
331 //TODO(nico): fix this eventually...
332 if (this.constrain) {
333 this.constrainDates();
335 this.chart.substore = this.chart.filteredStore;
341 // @private modifies the store and creates the labels for the axes.
342 applyData: function() {
344 var count = this.chart.substore.getCount();