mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-06 00:14:03 +01:00
Cached plugin descriptors hold a reference on the plugin options, which break if the plugin options object is replaced. That case happens when the user updates the plugin options with a new object, but also since the new config update logic (#4198) that now always clones the plugin options. The fix consists in explicitly invalidating that cache before updating the chart.
383 lines
13 KiB
JavaScript
383 lines
13 KiB
JavaScript
'use strict';
|
|
|
|
var defaults = require('./core.defaults');
|
|
var helpers = require('../helpers/index');
|
|
|
|
defaults._set('global', {
|
|
plugins: {}
|
|
});
|
|
|
|
/**
|
|
* The plugin service singleton
|
|
* @namespace Chart.plugins
|
|
* @since 2.1.0
|
|
*/
|
|
module.exports = {
|
|
/**
|
|
* Globally registered plugins.
|
|
* @private
|
|
*/
|
|
_plugins: [],
|
|
|
|
/**
|
|
* This identifier is used to invalidate the descriptors cache attached to each chart
|
|
* when a global plugin is registered or unregistered. In this case, the cache ID is
|
|
* incremented and descriptors are regenerated during following API calls.
|
|
* @private
|
|
*/
|
|
_cacheId: 0,
|
|
|
|
/**
|
|
* Registers the given plugin(s) if not already registered.
|
|
* @param {Array|Object} plugins plugin instance(s).
|
|
*/
|
|
register: function(plugins) {
|
|
var p = this._plugins;
|
|
([]).concat(plugins).forEach(function(plugin) {
|
|
if (p.indexOf(plugin) === -1) {
|
|
p.push(plugin);
|
|
}
|
|
});
|
|
|
|
this._cacheId++;
|
|
},
|
|
|
|
/**
|
|
* Unregisters the given plugin(s) only if registered.
|
|
* @param {Array|Object} plugins plugin instance(s).
|
|
*/
|
|
unregister: function(plugins) {
|
|
var p = this._plugins;
|
|
([]).concat(plugins).forEach(function(plugin) {
|
|
var idx = p.indexOf(plugin);
|
|
if (idx !== -1) {
|
|
p.splice(idx, 1);
|
|
}
|
|
});
|
|
|
|
this._cacheId++;
|
|
},
|
|
|
|
/**
|
|
* Remove all registered plugins.
|
|
* @since 2.1.5
|
|
*/
|
|
clear: function() {
|
|
this._plugins = [];
|
|
this._cacheId++;
|
|
},
|
|
|
|
/**
|
|
* Returns the number of registered plugins?
|
|
* @returns {Number}
|
|
* @since 2.1.5
|
|
*/
|
|
count: function() {
|
|
return this._plugins.length;
|
|
},
|
|
|
|
/**
|
|
* Returns all registered plugin instances.
|
|
* @returns {Array} array of plugin objects.
|
|
* @since 2.1.5
|
|
*/
|
|
getAll: function() {
|
|
return this._plugins;
|
|
},
|
|
|
|
/**
|
|
* Calls enabled plugins for `chart` on the specified hook and with the given args.
|
|
* This method immediately returns as soon as a plugin explicitly returns false. The
|
|
* returned value can be used, for instance, to interrupt the current action.
|
|
* @param {Object} chart - The chart instance for which plugins should be called.
|
|
* @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
|
|
* @param {Array} [args] - Extra arguments to apply to the hook call.
|
|
* @returns {Boolean} false if any of the plugins return false, else returns true.
|
|
*/
|
|
notify: function(chart, hook, args) {
|
|
var descriptors = this.descriptors(chart);
|
|
var ilen = descriptors.length;
|
|
var i, descriptor, plugin, params, method;
|
|
|
|
for (i = 0; i < ilen; ++i) {
|
|
descriptor = descriptors[i];
|
|
plugin = descriptor.plugin;
|
|
method = plugin[hook];
|
|
if (typeof method === 'function') {
|
|
params = [chart].concat(args || []);
|
|
params.push(descriptor.options);
|
|
if (method.apply(plugin, params) === false) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Returns descriptors of enabled plugins for the given chart.
|
|
* @returns {Array} [{ plugin, options }]
|
|
* @private
|
|
*/
|
|
descriptors: function(chart) {
|
|
var cache = chart.$plugins || (chart.$plugins = {});
|
|
if (cache.id === this._cacheId) {
|
|
return cache.descriptors;
|
|
}
|
|
|
|
var plugins = [];
|
|
var descriptors = [];
|
|
var config = (chart && chart.config) || {};
|
|
var options = (config.options && config.options.plugins) || {};
|
|
|
|
this._plugins.concat(config.plugins || []).forEach(function(plugin) {
|
|
var idx = plugins.indexOf(plugin);
|
|
if (idx !== -1) {
|
|
return;
|
|
}
|
|
|
|
var id = plugin.id;
|
|
var opts = options[id];
|
|
if (opts === false) {
|
|
return;
|
|
}
|
|
|
|
if (opts === true) {
|
|
opts = helpers.clone(defaults.global.plugins[id]);
|
|
}
|
|
|
|
plugins.push(plugin);
|
|
descriptors.push({
|
|
plugin: plugin,
|
|
options: opts || {}
|
|
});
|
|
});
|
|
|
|
cache.descriptors = descriptors;
|
|
cache.id = this._cacheId;
|
|
return descriptors;
|
|
},
|
|
|
|
/**
|
|
* Invalidates cache for the given chart: descriptors hold a reference on plugin option,
|
|
* but in some cases, this reference can be changed by the user when updating options.
|
|
* https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
|
* @private
|
|
*/
|
|
_invalidate: function(chart) {
|
|
delete chart.$plugins;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Plugin extension hooks.
|
|
* @interface IPlugin
|
|
* @since 2.1.0
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeInit
|
|
* @desc Called before initializing `chart`.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterInit
|
|
* @desc Called after `chart` has been initialized and before the first update.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeUpdate
|
|
* @desc Called before updating `chart`. If any plugin returns `false`, the update
|
|
* is cancelled (and thus subsequent render(s)) until another `update` is triggered.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart update.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterUpdate
|
|
* @desc Called after `chart` has been updated and before rendering. Note that this
|
|
* hook will not be called if the chart update has been previously cancelled.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeDatasetsUpdate
|
|
* @desc Called before updating the `chart` datasets. If any plugin returns `false`,
|
|
* the datasets update is cancelled until another `update` is triggered.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} false to cancel the datasets update.
|
|
* @since version 2.1.5
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterDatasetsUpdate
|
|
* @desc Called after the `chart` datasets have been updated. Note that this hook
|
|
* will not be called if the datasets update has been previously cancelled.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
* @since version 2.1.5
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeDatasetUpdate
|
|
* @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
|
|
* returns `false`, the datasets update is cancelled until another `update` is triggered.
|
|
* @param {Chart} chart - The chart instance.
|
|
* @param {Object} args - The call arguments.
|
|
* @param {Number} args.index - The dataset index.
|
|
* @param {Object} args.meta - The dataset metadata.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart datasets drawing.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterDatasetUpdate
|
|
* @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
|
|
* that this hook will not be called if the datasets update has been previously cancelled.
|
|
* @param {Chart} chart - The chart instance.
|
|
* @param {Object} args - The call arguments.
|
|
* @param {Number} args.index - The dataset index.
|
|
* @param {Object} args.meta - The dataset metadata.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeLayout
|
|
* @desc Called before laying out `chart`. If any plugin returns `false`,
|
|
* the layout update is cancelled until another `update` is triggered.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart layout.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterLayout
|
|
* @desc Called after the `chart` has been layed out. Note that this hook will not
|
|
* be called if the layout update has been previously cancelled.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeRender
|
|
* @desc Called before rendering `chart`. If any plugin returns `false`,
|
|
* the rendering is cancelled until another `render` is triggered.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart rendering.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterRender
|
|
* @desc Called after the `chart` has been fully rendered (and animation completed). Note
|
|
* that this hook will not be called if the rendering has been previously cancelled.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeDraw
|
|
* @desc Called before drawing `chart` at every animation frame specified by the given
|
|
* easing value. If any plugin returns `false`, the frame drawing is cancelled until
|
|
* another `render` is triggered.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart drawing.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterDraw
|
|
* @desc Called after the `chart` has been drawn for the specific easing value. Note
|
|
* that this hook will not be called if the drawing has been previously cancelled.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeDatasetsDraw
|
|
* @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
|
|
* the datasets drawing is cancelled until another `render` is triggered.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart datasets drawing.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterDatasetsDraw
|
|
* @desc Called after the `chart` datasets have been drawn. Note that this hook
|
|
* will not be called if the datasets drawing has been previously cancelled.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeDatasetDraw
|
|
* @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
|
|
* are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
|
|
* is cancelled until another `render` is triggered.
|
|
* @param {Chart} chart - The chart instance.
|
|
* @param {Object} args - The call arguments.
|
|
* @param {Number} args.index - The dataset index.
|
|
* @param {Object} args.meta - The dataset metadata.
|
|
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart datasets drawing.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterDatasetDraw
|
|
* @desc Called after the `chart` datasets at the given `args.index` have been drawn
|
|
* (datasets are drawn in the reverse order). Note that this hook will not be called
|
|
* if the datasets drawing has been previously cancelled.
|
|
* @param {Chart} chart - The chart instance.
|
|
* @param {Object} args - The call arguments.
|
|
* @param {Number} args.index - The dataset index.
|
|
* @param {Object} args.meta - The dataset metadata.
|
|
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeTooltipDraw
|
|
* @desc Called before drawing the `tooltip`. If any plugin returns `false`,
|
|
* the tooltip drawing is cancelled until another `render` is triggered.
|
|
* @param {Chart} chart - The chart instance.
|
|
* @param {Object} args - The call arguments.
|
|
* @param {Object} args.tooltip - The tooltip.
|
|
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
* @returns {Boolean} `false` to cancel the chart tooltip drawing.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterTooltipDraw
|
|
* @desc Called after drawing the `tooltip`. Note that this hook will not
|
|
* be called if the tooltip drawing has been previously cancelled.
|
|
* @param {Chart} chart - The chart instance.
|
|
* @param {Object} args - The call arguments.
|
|
* @param {Object} args.tooltip - The tooltip.
|
|
* @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#beforeEvent
|
|
* @desc Called before processing the specified `event`. If any plugin returns `false`,
|
|
* the event will be discarded.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {IEvent} event - The event object.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#afterEvent
|
|
* @desc Called after the `event` has been consumed. Note that this hook
|
|
* will not be called if the `event` has been previously discarded.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {IEvent} event - The event object.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#resize
|
|
* @desc Called after the chart as been resized.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Number} size - The new canvas display size (eq. canvas.style width & height).
|
|
* @param {Object} options - The plugin options.
|
|
*/
|
|
/**
|
|
* @method IPlugin#destroy
|
|
* @desc Called after the chart as been destroyed.
|
|
* @param {Chart.Controller} chart - The chart instance.
|
|
* @param {Object} options - The plugin options.
|
|
*/
|