mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-04 15:34:04 +01:00
For consistency, enable ESLint `one-var` rule to require multiple variable declarations for initialized variables per scope. Uninitialized variables can still be declared together (preferred) or separately. http://eslint.org/docs/rules/one-var
331 lines
8.1 KiB
JavaScript
331 lines
8.1 KiB
JavaScript
'use strict';
|
|
|
|
var helpers = require('../helpers/index');
|
|
|
|
module.exports = function(Chart) {
|
|
|
|
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
|
|
|
|
/**
|
|
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
|
|
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are
|
|
* called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
|
|
*/
|
|
function listenArrayEvents(array, listener) {
|
|
if (array._chartjs) {
|
|
array._chartjs.listeners.push(listener);
|
|
return;
|
|
}
|
|
|
|
Object.defineProperty(array, '_chartjs', {
|
|
configurable: true,
|
|
enumerable: false,
|
|
value: {
|
|
listeners: [listener]
|
|
}
|
|
});
|
|
|
|
arrayEvents.forEach(function(key) {
|
|
var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
|
|
var base = array[key];
|
|
|
|
Object.defineProperty(array, key, {
|
|
configurable: true,
|
|
enumerable: false,
|
|
value: function() {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
var res = base.apply(this, args);
|
|
|
|
helpers.each(array._chartjs.listeners, function(object) {
|
|
if (typeof object[method] === 'function') {
|
|
object[method].apply(object, args);
|
|
}
|
|
});
|
|
|
|
return res;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Removes the given array event listener and cleanup extra attached properties (such as
|
|
* the _chartjs stub and overridden methods) if array doesn't have any more listeners.
|
|
*/
|
|
function unlistenArrayEvents(array, listener) {
|
|
var stub = array._chartjs;
|
|
if (!stub) {
|
|
return;
|
|
}
|
|
|
|
var listeners = stub.listeners;
|
|
var index = listeners.indexOf(listener);
|
|
if (index !== -1) {
|
|
listeners.splice(index, 1);
|
|
}
|
|
|
|
if (listeners.length > 0) {
|
|
return;
|
|
}
|
|
|
|
arrayEvents.forEach(function(key) {
|
|
delete array[key];
|
|
});
|
|
|
|
delete array._chartjs;
|
|
}
|
|
|
|
// Base class for all dataset controllers (line, bar, etc)
|
|
Chart.DatasetController = function(chart, datasetIndex) {
|
|
this.initialize(chart, datasetIndex);
|
|
};
|
|
|
|
helpers.extend(Chart.DatasetController.prototype, {
|
|
|
|
/**
|
|
* Element type used to generate a meta dataset (e.g. Chart.element.Line).
|
|
* @type {Chart.core.element}
|
|
*/
|
|
datasetElementType: null,
|
|
|
|
/**
|
|
* Element type used to generate a meta data (e.g. Chart.element.Point).
|
|
* @type {Chart.core.element}
|
|
*/
|
|
dataElementType: null,
|
|
|
|
initialize: function(chart, datasetIndex) {
|
|
var me = this;
|
|
me.chart = chart;
|
|
me.index = datasetIndex;
|
|
me.linkScales();
|
|
me.addElements();
|
|
},
|
|
|
|
updateIndex: function(datasetIndex) {
|
|
this.index = datasetIndex;
|
|
},
|
|
|
|
linkScales: function() {
|
|
var me = this;
|
|
var meta = me.getMeta();
|
|
var dataset = me.getDataset();
|
|
|
|
if (meta.xAxisID === null) {
|
|
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
|
|
}
|
|
if (meta.yAxisID === null) {
|
|
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
|
|
}
|
|
},
|
|
|
|
getDataset: function() {
|
|
return this.chart.data.datasets[this.index];
|
|
},
|
|
|
|
getMeta: function() {
|
|
return this.chart.getDatasetMeta(this.index);
|
|
},
|
|
|
|
getScaleForId: function(scaleID) {
|
|
return this.chart.scales[scaleID];
|
|
},
|
|
|
|
reset: function() {
|
|
this.update(true);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
destroy: function() {
|
|
if (this._data) {
|
|
unlistenArrayEvents(this._data, this);
|
|
}
|
|
},
|
|
|
|
createMetaDataset: function() {
|
|
var me = this;
|
|
var type = me.datasetElementType;
|
|
return type && new type({
|
|
_chart: me.chart,
|
|
_datasetIndex: me.index
|
|
});
|
|
},
|
|
|
|
createMetaData: function(index) {
|
|
var me = this;
|
|
var type = me.dataElementType;
|
|
return type && new type({
|
|
_chart: me.chart,
|
|
_datasetIndex: me.index,
|
|
_index: index
|
|
});
|
|
},
|
|
|
|
addElements: function() {
|
|
var me = this;
|
|
var meta = me.getMeta();
|
|
var data = me.getDataset().data || [];
|
|
var metaData = meta.data;
|
|
var i, ilen;
|
|
|
|
for (i = 0, ilen = data.length; i < ilen; ++i) {
|
|
metaData[i] = metaData[i] || me.createMetaData(i);
|
|
}
|
|
|
|
meta.dataset = meta.dataset || me.createMetaDataset();
|
|
},
|
|
|
|
addElementAndReset: function(index) {
|
|
var element = this.createMetaData(index);
|
|
this.getMeta().data.splice(index, 0, element);
|
|
this.updateElement(element, index, true);
|
|
},
|
|
|
|
buildOrUpdateElements: function() {
|
|
var me = this;
|
|
var dataset = me.getDataset();
|
|
var data = dataset.data || (dataset.data = []);
|
|
|
|
// In order to correctly handle data addition/deletion animation (an thus simulate
|
|
// real-time charts), we need to monitor these data modifications and synchronize
|
|
// the internal meta data accordingly.
|
|
if (me._data !== data) {
|
|
if (me._data) {
|
|
// This case happens when the user replaced the data array instance.
|
|
unlistenArrayEvents(me._data, me);
|
|
}
|
|
|
|
listenArrayEvents(data, me);
|
|
me._data = data;
|
|
}
|
|
|
|
// Re-sync meta data in case the user replaced the data array or if we missed
|
|
// any updates and so make sure that we handle number of datapoints changing.
|
|
me.resyncElements();
|
|
},
|
|
|
|
update: helpers.noop,
|
|
|
|
transition: function(easingValue) {
|
|
var meta = this.getMeta();
|
|
var elements = meta.data || [];
|
|
var ilen = elements.length;
|
|
var i = 0;
|
|
|
|
for (; i < ilen; ++i) {
|
|
elements[i].transition(easingValue);
|
|
}
|
|
|
|
if (meta.dataset) {
|
|
meta.dataset.transition(easingValue);
|
|
}
|
|
},
|
|
|
|
draw: function() {
|
|
var meta = this.getMeta();
|
|
var elements = meta.data || [];
|
|
var ilen = elements.length;
|
|
var i = 0;
|
|
|
|
if (meta.dataset) {
|
|
meta.dataset.draw();
|
|
}
|
|
|
|
for (; i < ilen; ++i) {
|
|
elements[i].draw();
|
|
}
|
|
},
|
|
|
|
removeHoverStyle: function(element, elementOpts) {
|
|
var dataset = this.chart.data.datasets[element._datasetIndex];
|
|
var index = element._index;
|
|
var custom = element.custom || {};
|
|
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
|
var model = element._model;
|
|
|
|
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
|
|
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
|
|
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
|
|
},
|
|
|
|
setHoverStyle: function(element) {
|
|
var dataset = this.chart.data.datasets[element._datasetIndex];
|
|
var index = element._index;
|
|
var custom = element.custom || {};
|
|
var valueOrDefault = helpers.valueAtIndexOrDefault;
|
|
var getHoverColor = helpers.getHoverColor;
|
|
var model = element._model;
|
|
|
|
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
|
|
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
|
|
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
resyncElements: function() {
|
|
var me = this;
|
|
var meta = me.getMeta();
|
|
var data = me.getDataset().data;
|
|
var numMeta = meta.data.length;
|
|
var numData = data.length;
|
|
|
|
if (numData < numMeta) {
|
|
meta.data.splice(numData, numMeta - numData);
|
|
} else if (numData > numMeta) {
|
|
me.insertElements(numMeta, numData - numMeta);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
insertElements: function(start, count) {
|
|
for (var i = 0; i < count; ++i) {
|
|
this.addElementAndReset(start + i);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
onDataPush: function() {
|
|
this.insertElements(this.getDataset().data.length - 1, arguments.length);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
onDataPop: function() {
|
|
this.getMeta().data.pop();
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
onDataShift: function() {
|
|
this.getMeta().data.shift();
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
onDataSplice: function(start, count) {
|
|
this.getMeta().data.splice(start, count);
|
|
this.insertElements(start, arguments.length - 2);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
onDataUnshift: function() {
|
|
this.insertElements(0, arguments.length);
|
|
}
|
|
});
|
|
|
|
Chart.DatasetController.extend = helpers.inherits;
|
|
};
|