}],
},
- //Number - Tension of the bezier curve between points
- tension: 0.4,
-
- //Number - Radius of each point dot in pixels
- pointRadius: 4,
-
- //Number - Pixel width of point dot border
- pointBorderWidth: 1,
-
- //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
- pointHoverRadius: 20,
-
- //Number - Pixel width of dataset border
- borderWidth: 2,
-
//String - A legend template
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
tooltips: {
- template: "(<%= dataX %>, <%= dataY %>)",
- multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= dataX %>, <%= dataY %>)",
+ template: "(<%= value.x %>, <%= value.y %>)",
+ multiTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= value.x %>, <%= value.y %>)",
},
};
defaults: defaultConfig,
initialize: function() {
//Custom Point Defaults
- this.PointClass = Chart.Point.extend({
- _chart: this.chart,
- offsetGridLines: this.options.offsetGridLines,
- borderWidth: this.options.pointBorderWidth,
- radius: this.options.pointRadius,
- hoverRadius: this.options.pointHoverRadius,
- });
-
- // Events
- helpers.bindEvents(this, this.options.events, this.events);
-
- // Build Scale
- this.buildScale();
- Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
- //Create a new line and its points for each dataset and piece of data
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
- dataset.metaDataset = new Chart.Line();
+ dataset.metaDataset = new Chart.Line({
+ _chart: this.chart,
+ _datasetIndex: datasetIndex,
+ _points: dataset.metaData,
+ });
+
dataset.metaData = [];
+
helpers.each(dataset.data, function(dataPoint, index) {
- dataset.metaData.push(new this.PointClass());
+ dataset.metaData.push(new Chart.Point({
+ _datasetIndex: datasetIndex,
+ _index: index,
+ _chart: this.chart,
+ _model: {
+ x: 0, //xScale.getPixelForValue(null, index, true),
+ y: 0, //this.chartArea.bottom,
+ },
+ }));
+
}, this);
- // Make sure each dataset is bound to an x and a y axis
+ // The line chart onlty supports a single x axis because the x axis is always a dataset axis
if (!dataset.xAxisID) {
dataset.xAxisID = this.options.scales.xAxes[0].id;
}
if (!dataset.yAxisID) {
dataset.yAxisID = this.options.scales.yAxes[0].id;
}
- }, this);
- // Set defaults for lines
- this.eachDataset(function(dataset, datasetIndex) {
- dataset = helpers.merge(this.options, dataset);
- helpers.extend(dataset.metaDataset, {
- _points: dataset.metaData,
- _datasetIndex: datasetIndex,
- _chart: this.chart,
- });
- // Copy to view model
- dataset.metaDataset.save();
}, this);
- // Set defaults for points
- this.eachElement(function(point, index, dataset, datasetIndex) {
- var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
-
- helpers.extend(point, {
- x: xScale.getPixelForValue(index),
- y: this.chartArea.bottom,
- _datasetIndex: datasetIndex,
- _index: index,
- _chart: this.chart
- });
-
- // Default bezier control points
- helpers.extend(point, {
- controlPointPreviousX: this.previousPoint(dataset, index).x,
- controlPointPreviousY: this.nextPoint(dataset, index).y,
- controlPointNextX: this.previousPoint(dataset, index).x,
- controlPointNextY: this.nextPoint(dataset, index).y,
- });
- // Copy to view model
- point.save();
- }, this);
+ // Build and fit the scale. Needs to happen after the axis IDs have been set
+ this.buildScale();
// Create tooltip instance exclusively for this chart with some defaults.
this.tooltip = new Chart.Tooltip({
_options: this.options,
}, this);
+ // Need to fit scales before we reset elements.
+ Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
+
+ // Reset so that we animation from the baseline
+ this.resetElements();
+
+ // Update that shiz
this.update();
},
nextPoint: function(collection, index) {
- return collection[index - 1] || collection[index];
+ return collection[index + 1] || collection[index];
},
previousPoint: function(collection, index) {
- return collection[index + 1] || collection[index];
+ return collection[index - 1] || collection[index];
},
- events: function(e) {
- // If exiting chart
- if (e.type == 'mouseout') {
- return this;
- }
+ resetElements: function() {
+ // Update the points
+ this.eachElement(function(point, index, dataset, datasetIndex) {
+ var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
+ var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
- this.lastActive = this.lastActive || [];
+ var yScalePoint;
- // Find Active Elements
- this.active = function() {
- switch (this.options.hover.mode) {
- case 'single':
- return this.getElementAtEvent(e);
- case 'label':
- return this.getElementsAtEvent(e);
- case 'dataset':
- return this.getDatasetAtEvent(e);
- default:
- return e;
+ if (yScale.min < 0 && yScale.max < 0) {
+ // all less than 0. use the top
+ yScalePoint = yScale.getPixelForValue(yScale.max);
+ } else if (yScale.min > 0 && yScale.max > 0) {
+ yScalePoint = yScale.getPixelForValue(yScale.min);
+ } else {
+ yScalePoint = yScale.getPixelForValue(0);
}
- }.call(this);
-
- // On Hover hook
- if (this.options.onHover) {
- this.options.onHover.call(this, this.active);
- }
- // Remove styling for last active (even if it may still be active)
- if (this.lastActive.length) {
- switch (this.options.hover.mode) {
- case 'single':
- this.lastActive[0].backgroundColor = this.data.datasets[this.lastActive[0]._datasetIndex].pointBackgroundColor;
- this.lastActive[0].borderColor = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderColor;
- this.lastActive[0].borderWidth = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderWidth;
- break;
- case 'label':
- for (var i = 0; i < this.lastActive.length; i++) {
- this.lastActive[i].backgroundColor = this.data.datasets[this.lastActive[i]._datasetIndex].pointBackgroundColor;
- this.lastActive[i].borderColor = this.data.datasets[this.lastActive[i]._datasetIndex].pointBorderColor;
- this.lastActive[i].borderWidth = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderWidth;
- }
- break;
- case 'dataset':
- break;
- default:
- // Don't change anything
- }
- }
+ helpers.extend(point, {
+ // Utility
+ _chart: this.chart,
+ _xScale: xScale,
+ _yScale: yScale,
+ _datasetIndex: datasetIndex,
+ _index: index,
- // Built in hover styling
- if (this.active.length && this.options.hover.mode) {
- switch (this.options.hover.mode) {
- case 'single':
- this.active[0].backgroundColor = this.data.datasets[this.active[0]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.5).darken(0.35).rgbString();
- this.active[0].borderColor = this.data.datasets[this.active[0]._datasetIndex].hoverBorderColor || helpers.color(this.active[0].borderColor).saturate(0.5).darken(0.35).rgbString();
- this.active[0].borderWidth = this.data.datasets[this.active[0]._datasetIndex].borderWidth + 10;
- break;
- case 'label':
- for (var i = 0; i < this.active.length; i++) {
- this.active[i].backgroundColor = this.data.datasets[this.active[i]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[i].backgroundColor).saturate(0.5).darken(0.35).rgbString();
- this.active[i].borderColor = this.data.datasets[this.active[i]._datasetIndex].hoverBorderColor || helpers.color(this.active[i].borderColor).saturate(0.5).darken(0.35).rgbString();
- this.active[i].borderWidth = this.data.datasets[this.active[i]._datasetIndex].borderWidth + 2;
- }
- break;
- case 'dataset':
- break;
- default:
- // Don't change anything
- }
- }
+ // Desired view properties
+ _model: {
+ x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x), // value not used in dataset scale, but we want a consistent API between scales
+ y: yScalePoint,
+
+ // Appearance
+ tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
+ radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
+ backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
+ borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
+ borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
+ skip: (typeof this.data.datasets[datasetIndex].data[index].x != 'number') || (typeof this.data.datasets[datasetIndex].data[index].y != 'number'),
+
+ // Tooltip
+ hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
+ },
+ });
+ }, this);
- // Built in Tooltips
- if (this.options.tooltips.enabled) {
+ // Update control points for the bezier curve
+ this.eachElement(function(point, index, dataset, datasetIndex) {
+ var controlPoints = helpers.splineCurve(
+ this.previousPoint(dataset, index)._model,
+ point._model,
+ this.nextPoint(dataset, index)._model,
+ point._model.tension
+ );
- // The usual updates
- this.tooltip.initialize();
+ point._model.controlPointPreviousX = controlPoints.previous.x;
+ point._model.controlPointNextX = controlPoints.next.x;
- // Active
- if (this.active.length) {
- helpers.extend(this.tooltip, {
- opacity: 1,
- _active: this.active,
- });
+ // Prevent the bezier going outside of the bounds of the graph
- this.tooltip.update();
+ // Cap puter bezier handles to the upper/lower scale bounds
+ if (controlPoints.next.y > this.chartArea.bottom) {
+ point._model.controlPointNextY = this.chartArea.bottom;
+ } else if (controlPoints.next.y < this.chartArea.top) {
+ point._model.controlPointNextY = this.chartArea.top;
} else {
- // Inactive
- helpers.extend(this.tooltip, {
- opacity: 0,
- });
+ point._model.controlPointNextY = controlPoints.next.y;
}
- }
-
- // Hover animations
- this.tooltip.pivot();
-
- if (!this.animating) {
- var changed;
-
- helpers.each(this.active, function(element, index) {
- if (element !== this.lastActive[index]) {
- changed = true;
- }
- }, this);
- // If entering, leaving, or changing elements, animate the change via pivot
- if ((!this.lastActive.length && this.active.length) ||
- (this.lastActive.length && !this.active.length) ||
- (this.lastActive.length && this.active.length && changed)) {
-
- this.stop();
- this.render(this.options.hoverAnimationDuration);
+ // Cap inner bezier handles to the upper/lower scale bounds
+ if (controlPoints.previous.y > this.chartArea.bottom) {
+ point._model.controlPointPreviousY = this.chartArea.bottom;
+ } else if (controlPoints.previous.y < this.chartArea.top) {
+ point._model.controlPointPreviousY = this.chartArea.top;
+ } else {
+ point._model.controlPointPreviousY = controlPoints.previous.y;
}
- }
-
- // Remember Last Active
- this.lastActive = this.active;
- return this;
+ // Now pivot the point for animation
+ point.pivot();
+ }, this);
},
update: function() {
- Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
+ Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
// Update the lines
this.eachDataset(function(dataset, datasetIndex) {
+ var yScale = this.scales[dataset.yAxisID];
+
helpers.extend(dataset.metaDataset, {
- backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
- borderWidth: dataset.borderWidth || this.options.borderWidth,
- borderColor: dataset.borderColor || this.options.borderColor,
- tension: dataset.tension || this.options.tension,
- scaleTop: this.chartArea.top,
- scaleBottom: this.chartArea.bottom,
- _points: dataset.metaData,
+ // Utility
+ _scale: yScale,
_datasetIndex: datasetIndex,
+ // Data
+ _children: dataset.metaData,
+ // Model
+ _model: {
+ // Appearance
+ tension: dataset.tension || this.options.elements.line.tension,
+ backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
+ borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
+ borderColor: dataset.borderColor || this.options.elements.line.borderColor,
+ fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
+ skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
+ drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
+ // Scale
+ scaleTop: yScale.top,
+ scaleBottom: yScale.bottom,
+ scaleZero: yScale.getPixelForValue(0),
+ },
});
+
dataset.metaDataset.pivot();
});
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
helpers.extend(point, {
- x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x),
- y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y),
- dataX: this.data.datasets[datasetIndex].data[index].x,
- dataY: this.data.datasets[datasetIndex].data[index].y,
- label: '', // so that the multitooltip looks ok
- value: this.data.datasets[datasetIndex].data[index].y, // for legacy reasons
- datasetLabel: this.data.datasets[datasetIndex].label,
- // Appearance
- hoverBackgroundColor: this.data.datasets[datasetIndex].pointHoverBackgroundColor || this.options.pointHoverBackgroundColor,
- hoverBorderColor: this.data.datasets[datasetIndex].pointHoverBorderColor || this.options.pointHoverBorderColor,
- hoverRadius: this.data.datasets[datasetIndex].pointHoverRadius || this.options.pointHoverRadius,
- radius: this.data.datasets[datasetIndex].pointRadius || this.options.pointRadius,
- borderWidth: this.data.datasets[datasetIndex].pointBorderWidth || this.options.pointBorderWidth,
- borderColor: this.data.datasets[datasetIndex].pointBorderColor || this.options.pointBorderColor,
- backgroundColor: this.data.datasets[datasetIndex].pointBackgroundColor || this.options.pointBackgroundColor,
- tension: this.data.datasets[datasetIndex].metaDataset.tension,
+ // Utility
+ _chart: this.chart,
+ _xScale: xScale,
+ _yScale: yScale,
_datasetIndex: datasetIndex,
_index: index,
+
+ // Desired view properties
+ _model: {
+ x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x),
+ y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y),
+
+ // Appearance
+ tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
+ radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
+ backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
+ borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
+ borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
+ skip: (typeof this.data.datasets[datasetIndex].data[index].x != 'number') || (typeof this.data.datasets[datasetIndex].data[index].y != 'number'),
+
+ // Tooltip
+ hoverRadius: point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointHitRadius, index, this.options.elements.point.hitRadius),
+ },
});
}, this);
+
// Update control points for the bezier curve
this.eachElement(function(point, index, dataset, datasetIndex) {
var controlPoints = helpers.splineCurve(
- this.previousPoint(dataset, index),
- point,
- this.nextPoint(dataset, index),
- point.tension
+ this.previousPoint(dataset, index)._model,
+ point._model,
+ this.nextPoint(dataset, index)._model,
+ point._model.tension
);
- point.controlPointPreviousX = controlPoints.previous.x;
- point.controlPointNextX = controlPoints.next.x;
+ point._model.controlPointPreviousX = controlPoints.previous.x;
+ point._model.controlPointNextX = controlPoints.next.x;
// Prevent the bezier going outside of the bounds of the graph
// Cap puter bezier handles to the upper/lower scale bounds
if (controlPoints.next.y > this.chartArea.bottom) {
- point.controlPointNextY = this.chartArea.bottom;
+ point._model.controlPointNextY = this.chartArea.bottom;
} else if (controlPoints.next.y < this.chartArea.top) {
- point.controlPointNextY = this.chartArea.top;
+ point._model.controlPointNextY = this.chartArea.top;
} else {
- point.controlPointNextY = controlPoints.next.y;
+ point._model.controlPointNextY = controlPoints.next.y;
}
// Cap inner bezier handles to the upper/lower scale bounds
if (controlPoints.previous.y > this.chartArea.bottom) {
- point.controlPointPreviousY = this.chartArea.bottom;
+ point._model.controlPointPreviousY = this.chartArea.bottom;
} else if (controlPoints.previous.y < this.chartArea.top) {
- point.controlPointPreviousY = this.chartArea.top;
+ point._model.controlPointPreviousY = this.chartArea.top;
} else {
- point.controlPointPreviousY = controlPoints.previous.y;
+ point._model.controlPointPreviousY = controlPoints.previous.y;
}
+
// Now pivot the point for animation
point.pivot();
}, this);
options: yAxisOptions,
calculateRange: calculateYRange,
id: yAxisOptions.id,
+ getPointPixelForValue: function(value, index, datasetIndex) {
+ return this.getPixelForValue(value);
+ }
});
this.scales[scale.id] = scale;
}, this);
- },
- redraw: function() {
-
},
draw: function(ease) {
-
var easingDecimal = ease || 1;
this.clear();
-
- // Draw all the scales
- helpers.each(this.scales, function(scale) {
- scale.draw(this.chartArea);
- }, this);
- this.eachDataset(function(dataset, datasetIndex) {
+ // Draw all the scales
+ helpers.each(this.scales, function(scale) {
+ scale.draw(this.chartArea);
+ }, this);
+
+ // reverse for-loop for proper stacking
+ for (var i = this.data.datasets.length - 1; i >= 0; i--) {
+
+ var dataset = this.data.datasets[i];
+
// Transition Point Locations
helpers.each(dataset.metaData, function(point, index) {
point.transition(easingDecimal);
helpers.each(dataset.metaData, function(point) {
point.draw();
});
- }, this);
+ }
// Finally draw the tooltip
this.tooltip.transition(easingDecimal).draw();
- }
+ },
+ events: function(e) {
+ // If exiting chart
+ if (e.type == 'mouseout') {
+ return this;
+ }
+
+ this.lastActive = this.lastActive || [];
+
+ // Find Active Elements
+ this.active = function() {
+ switch (this.options.hover.mode) {
+ case 'single':
+ return this.getElementAtEvent(e);
+ case 'label':
+ return this.getElementsAtEvent(e);
+ case 'dataset':
+ return this.getDatasetAtEvent(e);
+ default:
+ return e;
+ }
+ }.call(this);
+
+ // On Hover hook
+ if (this.options.hover.onHover) {
+ this.options.hover.onHover.call(this, this.active);
+ }
+
+ var dataset;
+ var index;
+ // Remove styling for last active (even if it may still be active)
+ if (this.lastActive.length) {
+ switch (this.options.hover.mode) {
+ case 'single':
+ dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
+ index = this.lastActive[0]._index;
+
+ this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
+ this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
+ this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
+ this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
+ break;
+ case 'label':
+ for (var i = 0; i < this.lastActive.length; i++) {
+ dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
+ index = this.lastActive[i]._index;
+
+ this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
+ this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
+ this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
+ this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
+ }
+ break;
+ case 'dataset':
+ break;
+ default:
+ // Don't change anything
+ }
+ }
+
+ // Built in hover styling
+ if (this.active.length && this.options.hover.mode) {
+ switch (this.options.hover.mode) {
+ case 'single':
+ dataset = this.data.datasets[this.active[0]._datasetIndex];
+ index = this.active[0]._index;
+
+ this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
+ this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
+ this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
+ this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
+ break;
+ case 'label':
+ for (var i = 0; i < this.active.length; i++) {
+ dataset = this.data.datasets[this.active[i]._datasetIndex];
+ index = this.active[i]._index;
+
+ this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
+ this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.35).rgbString());
+ this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.35).rgbString());
+ this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
+ }
+ break;
+ case 'dataset':
+ break;
+ default:
+ // Don't change anything
+ }
+ }
+
+ // Built in Tooltips
+ if (this.options.tooltips.enabled) {
+
+ // The usual updates
+ this.tooltip.initialize();
+
+ // Active
+ if (this.active.length) {
+ helpers.extend(this.tooltip, {
+ opacity: 1,
+ _active: this.active,
+ });
+
+ this.tooltip.update();
+ } else {
+ // Inactive
+ helpers.extend(this.tooltip, {
+ opacity: 0,
+ });
+ }
+ }
+
+ // Hover animations
+ this.tooltip.pivot();
+
+ if (!this.animating) {
+ var changed;
+
+ helpers.each(this.active, function(element, index) {
+ if (element !== this.lastActive[index]) {
+ changed = true;
+ }
+ }, this);
+
+ // If entering, leaving, or changing elements, animate the change via pivot
+ if ((!this.lastActive.length && this.active.length) ||
+ (this.lastActive.length && !this.active.length) ||
+ (this.lastActive.length && this.active.length && changed)) {
+
+ this.stop();
+ this.render(this.options.hoverAnimationDuration);
+ }
+ }
+
+ // Remember Last Active
+ this.lastActive = this.active;
+ return this;
+
+ },
});