// Implement this so that
determineDataLimits: function() {
this.minIndex = 0;
- this.maxIndex = this.chart.data.labels.length;
+ this.maxIndex = this.chart.data.labels.length - 1;
var findIndex;
if (this.options.ticks.min !== undefined) {
buildTicks: function(index) {
// If we are viewing some subset of labels, slice the original array
- this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1);
+ this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length - 1) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1);
},
getLabelForIndex: function(index, datasetIndex) {
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
// 1 is added because we need the length but we have the indexes
- var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
+ var offsetAmt = Math.max((this.maxIndex + 1 - this.minIndex - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
getPixelForTick: function(index, includeOffset) {
return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
},
- getValueForPixel: function(pixel)
- {
+ getValueForPixel: function(pixel) {
var value
; var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
var horz = this.isHorizontal();
unitDefinition = time.units[unitDefinitionIndex];
this.tickUnit = unitDefinition.name;
- this.leadingUnitBuffer = this.firstTick.diff(this.firstTick.clone().startOf(this.tickUnit), this.tickUnit, true);
- this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true) + (this.leadingUnitBuffer > 0 ? 2 : 0);
+ var leadingUnitBuffer = this.firstTick.diff(this.firstTick.clone().startOf(this.tickUnit), this.tickUnit, true);
+ var trailingUnitBuffer = this.lastTick.clone().add(1, this.tickUnit).startOf(this.tickUnit).diff(this.lastTick, this.tickUnit, true);
+ this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true) + leadingUnitBuffer + trailingUnitBuffer;
this.displayFormat = this.options.time.displayFormats[unitDefinition.name];
}
}
// Only round the last tick if we have no hard maximum
if (!this.options.time.max) {
- this.lastTick.endOf(this.tickUnit);
+ var roundedEnd = this.lastTick.clone().startOf(this.tickUnit);
+ if (roundedEnd.diff(this.lastTick, this.tickUnit, true) !== 0) {
+ // Do not use end of because we need this to be in the next time unit
+ this.lastTick.add(1, this.tickUnit).startOf(this.tickUnit);
+ }
}
this.smallestLabelSeparation = this.width;
this.ticks.push(this.firstTick.clone());
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
- for (var i = 1; i < this.scaleSizeInUnits; ++i) {
+ for (var i = 1; i <= this.scaleSizeInUnits; ++i) {
var newTick = roundedStart.clone().add(i, this.tickUnit);
// Are we greater than the max time
}
// Always show the right tick
- if (this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit) !== 0 || this.scaleSizeInUnits === 0) {
- // this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
- // but the last tick was not rounded.
+ var diff = this.ticks[this.ticks.length - 1].diff(this.lastTick, this.tickUnit);
+ if (diff !== 0 || this.scaleSizeInUnits === 0) {
+ // this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
+ // but the last tick was not rounded.
if (this.options.time.max) {
this.ticks.push(this.lastTick.clone());
this.scaleSizeInUnits = this.lastTick.diff(this.ticks[0], this.tickUnit, true);
} else {
- this.scaleSizeInUnits = Math.ceil(this.scaleSizeInUnits / this.unitScale) * this.unitScale;
- this.ticks.push(this.firstTick.clone().add(this.scaleSizeInUnits, this.tickUnit));
- this.lastTick = this.ticks[this.ticks.length - 1].clone();
+ this.ticks.push(this.lastTick.clone());
+ this.scaleSizeInUnits = this.lastTick.diff(this.firstTick, this.tickUnit, true);
}
}
+
this.ctx.restore();
},
// Get tooltip label
this.ticks = this.ticks.map(this.tickFormatFunction, this);
},
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
- var labelMoment = this.getLabelMoment(datasetIndex, index);
+ var labelMoment = value && value.isValid && value.isValid() ? value : this.getLabelMoment(datasetIndex, index);
if (labelMoment) {
var offset = labelMoment.diff(this.firstTick, this.tickUnit, true);
- var decimal = offset / (this.scaleSizeInUnits - (this.leadingUnitBuffer > 0 ? 1 : 0));
+ var decimal = offset / this.scaleSizeInUnits;
if (this.isHorizontal()) {
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
getValueForPixel: function(pixel) {
var innerDimension = this.isHorizontal() ? this.width - (this.paddingLeft + this.paddingRight) : this.height - (this.paddingTop + this.paddingBottom);
var offset = (pixel - (this.isHorizontal() ? this.left + this.paddingLeft : this.top + this.paddingTop)) / innerDimension;
- offset *= (this.scaleSizeInUnits - (this.leadingUnitBuffer > 0 ? 1 : 0));
+ offset *= this.scaleSizeInUnits;
return this.firstTick.clone().add(moment.duration(offset, this.tickUnit).asSeconds(), 'seconds');
},
parseTime: function(label) {
var result = false;
var diff = actual.diff(expected.value, expected.unit, true);
- result = Math.abs(diff) < 0.5;
+ result = Math.abs(diff) < (expected.threshold !== undefined ? expected.threshold : 0.5);
return {
pass: result
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
- expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 11, 2015' ]);
+ expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015' ]);
});
it('should build ticks using date objects', function() {
scale.update(400, 50);
// Counts down because the lines are drawn top to bottom
- expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 11, 2015' ]);
+ expect(scale.ticks).toEqual([ 'Dec 28, 2014', 'Jan 4, 2015', 'Jan 11, 2015' ]);
});
it('should build ticks when the data is xy points', function() {
var xScale = chartInstance.scales.xScale0;
// Counts down because the lines are drawn top to bottom
- expect(xScale.ticks[0]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981']); // handle time zone changes
- expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981']); // handle time zone changes
+ expect(xScale.ticks[0]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes
+ expect(xScale.ticks[1]).toEqualOneOf(['Nov 19, 1981', 'Nov 20, 1981', 'Nov 21, 1981']); // handle time zone changes
});
it('should build ticks using the config unit', function() {
var xScale = chartInstance.scales.xScale0;
expect(xScale.getPixelForValue('', 0, 0)).toBeCloseToPixel(78);
- expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(466);
+ expect(xScale.getPixelForValue('', 6, 0)).toBeCloseToPixel(452);
expect(xScale.getValueForPixel(78)).toBeCloseToTime({
value: moment(chartInstance.data.labels[0]),
- unit: 'hour'
+ unit: 'hour',
+ threshold: 0.75
});
- expect(xScale.getValueForPixel(466)).toBeCloseToTime({
+ expect(xScale.getValueForPixel(452)).toBeCloseToTime({
value: moment(chartInstance.data.labels[6]),
unit: 'hour'
});