X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..HEAD:/src/chart/axis/Time.js diff --git a/src/chart/axis/Time.js b/src/chart/axis/Time.js index 22c89c8b..cf304c84 100644 --- a/src/chart/axis/Time.js +++ b/src/chart/axis/Time.js @@ -14,7 +14,7 @@ If you are unsure which license is appropriate for your use, please contact the */ /** * @class Ext.chart.axis.Time - * @extends Ext.chart.axis.Axis + * @extends Ext.chart.axis.Numeric * * A type of axis whose units are measured in time values. Use this axis * for listing dates that you will want to group or dynamically change. @@ -29,9 +29,7 @@ If you are unsure which license is appropriate for your use, please contact the * fields: 'date', * title: 'Day', * dateFormat: 'M d', - * groupBy: 'year,month,day', - * aggregateOp: 'sum', - * + * * constrain: true, * fromDate: new Date('1/1/11'), * toDate: new Date('1/7/11') @@ -44,13 +42,13 @@ If you are unsure which license is appropriate for your use, please contact the * The time axis will show values for dates between `fromDate` and `toDate`. * Since `constrain` is set to true all other values for other dates not between * the fromDate and toDate will not be displayed. - * + * */ Ext.define('Ext.chart.axis.Time', { /* Begin Definitions */ - extend: 'Ext.chart.axis.Category', + extend: 'Ext.chart.axis.Numeric', alternateClassName: 'Ext.chart.TimeAxis', @@ -60,306 +58,124 @@ Ext.define('Ext.chart.axis.Time', { /* End Definitions */ - /** - * The minimum value drawn by the axis. If not set explicitly, the axis - * minimum will be calculated automatically. - * @property calculateByLabelSize - * @type Boolean - */ - calculateByLabelSize: true, - - /** - * Indicates the format the date will be rendered on. + /** + * @cfg {String/Boolean} dateFormat + * Indicates the format the date will be rendered on. * For example: 'M d' will render the dates as 'Jan 30', etc. - * - * @property dateFormat - * @type {String|Boolean} - */ - dateFormat: false, - - /** - * Indicates the time unit to use for each step. Can be 'day', 'month', 'year' or a comma-separated combination of all of them. - * Default's 'year,month,day'. - * - * @property timeUnit - * @type {String} + * For a list of possible format strings see {@link Ext.Date Date} */ - groupBy: 'year,month,day', - - /** - * Aggregation operation when grouping. Possible options are 'sum', 'avg', 'max', 'min'. Default's 'sum'. - * - * @property aggregateOp - * @type {String} - */ - aggregateOp: 'sum', - + dateFormat: false, + /** - * The starting date for the time axis. - * @property fromDate - * @type Date + * @cfg {Date} fromDate The starting date for the time axis. */ fromDate: false, - + /** - * The ending date for the time axis. - * @property toDate - * @type Date + * @cfg {Date} toDate The ending date for the time axis. */ toDate: false, - + /** - * 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.). - * Default's [Ext.Date.DAY, 1]. - * - * @property step - * @type Array + * @cfg {Array/Boolean} step + * 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.). + * Defaults to `[Ext.Date.DAY, 1]`. */ step: [Ext.Date.DAY, 1], /** - * If true, the values of the chart will be rendered only if they belong between the fromDate and toDate. + * @cfg {Boolean} constrain + * If true, the values of the chart will be rendered only if they belong between the fromDate and toDate. * If false, the time axis will adapt to the new values by adding/removing steps. - * Default's [Ext.Date.DAY, 1]. - * - * @property constrain - * @type Boolean */ constrain: false, - - // @private a wrapper for date methods. - dateMethods: { - 'year': function(date) { - return date.getFullYear(); - }, - 'month': function(date) { - return date.getMonth() + 1; - }, - 'day': function(date) { - return date.getDate(); - }, - 'hour': function(date) { - return date.getHours(); - }, - 'minute': function(date) { - return date.getMinutes(); - }, - 'second': function(date) { - return date.getSeconds(); - }, - 'millisecond': function(date) { - return date.getMilliseconds(); - } - }, - - // @private holds aggregate functions. - aggregateFn: (function() { - var etype = (function() { - var rgxp = /^\[object\s(.*)\]$/, - toString = Object.prototype.toString; - return function(e) { - return toString.call(e).match(rgxp)[1]; - }; - })(); - return { - 'sum': function(list) { - var i = 0, l = list.length, acum = 0; - if (!list.length || etype(list[0]) != 'Number') { - return list[0]; - } - for (; i < l; i++) { - acum += list[i]; - } - return acum; - }, - 'max': function(list) { - if (!list.length || etype(list[0]) != 'Number') { - return list[0]; - } - return Math.max.apply(Math, list); - }, - 'min': function(list) { - if (!list.length || etype(list[0]) != 'Number') { - return list[0]; - } - return Math.min.apply(Math, list); - }, - 'avg': function(list) { - var i = 0, l = list.length, acum = 0; - if (!list.length || etype(list[0]) != 'Number') { - return list[0]; - } - for (; i < l; i++) { - acum += list[i]; - } - return acum / l; - } - }; - })(), - - // @private normalized the store to fill date gaps in the time interval. - constrainDates: function() { - var fromDate = Ext.Date.clone(this.fromDate), - toDate = Ext.Date.clone(this.toDate), - step = this.step, - field = this.fields, - store = this.chart.store, - record, recObj, fieldNames = [], - newStore = Ext.create('Ext.data.Store', { - model: store.model - }); - - var getRecordByDate = (function() { - var index = 0, l = store.getCount(); - return function(date) { - var rec, recDate; - for (; index < l; index++) { - rec = store.getAt(index); - recDate = rec.get(field); - if (+recDate > +date) { - return false; - } else if (+recDate == +date) { - return rec; - } - } - return false; - }; - })(); - - if (!this.constrain) { - this.chart.filteredStore = this.chart.store; - return; - } - while(+fromDate <= +toDate) { - record = getRecordByDate(fromDate); - recObj = {}; - if (record) { - newStore.add(record.data); + // Avoid roundtoDecimal call in Numeric Axis's constructor + roundToDecimal: false, + + constructor: function (config) { + var me = this, label, f, df; + me.callParent([config]); + label = me.label || {}; + df = this.dateFormat; + if (df) { + if (label.renderer) { + f = label.renderer; + label.renderer = function(v) { + v = f(v); + return Ext.Date.format(new Date(f(v)), df); + }; } else { - newStore.model.prototype.fields.each(function(f) { - recObj[f.name] = false; - }); - recObj.date = fromDate; - newStore.add(recObj); + label.renderer = function(v) { + return Ext.Date.format(new Date(v >> 0), df); + }; } - fromDate = Ext.Date.add(fromDate, step[0], step[1]); } - - this.chart.filteredStore = newStore; }, - - // @private aggregates values if multiple store elements belong to the same time step. - aggregate: function() { - var aggStore = {}, - aggKeys = [], key, value, - op = this.aggregateOp, - field = this.fields, i, - fields = this.groupBy.split(','), - curField, - recFields = [], - recFieldsLen = 0, - obj, - dates = [], - json = [], - l = fields.length, - dateMethods = this.dateMethods, - aggregateFn = this.aggregateFn, - store = this.chart.filteredStore || this.chart.store; - - store.each(function(rec) { - //get all record field names in a simple array - if (!recFields.length) { - rec.fields.each(function(f) { - recFields.push(f.name); - }); - recFieldsLen = recFields.length; - } - //get record date value - value = rec.get(field); - //generate key for grouping records - for (i = 0; i < l; i++) { - if (i == 0) { - key = String(dateMethods[fields[i]](value)); - } else { - key += '||' + dateMethods[fields[i]](value); - } - } - //get aggregation record from hash - if (key in aggStore) { - obj = aggStore[key]; - } else { - obj = aggStore[key] = {}; - aggKeys.push(key); - dates.push(value); - } - //append record values to an aggregation record - for (i = 0; i < recFieldsLen; i++) { - curField = recFields[i]; - if (!obj[curField]) { - obj[curField] = []; - } - if (rec.get(curField) !== undefined) { - obj[curField].push(rec.get(curField)); + + doConstrain: function () { + var me = this, + store = me.chart.store, + data = [], + series = me.chart.series.items, + math = Math, + mmax = math.max, + mmin = math.min, + fields = me.fields, + ln = fields.length, + range = me.getRange(), + min = range.min, max = range.max, i, l, excludes = [], + value, values, rec, data = []; + for (i = 0, l = series.length; i < l; i++) { + excludes[i] = series[i].__excludes; + } + store.each(function(record) { + for (i = 0; i < ln; i++) { + if (excludes[i]) { + continue; } + value = record.get(fields[i]); + if (+value < +min) return; + if (+value > +max) return; } - }); - //perform aggregation operations on fields - for (key in aggStore) { - obj = aggStore[key]; - for (i = 0; i < recFieldsLen; i++) { - curField = recFields[i]; - obj[curField] = aggregateFn[op](obj[curField]); - } - json.push(obj); - } - this.chart.substore = Ext.create('Ext.data.JsonStore', { - fields: recFields, - data: json - }); - - this.dates = dates; + data.push(record); + }) + me.chart.substore = Ext.create('Ext.data.JsonStore', { model: store.model, data: data }); }, - - // @private creates a label array to be used as the axis labels. - setLabels: function() { - var store = this.chart.substore, - fields = this.fields, - format = this.dateFormat, - labels, i, dates = this.dates, - formatFn = Ext.Date.format; - this.labels = labels = []; - store.each(function(record, i) { - if (!format) { - labels.push(record.get(fields)); - } else { - labels.push(formatFn(dates[i], format)); - } - }, this); - }, - processView: function() { - //TODO(nico): fix this eventually... - if (this.constrain) { - this.constrainDates(); - this.aggregate(); - this.chart.substore = this.chart.filteredStore; - } else { - this.aggregate(); - } - }, + // Before rendering, set current default step count to be number of records. + processView: function () { + var me = this; + if (me.fromDate) { + me.minimum = +me.fromDate; + } + if (me.toDate) { + me.maximum = +me.toDate; + } + if (me.constrain) { + me.doConstrain(); + } + }, - // @private modifies the store and creates the labels for the axes. - applyData: function() { - this.setLabels(); - var count = this.chart.substore.getCount(); - return { - from: 0, - to: count, - steps: count - 1, - step: 1 - }; - } + // @private modifies the store and creates the labels for the axes. + calcEnds: function() { + var me = this, range, step = me.step; + if (step) { + range = me.getRange(); + range = Ext.draw.Draw.snapEndsByDateAndStep(new Date(range.min), new Date(range.max), Ext.isNumber(step) ? [Date.MILLI, step]: step); + if (me.minimum) { + range.from = me.minimum; + } + if (me.maximum) { + range.to = me.maximum; + } + range.step = (range.to - range.from) / range.steps; + return range; + } else { + return me.callParent(arguments); + } + } });