2015-06-15 03:15:10 +02:00
|
|
|
(function() {
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
//Declare root variable - window in the browser, global on the server
|
|
|
|
var root = this,
|
2015-10-22 18:47:45 +02:00
|
|
|
Chart = root.Chart,
|
2015-06-15 03:15:10 +02:00
|
|
|
helpers = Chart.helpers;
|
|
|
|
|
|
|
|
|
|
|
|
//Create a dictionary of chart types, to allow for extension of existing types
|
|
|
|
Chart.types = {};
|
|
|
|
|
|
|
|
//Store a reference to each instance - allowing us to globally resize chart instances on window resize.
|
|
|
|
//Destroy method on the chart will remove the instance of the chart from this reference.
|
|
|
|
Chart.instances = {};
|
|
|
|
|
2015-06-15 22:36:02 +02:00
|
|
|
// Controllers available for dataset visualization eg. bar, line, slice, etc.
|
|
|
|
Chart.controllers = {};
|
|
|
|
|
|
|
|
// The main controller of a chart
|
2015-06-15 03:15:10 +02:00
|
|
|
Chart.Controller = function(instance) {
|
|
|
|
|
|
|
|
this.chart = instance;
|
2015-06-15 22:36:02 +02:00
|
|
|
this.config = instance.config;
|
|
|
|
this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {});
|
2015-06-15 03:15:10 +02:00
|
|
|
this.id = helpers.uid();
|
|
|
|
|
2015-11-13 15:38:35 +01:00
|
|
|
Object.defineProperty(this, 'data', {
|
|
|
|
get: function() {
|
|
|
|
return this.config.data;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
//Add the chart instance to the global namespace
|
|
|
|
Chart.instances[this.id] = this;
|
|
|
|
|
|
|
|
if (this.options.responsive) {
|
2015-06-16 21:19:01 +02:00
|
|
|
// Silent resize before chart draws
|
|
|
|
this.resize(true);
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
this.initialize.call(this);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
|
|
|
helpers.extend(Chart.Controller.prototype, {
|
|
|
|
|
|
|
|
initialize: function initialize() {
|
2015-06-15 03:26:44 +02:00
|
|
|
|
2015-06-15 22:36:02 +02:00
|
|
|
// TODO
|
|
|
|
// If BeforeInit(this) doesn't return false, proceed
|
|
|
|
|
2015-06-15 03:26:44 +02:00
|
|
|
this.bindEvents();
|
2015-06-16 00:54:46 +02:00
|
|
|
|
|
|
|
// Make sure controllers are built first so that each dataset is bound to an axis before the scales
|
|
|
|
// are built
|
2015-06-17 01:20:26 +02:00
|
|
|
this.ensureScalesHaveIDs();
|
2015-06-17 04:04:52 +02:00
|
|
|
this.buildOrUpdateControllers();
|
2015-06-16 00:54:46 +02:00
|
|
|
this.buildScales();
|
2015-12-06 16:20:38 +01:00
|
|
|
this.buildSurroundingItems();
|
2015-10-26 20:57:59 +01:00
|
|
|
this.updateLayout();
|
2015-06-15 03:26:44 +02:00
|
|
|
this.resetElements();
|
|
|
|
this.initToolTip();
|
|
|
|
this.update();
|
|
|
|
|
2015-06-15 22:36:02 +02:00
|
|
|
// TODO
|
|
|
|
// If AfterInit(this) doesn't return false, proceed
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
clear: function clear() {
|
|
|
|
helpers.clear(this.chart);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
stop: function stop() {
|
|
|
|
// Stops any current animation loop occuring
|
|
|
|
Chart.animationService.cancelAnimation(this);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2015-06-16 21:19:01 +02:00
|
|
|
resize: function resize(silent) {
|
2015-06-15 03:15:10 +02:00
|
|
|
this.stop();
|
2015-09-20 16:30:12 +02:00
|
|
|
var canvas = this.chart.canvas;
|
|
|
|
var newWidth = helpers.getMaximumWidth(this.chart.canvas);
|
2015-09-24 05:52:31 +02:00
|
|
|
var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0) ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas);
|
2015-06-15 03:15:10 +02:00
|
|
|
|
|
|
|
canvas.width = this.chart.width = newWidth;
|
|
|
|
canvas.height = this.chart.height = newHeight;
|
|
|
|
|
|
|
|
helpers.retinaScale(this.chart);
|
|
|
|
|
2015-06-16 21:19:01 +02:00
|
|
|
if (!silent) {
|
|
|
|
this.update(this.options.responsiveAnimationDuration);
|
|
|
|
}
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
return this;
|
|
|
|
},
|
2015-06-17 01:20:26 +02:00
|
|
|
ensureScalesHaveIDs: function ensureScalesHaveIDs() {
|
|
|
|
var defaultXAxisID = 'x-axis-';
|
|
|
|
var defaultYAxisID = 'y-axis-';
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-06-17 01:20:26 +02:00
|
|
|
if (this.options.scales) {
|
|
|
|
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
|
|
|
|
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
|
|
|
|
xAxisOptions.id = xAxisOptions.id || (defaultXAxisID + index);
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
|
|
|
|
// Build the y axes
|
|
|
|
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
|
|
|
|
yAxisOptions.id = yAxisOptions.id || (defaultYAxisID + index);
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2015-06-15 03:26:44 +02:00
|
|
|
buildScales: function buildScales() {
|
2015-10-31 23:07:36 +01:00
|
|
|
// Map of scale ID to scale object so we can lookup later
|
2015-06-15 03:26:44 +02:00
|
|
|
this.scales = {};
|
|
|
|
|
|
|
|
// Build the x axes
|
2015-06-16 03:14:11 +02:00
|
|
|
if (this.options.scales) {
|
|
|
|
if (this.options.scales.xAxes && this.options.scales.xAxes.length) {
|
2015-06-17 01:20:26 +02:00
|
|
|
helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) {
|
2015-06-16 03:14:11 +02:00
|
|
|
var ScaleClass = Chart.scaleService.getScaleConstructor(xAxisOptions.type);
|
|
|
|
var scale = new ScaleClass({
|
|
|
|
ctx: this.chart.ctx,
|
|
|
|
options: xAxisOptions,
|
2015-11-13 15:38:35 +01:00
|
|
|
chart: this,
|
2015-06-16 03:14:11 +02:00
|
|
|
id: xAxisOptions.id,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.scales[scale.id] = scale;
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.options.scales.yAxes && this.options.scales.yAxes.length) {
|
|
|
|
// Build the y axes
|
2015-06-17 01:20:26 +02:00
|
|
|
helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) {
|
2015-06-16 03:14:11 +02:00
|
|
|
var ScaleClass = Chart.scaleService.getScaleConstructor(yAxisOptions.type);
|
|
|
|
var scale = new ScaleClass({
|
|
|
|
ctx: this.chart.ctx,
|
|
|
|
options: yAxisOptions,
|
2015-11-13 15:38:35 +01:00
|
|
|
chart: this,
|
2015-06-16 03:14:11 +02:00
|
|
|
id: yAxisOptions.id,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.scales[scale.id] = scale;
|
|
|
|
}, this);
|
|
|
|
}
|
2015-06-16 08:17:26 +02:00
|
|
|
}
|
|
|
|
if (this.options.scale) {
|
|
|
|
// Build radial axes
|
|
|
|
var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scale.type);
|
|
|
|
var scale = new ScaleClass({
|
|
|
|
ctx: this.chart.ctx,
|
|
|
|
options: this.options.scale,
|
2015-11-13 15:38:35 +01:00
|
|
|
chart: this,
|
2015-06-16 08:17:26 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
this.scale = scale;
|
2015-09-27 17:58:20 +02:00
|
|
|
|
2015-10-21 02:33:51 +02:00
|
|
|
this.scales.radialScale = scale;
|
2015-06-16 03:14:11 +02:00
|
|
|
}
|
2015-06-15 03:42:39 +02:00
|
|
|
|
2015-10-26 20:57:59 +01:00
|
|
|
Chart.scaleService.addScalesToLayout(this);
|
|
|
|
},
|
|
|
|
|
2015-12-06 16:20:38 +01:00
|
|
|
buildSurroundingItems: function() {
|
|
|
|
if (this.options.title) {
|
|
|
|
this.titleBlock = new Chart.Title({
|
|
|
|
ctx: this.chart.ctx,
|
|
|
|
options: this.options.title,
|
|
|
|
chart: this
|
|
|
|
});
|
|
|
|
|
|
|
|
Chart.layoutService.addBox(this, this.titleBlock);
|
2015-10-26 20:57:59 +01:00
|
|
|
}
|
|
|
|
|
2015-12-06 16:20:38 +01:00
|
|
|
if (this.options.legend) {
|
|
|
|
this.legend = new Chart.Legend({
|
|
|
|
ctx: this.chart.ctx,
|
|
|
|
options: this.options.legend,
|
|
|
|
chart: this,
|
|
|
|
});
|
2015-10-26 20:57:59 +01:00
|
|
|
|
2015-12-06 16:20:38 +01:00
|
|
|
Chart.layoutService.addBox(this, this.legend);
|
|
|
|
}
|
2015-10-26 20:57:59 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
updateLayout: function() {
|
|
|
|
Chart.layoutService.update(this, this.chart.width, this.chart.height);
|
2015-06-15 03:26:44 +02:00
|
|
|
},
|
|
|
|
|
2015-09-22 02:59:53 +02:00
|
|
|
buildOrUpdateControllers: function buildOrUpdateControllers(resetNewControllers) {
|
2015-09-24 10:25:21 +02:00
|
|
|
var types = [];
|
2015-06-16 00:18:56 +02:00
|
|
|
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
2015-09-24 20:07:40 +02:00
|
|
|
if (!dataset.type) {
|
|
|
|
dataset.type = this.config.type;
|
|
|
|
}
|
2015-11-13 15:38:35 +01:00
|
|
|
|
2015-09-24 20:07:40 +02:00
|
|
|
var type = dataset.type;
|
2015-09-24 10:25:21 +02:00
|
|
|
types.push(type);
|
2015-11-13 15:38:35 +01:00
|
|
|
|
2015-06-15 22:36:02 +02:00
|
|
|
if (dataset.controller) {
|
|
|
|
dataset.controller.updateIndex(datasetIndex);
|
2015-12-09 01:57:58 +01:00
|
|
|
} else {
|
|
|
|
dataset.controller = new Chart.controllers[type](this, datasetIndex);
|
2015-09-22 02:59:53 +02:00
|
|
|
|
2015-12-09 01:57:58 +01:00
|
|
|
if (resetNewControllers) {
|
|
|
|
dataset.controller.reset();
|
|
|
|
}
|
2015-09-22 02:59:53 +02:00
|
|
|
}
|
2015-06-16 00:18:56 +02:00
|
|
|
}, this);
|
2015-11-13 15:38:35 +01:00
|
|
|
|
2015-09-24 10:25:21 +02:00
|
|
|
if (types.length > 1) {
|
|
|
|
for (var i = 1; i < types.length; i++) {
|
|
|
|
if (types[i] != types[i - 1]) {
|
|
|
|
this.isCombo = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-15 22:36:02 +02:00
|
|
|
},
|
|
|
|
|
2015-06-15 03:26:44 +02:00
|
|
|
resetElements: function resetElements() {
|
2015-06-16 00:18:56 +02:00
|
|
|
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
2015-06-15 22:36:02 +02:00
|
|
|
dataset.controller.reset();
|
2015-06-16 00:18:56 +02:00
|
|
|
}, this);
|
2015-06-15 03:26:44 +02:00
|
|
|
},
|
2015-06-15 22:36:02 +02:00
|
|
|
|
2015-06-19 21:29:36 +02:00
|
|
|
update: function update(animationDuration, lazy) {
|
2015-11-13 15:38:35 +01:00
|
|
|
// In case the entire data object changed
|
|
|
|
this.tooltip._data = this.data;
|
|
|
|
|
2015-09-22 02:59:53 +02:00
|
|
|
// Make sure dataset controllers are updated and new controllers are reset
|
|
|
|
this.buildOrUpdateControllers(true);
|
|
|
|
|
2015-12-12 15:01:59 +01:00
|
|
|
Chart.layoutService.update(this, this.chart.width, this.chart.height);
|
2015-12-09 01:57:58 +01:00
|
|
|
|
2015-09-22 02:59:53 +02:00
|
|
|
// Make sure all dataset controllers have correct meta data counts
|
|
|
|
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
|
|
|
dataset.controller.buildOrUpdateElements();
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
// This will loop through any data and do the appropriate element update for the type
|
2015-06-16 00:18:56 +02:00
|
|
|
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
2015-06-15 22:36:02 +02:00
|
|
|
dataset.controller.update();
|
2015-06-16 00:18:56 +02:00
|
|
|
}, this);
|
2015-06-19 21:29:36 +02:00
|
|
|
this.render(animationDuration, lazy);
|
2015-06-15 03:15:10 +02:00
|
|
|
},
|
|
|
|
|
2015-06-19 21:29:36 +02:00
|
|
|
render: function render(duration, lazy) {
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-12-12 16:01:08 +01:00
|
|
|
if (this.options.animation && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration == 'undefined' && this.options.animation.duration !== 0))) {
|
2015-06-15 03:15:10 +02:00
|
|
|
var animation = new Chart.Animation();
|
|
|
|
animation.numSteps = (duration || this.options.animation.duration) / 16.66; //60 fps
|
|
|
|
animation.easing = this.options.animation.easing;
|
|
|
|
|
|
|
|
// render function
|
|
|
|
animation.render = function(chartInstance, animationObject) {
|
|
|
|
var easingFunction = helpers.easingEffects[animationObject.easing];
|
|
|
|
var stepDecimal = animationObject.currentStep / animationObject.numSteps;
|
|
|
|
var easeDecimal = easingFunction(stepDecimal);
|
|
|
|
|
|
|
|
chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
|
|
|
|
};
|
|
|
|
|
|
|
|
// user events
|
2015-12-12 16:01:08 +01:00
|
|
|
animation.onAnimationProgress = this.options.animation.onProgress;
|
|
|
|
animation.onAnimationComplete = this.options.animation.onComplete;
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-06-19 21:29:36 +02:00
|
|
|
Chart.animationService.addAnimation(this, animation, duration, lazy);
|
2015-06-15 03:15:10 +02:00
|
|
|
} else {
|
|
|
|
this.draw();
|
2015-12-12 16:01:08 +01:00
|
|
|
if (this.options.animation && this.options.animation.onComplete && this.options.animation.onComplete.call) {
|
|
|
|
this.options.animation.onComplete.call(this);
|
2015-08-07 21:39:09 +02:00
|
|
|
}
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2015-06-15 22:36:02 +02:00
|
|
|
draw: function(ease) {
|
|
|
|
var easingDecimal = ease || 1;
|
|
|
|
this.clear();
|
|
|
|
|
|
|
|
// Draw all the scales
|
2015-10-26 20:57:59 +01:00
|
|
|
helpers.each(this.boxes, function(box) {
|
|
|
|
box.draw(this.chartArea);
|
2015-06-15 22:36:02 +02:00
|
|
|
}, this);
|
2015-06-16 08:17:26 +02:00
|
|
|
if (this.scale) {
|
|
|
|
this.scale.draw();
|
|
|
|
}
|
2015-06-15 22:36:02 +02:00
|
|
|
|
2015-06-16 19:37:07 +02:00
|
|
|
// Draw each dataset via its respective controller (reversed to support proper line stacking)
|
2015-06-15 22:36:02 +02:00
|
|
|
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
2015-10-08 01:54:11 +02:00
|
|
|
if (helpers.isDatasetVisible(dataset)) {
|
|
|
|
dataset.controller.draw(ease);
|
|
|
|
}
|
2015-06-19 22:23:05 +02:00
|
|
|
}, this);
|
2015-06-15 22:36:02 +02:00
|
|
|
|
|
|
|
// Finally draw the tooltip
|
|
|
|
this.tooltip.transition(easingDecimal).draw();
|
|
|
|
},
|
|
|
|
|
2015-06-15 03:26:44 +02:00
|
|
|
// Get the single element that was clicked on
|
|
|
|
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
|
2015-06-16 00:18:56 +02:00
|
|
|
getElementAtEvent: function(e) {
|
2015-06-15 03:26:44 +02:00
|
|
|
|
2015-09-23 01:22:55 +02:00
|
|
|
var eventPosition = helpers.getRelativePosition(e, this.chart);
|
2015-06-16 06:43:45 +02:00
|
|
|
var elementsArray = [];
|
2015-06-15 03:26:44 +02:00
|
|
|
|
2015-06-16 00:18:56 +02:00
|
|
|
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
2015-10-08 01:54:11 +02:00
|
|
|
if (helpers.isDatasetVisible(dataset)) {
|
|
|
|
helpers.each(dataset.metaData, function(element, index) {
|
|
|
|
if (element.inRange(eventPosition.x, eventPosition.y)) {
|
|
|
|
elementsArray.push(element);
|
|
|
|
return elementsArray;
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
}
|
2015-06-16 00:18:56 +02:00
|
|
|
}, this);
|
2015-06-15 03:26:44 +02:00
|
|
|
|
2015-06-16 06:43:45 +02:00
|
|
|
return elementsArray;
|
2015-06-15 03:26:44 +02:00
|
|
|
},
|
|
|
|
|
2015-06-16 00:18:56 +02:00
|
|
|
getElementsAtEvent: function(e) {
|
2015-09-23 01:22:55 +02:00
|
|
|
var eventPosition = helpers.getRelativePosition(e, this.chart);
|
2015-06-16 00:18:56 +02:00
|
|
|
var elementsArray = [];
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-11-02 06:44:31 +01:00
|
|
|
var found = (function(){
|
|
|
|
for (var i = 0; i < this.data.datasets.length; i++) {
|
|
|
|
if (helpers.isDatasetVisible(this.data.datasets[i])) {
|
|
|
|
for (var j = 0; j < this.data.datasets[i].metaData.length; j++) {
|
|
|
|
if (this.data.datasets[i].metaData[j].inRange(eventPosition.x, eventPosition.y)) {
|
|
|
|
return this.data.datasets[i].metaData[j];
|
|
|
|
}
|
2015-10-08 01:54:11 +02:00
|
|
|
}
|
2015-11-02 06:44:31 +01:00
|
|
|
}
|
2015-10-08 01:54:11 +02:00
|
|
|
}
|
2015-11-02 06:44:31 +01:00
|
|
|
}).call(this);
|
|
|
|
|
|
|
|
if(!found){
|
|
|
|
return elementsArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
helpers.each(this.data.datasets, function(dataset, dsIndex){
|
2015-11-02 06:50:16 +01:00
|
|
|
if(helpers.isDatasetVisible(dataset)){
|
|
|
|
elementsArray.push(dataset.metaData[found._index]);
|
|
|
|
}
|
2015-06-16 00:18:56 +02:00
|
|
|
}, this);
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-06-16 00:18:56 +02:00
|
|
|
return elementsArray;
|
2015-06-15 03:15:10 +02:00
|
|
|
},
|
|
|
|
|
2015-06-16 00:18:56 +02:00
|
|
|
getDatasetAtEvent: function(e) {
|
2015-12-05 02:01:04 +01:00
|
|
|
var elementsArray = this.getElementAtEvent(e);
|
2015-06-15 03:42:39 +02:00
|
|
|
|
2015-12-05 02:01:04 +01:00
|
|
|
if (elementsArray.length > 0) {
|
|
|
|
elementsArray = this.data.datasets[elementsArray[0]._datasetIndex].metaData;
|
|
|
|
}
|
2015-06-15 03:42:39 +02:00
|
|
|
|
2015-12-05 02:01:04 +01:00
|
|
|
return elementsArray;
|
2015-06-15 03:42:39 +02:00
|
|
|
},
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
generateLegend: function generateLegend() {
|
2015-10-07 04:40:25 +02:00
|
|
|
return this.options.legendCallback(this);
|
2015-06-15 03:15:10 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function destroy() {
|
|
|
|
this.clear();
|
2015-06-15 03:26:44 +02:00
|
|
|
helpers.unbindEvents(this, this.events);
|
2015-09-20 16:30:12 +02:00
|
|
|
helpers.removeResizeListener(this.chart.canvas.parentNode);
|
2015-09-19 23:52:58 +02:00
|
|
|
|
2015-09-20 16:30:12 +02:00
|
|
|
// Reset canvas height/width attributes
|
|
|
|
var canvas = this.chart.canvas;
|
2015-06-15 03:15:10 +02:00
|
|
|
canvas.width = this.chart.width;
|
|
|
|
canvas.height = this.chart.height;
|
|
|
|
|
2015-09-20 16:30:12 +02:00
|
|
|
// if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here
|
|
|
|
if (this.chart.originalDevicePixelRatio !== undefined) {
|
2015-09-23 01:22:55 +02:00
|
|
|
this.chart.ctx.scale(1 / this.chart.originalDevicePixelRatio, 1 / this.chart.originalDevicePixelRatio);
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
|
2015-09-23 01:22:55 +02:00
|
|
|
// Reset to the old style since it may have been changed by the device pixel ratio changes
|
|
|
|
canvas.style.width = this.chart.originalCanvasStyleWidth;
|
|
|
|
canvas.style.height = this.chart.originalCanvasStyleHeight;
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
delete Chart.instances[this.id];
|
|
|
|
},
|
|
|
|
|
|
|
|
toBase64Image: function toBase64Image() {
|
|
|
|
return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
|
|
|
|
},
|
2015-06-15 03:42:39 +02:00
|
|
|
|
|
|
|
initToolTip: function initToolTip() {
|
|
|
|
this.tooltip = new Chart.Tooltip({
|
|
|
|
_chart: this.chart,
|
|
|
|
_data: this.data,
|
|
|
|
_options: this.options,
|
|
|
|
}, this);
|
|
|
|
},
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
bindEvents: function bindEvents() {
|
|
|
|
helpers.bindEvents(this, this.options.events, function(evt) {
|
2015-06-15 03:42:39 +02:00
|
|
|
this.eventHandler(evt);
|
2015-06-15 03:15:10 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
eventHandler: function eventHandler(e) {
|
|
|
|
this.lastActive = this.lastActive || [];
|
2015-10-23 20:40:38 +02:00
|
|
|
this.lastTooltipActive = this.lastTooltipActive || [];
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-10-07 04:40:25 +02:00
|
|
|
// Find Active Elements for hover and tooltips
|
2015-06-15 03:15:10 +02:00
|
|
|
if (e.type == 'mouseout') {
|
2015-11-01 01:15:58 +01:00
|
|
|
this.active = [];
|
|
|
|
this.tooltipActive = [];
|
2015-06-15 03:15:10 +02:00
|
|
|
} else {
|
2015-11-22 04:44:11 +01:00
|
|
|
|
|
|
|
var _this = this;
|
2015-12-04 03:25:36 +01:00
|
|
|
var getItemsForMode = function(mode) {
|
2015-11-22 04:44:11 +01:00
|
|
|
switch (mode) {
|
2015-10-07 04:40:25 +02:00
|
|
|
case 'single':
|
2015-11-22 04:44:11 +01:00
|
|
|
return _this.getElementAtEvent(e);
|
2015-10-07 04:40:25 +02:00
|
|
|
case 'label':
|
2015-11-22 04:44:11 +01:00
|
|
|
return _this.getElementsAtEvent(e);
|
2015-10-07 04:40:25 +02:00
|
|
|
case 'dataset':
|
2015-11-22 04:44:11 +01:00
|
|
|
return _this.getDatasetAtEvent(e);
|
2015-10-07 04:40:25 +02:00
|
|
|
default:
|
|
|
|
return e;
|
|
|
|
}
|
2015-12-04 03:25:36 +01:00
|
|
|
};
|
2015-11-22 04:44:11 +01:00
|
|
|
|
|
|
|
this.active = getItemsForMode(this.options.hover.mode);
|
|
|
|
this.tooltipActive = getItemsForMode(this.options.tooltips.mode);
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// On Hover hook
|
|
|
|
if (this.options.hover.onHover) {
|
|
|
|
this.options.hover.onHover.call(this, this.active);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.type == 'mouseup' || e.type == 'click') {
|
|
|
|
if (this.options.onClick) {
|
|
|
|
this.options.onClick.call(this, e, this.active);
|
|
|
|
}
|
2015-11-23 04:31:08 +01:00
|
|
|
|
|
|
|
if (this.legend && this.legend.handleEvent) {
|
|
|
|
this.legend.handleEvent(e);
|
|
|
|
}
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var dataset;
|
|
|
|
var index;
|
2015-10-07 04:40:25 +02:00
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
// Remove styling for last active (even if it may still be active)
|
|
|
|
if (this.lastActive.length) {
|
|
|
|
switch (this.options.hover.mode) {
|
|
|
|
case 'single':
|
2015-06-16 00:18:56 +02:00
|
|
|
this.data.datasets[this.lastActive[0]._datasetIndex].controller.removeHoverStyle(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index);
|
2015-06-15 03:15:10 +02:00
|
|
|
break;
|
|
|
|
case 'label':
|
2015-09-17 09:43:02 +02:00
|
|
|
case 'dataset':
|
2015-06-15 03:15:10 +02:00
|
|
|
for (var i = 0; i < this.lastActive.length; i++) {
|
2015-11-10 01:00:30 +01:00
|
|
|
if (this.lastActive[i])
|
|
|
|
this.data.datasets[this.lastActive[i]._datasetIndex].controller.removeHoverStyle(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index);
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
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':
|
2015-06-16 00:18:56 +02:00
|
|
|
this.data.datasets[this.active[0]._datasetIndex].controller.setHoverStyle(this.active[0]);
|
2015-06-15 03:15:10 +02:00
|
|
|
break;
|
|
|
|
case 'label':
|
2015-09-17 09:43:02 +02:00
|
|
|
case 'dataset':
|
2015-10-21 02:33:51 +02:00
|
|
|
for (var j = 0; j < this.active.length; j++) {
|
2015-11-10 01:00:30 +01:00
|
|
|
if (this.active[j])
|
|
|
|
this.data.datasets[this.active[j]._datasetIndex].controller.setHoverStyle(this.active[j]);
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Don't change anything
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Built in Tooltips
|
2015-06-22 21:10:49 +02:00
|
|
|
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
|
2015-06-15 03:15:10 +02:00
|
|
|
|
|
|
|
// The usual updates
|
|
|
|
this.tooltip.initialize();
|
2015-11-01 01:16:35 +01:00
|
|
|
this.tooltip._active = this.tooltipActive;
|
2015-11-01 21:56:36 +01:00
|
|
|
this.tooltip.update();
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2015-10-23 20:40:38 +02:00
|
|
|
helpers.each(this.tooltipActive, function(element, index) {
|
|
|
|
if (element !== this.lastTooltipActive[index]) {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
2015-06-15 03:15:10 +02:00
|
|
|
// If entering, leaving, or changing elements, animate the change via pivot
|
2015-10-31 23:07:36 +01:00
|
|
|
if ((this.lastActive.length !== this.active.length) ||
|
|
|
|
(this.lastTooltipActive.length !== this.tooltipActive.length) ||
|
2015-11-01 01:17:06 +01:00
|
|
|
changed) {
|
2015-06-15 03:15:10 +02:00
|
|
|
|
2015-11-01 21:56:36 +01:00
|
|
|
this.stop();
|
|
|
|
|
2015-11-01 01:17:32 +01:00
|
|
|
if (this.options.tooltips.enabled || this.options.tooltips.custom) {
|
2015-11-01 21:56:36 +01:00
|
|
|
this.tooltip.update(true);
|
2015-11-01 01:17:32 +01:00
|
|
|
}
|
2015-09-24 05:52:31 +02:00
|
|
|
|
2015-10-31 23:07:36 +01:00
|
|
|
// We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more
|
2015-08-09 00:12:01 +02:00
|
|
|
// memory than necessary.
|
|
|
|
this.render(this.options.hover.animationDuration, true);
|
2015-06-15 03:15:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-23 20:40:38 +02:00
|
|
|
// Remember Last Actives
|
2015-06-15 03:15:10 +02:00
|
|
|
this.lastActive = this.active;
|
2015-10-23 20:40:38 +02:00
|
|
|
this.lastTooltipActive = this.tooltipActive;
|
2015-06-15 03:15:10 +02:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
}).call(this);
|