Explicitly deprecate (since 2.1.5) `Chart.Legend` and `Chart.Title`.
Chart.Interaction = require('./core/core.interaction');
Chart.layout = require('./core/core.layout');
Chart.platform = require('./platforms/platform');
+Chart.plugins = require('./core/core.plugins');
Chart.Ticks = require('./core/core.ticks');
-require('./core/core.plugin')(Chart);
require('./core/core.animation')(Chart);
require('./core/core.controller')(Chart);
require('./core/core.datasetController')(Chart);
require('./charts/Chart.Scatter')(Chart);
// Loading built-it plugins
-var plugins = [];
-
-plugins.push(
- require('./plugins/plugin.filler')(Chart),
- require('./plugins/plugin.legend')(Chart),
- require('./plugins/plugin.title')(Chart)
-);
-
-Chart.plugins.register(plugins);
+var plugins = require('./plugins');
+for (var k in plugins) {
+ if (plugins.hasOwnProperty(k)) {
+ Chart.plugins.register(plugins[k]);
+ }
+}
Chart.platform.initialize();
// DEPRECATIONS
+/**
+ * Provided for backward compatibility, not available anymore
+ * @namespace Chart.Legend
+ * @deprecated since version 2.1.5
+ * @todo remove at version 3
+ * @private
+ */
+Chart.Legend = plugins.legend._element;
+
+/**
+ * Provided for backward compatibility, not available anymore
+ * @namespace Chart.Title
+ * @deprecated since version 2.1.5
+ * @todo remove at version 3
+ * @private
+ */
+Chart.Title = plugins.title._element;
+
+/**
+ * Provided for backward compatibility, use Chart.plugins instead
+ * @namespace Chart.pluginService
+ * @deprecated since version 2.1.5
+ * @todo remove at version 3
+ * @private
+ */
+Chart.pluginService = Chart.plugins;
+
+/**
+ * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
+ * effect, instead simply create/register plugins via plain JavaScript objects.
+ * @interface Chart.PluginBase
+ * @deprecated since version 2.5.0
+ * @todo remove at version 3
+ * @private
+ */
+Chart.PluginBase = Chart.Element.extend({});
+
/**
* Provided for backward compatibility, use Chart.helpers.canvas instead.
* @namespace Chart.canvasHelpers
var Interaction = require('./core.interaction');
var layout = require('./core.layout');
var platform = require('../platforms/platform');
+var plugins = require('./core.plugins');
module.exports = function(Chart) {
- var plugins = Chart.plugins;
// Create a dictionary of chart types, to allow for extension of existing types
Chart.types = {};
+++ /dev/null
-'use strict';
-
-var defaults = require('./core.defaults');
-var Element = require('./core.element');
-var helpers = require('../helpers/index');
-
-defaults._set('global', {
- plugins: {}
-});
-
-module.exports = function(Chart) {
-
- /**
- * The plugin service singleton
- * @namespace Chart.plugins
- * @since 2.1.0
- */
- Chart.plugins = {
- /**
- * 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;
- }
- };
-
- /**
- * 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.
- */
-
- /**
- * Provided for backward compatibility, use Chart.plugins instead
- * @namespace Chart.pluginService
- * @deprecated since version 2.1.5
- * @todo remove at version 3
- * @private
- */
- Chart.pluginService = Chart.plugins;
-
- /**
- * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
- * effect, instead simply create/register plugins via plain JavaScript objects.
- * @interface Chart.PluginBase
- * @deprecated since version 2.5.0
- * @todo remove at version 3
- * @private
- */
- Chart.PluginBase = Element.extend({});
-};
--- /dev/null
+'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;
+ }
+};
+
+/**
+ * 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.
+ */
--- /dev/null
+'use strict';
+
+module.exports = {};
+module.exports.filler = require('./plugin.filler');
+module.exports.legend = require('./plugin.legend');
+module.exports.title = require('./plugin.title');
}
});
-module.exports = function() {
-
- var mappers = {
- dataset: function(source) {
- var index = source.fill;
- var chart = source.chart;
- var meta = chart.getDatasetMeta(index);
- var visible = meta && chart.isDatasetVisible(index);
- var points = (visible && meta.dataset._children) || [];
- var length = points.length || 0;
-
- return !length ? null : function(point, i) {
- return (i < length && points[i]._view) || null;
+var mappers = {
+ dataset: function(source) {
+ var index = source.fill;
+ var chart = source.chart;
+ var meta = chart.getDatasetMeta(index);
+ var visible = meta && chart.isDatasetVisible(index);
+ var points = (visible && meta.dataset._children) || [];
+ var length = points.length || 0;
+
+ return !length ? null : function(point, i) {
+ return (i < length && points[i]._view) || null;
+ };
+ },
+
+ boundary: function(source) {
+ var boundary = source.boundary;
+ var x = boundary ? boundary.x : null;
+ var y = boundary ? boundary.y : null;
+
+ return function(point) {
+ return {
+ x: x === null ? point.x : x,
+ y: y === null ? point.y : y,
};
- },
+ };
+ }
+};
- boundary: function(source) {
- var boundary = source.boundary;
- var x = boundary ? boundary.x : null;
- var y = boundary ? boundary.y : null;
+// @todo if (fill[0] === '#')
+function decodeFill(el, index, count) {
+ var model = el._model || {};
+ var fill = model.fill;
+ var target;
- return function(point) {
- return {
- x: x === null ? point.x : x,
- y: y === null ? point.y : y,
- };
- };
- }
- };
+ if (fill === undefined) {
+ fill = !!model.backgroundColor;
+ }
+
+ if (fill === false || fill === null) {
+ return false;
+ }
- // @todo if (fill[0] === '#')
- function decodeFill(el, index, count) {
- var model = el._model || {};
- var fill = model.fill;
- var target;
+ if (fill === true) {
+ return 'origin';
+ }
- if (fill === undefined) {
- fill = !!model.backgroundColor;
+ target = parseFloat(fill, 10);
+ if (isFinite(target) && Math.floor(target) === target) {
+ if (fill[0] === '-' || fill[0] === '+') {
+ target = index + target;
}
- if (fill === false || fill === null) {
+ if (target === index || target < 0 || target >= count) {
return false;
}
- if (fill === true) {
- return 'origin';
- }
+ return target;
+ }
- target = parseFloat(fill, 10);
- if (isFinite(target) && Math.floor(target) === target) {
- if (fill[0] === '-' || fill[0] === '+') {
- target = index + target;
- }
+ switch (fill) {
+ // compatibility
+ case 'bottom':
+ return 'start';
+ case 'top':
+ return 'end';
+ case 'zero':
+ return 'origin';
+ // supported boundaries
+ case 'origin':
+ case 'start':
+ case 'end':
+ return fill;
+ // invalid fill values
+ default:
+ return false;
+ }
+}
- if (target === index || target < 0 || target >= count) {
- return false;
- }
+function computeBoundary(source) {
+ var model = source.el._model || {};
+ var scale = source.el._scale || {};
+ var fill = source.fill;
+ var target = null;
+ var horizontal;
- return target;
- }
-
- switch (fill) {
- // compatibility
- case 'bottom':
- return 'start';
- case 'top':
- return 'end';
- case 'zero':
- return 'origin';
- // supported boundaries
- case 'origin':
- case 'start':
- case 'end':
- return fill;
- // invalid fill values
- default:
- return false;
- }
+ if (isFinite(fill)) {
+ return null;
}
- function computeBoundary(source) {
- var model = source.el._model || {};
- var scale = source.el._scale || {};
- var fill = source.fill;
- var target = null;
- var horizontal;
+ // Backward compatibility: until v3, we still need to support boundary values set on
+ // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
+ // controllers might still use it (e.g. the Smith chart).
+
+ if (fill === 'start') {
+ target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
+ } else if (fill === 'end') {
+ target = model.scaleTop === undefined ? scale.top : model.scaleTop;
+ } else if (model.scaleZero !== undefined) {
+ target = model.scaleZero;
+ } else if (scale.getBasePosition) {
+ target = scale.getBasePosition();
+ } else if (scale.getBasePixel) {
+ target = scale.getBasePixel();
+ }
- if (isFinite(fill)) {
- return null;
+ if (target !== undefined && target !== null) {
+ if (target.x !== undefined && target.y !== undefined) {
+ return target;
}
- // Backward compatibility: until v3, we still need to support boundary values set on
- // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
- // controllers might still use it (e.g. the Smith chart).
-
- if (fill === 'start') {
- target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
- } else if (fill === 'end') {
- target = model.scaleTop === undefined ? scale.top : model.scaleTop;
- } else if (model.scaleZero !== undefined) {
- target = model.scaleZero;
- } else if (scale.getBasePosition) {
- target = scale.getBasePosition();
- } else if (scale.getBasePixel) {
- target = scale.getBasePixel();
+ if (typeof target === 'number' && isFinite(target)) {
+ horizontal = scale.isHorizontal();
+ return {
+ x: horizontal ? target : null,
+ y: horizontal ? null : target
+ };
}
+ }
- if (target !== undefined && target !== null) {
- if (target.x !== undefined && target.y !== undefined) {
- return target;
- }
+ return null;
+}
- if (typeof target === 'number' && isFinite(target)) {
- horizontal = scale.isHorizontal();
- return {
- x: horizontal ? target : null,
- y: horizontal ? null : target
- };
- }
- }
+function resolveTarget(sources, index, propagate) {
+ var source = sources[index];
+ var fill = source.fill;
+ var visited = [index];
+ var target;
- return null;
+ if (!propagate) {
+ return fill;
}
- function resolveTarget(sources, index, propagate) {
- var source = sources[index];
- var fill = source.fill;
- var visited = [index];
- var target;
-
- if (!propagate) {
+ while (fill !== false && visited.indexOf(fill) === -1) {
+ if (!isFinite(fill)) {
return fill;
}
- while (fill !== false && visited.indexOf(fill) === -1) {
- if (!isFinite(fill)) {
- return fill;
- }
-
- target = sources[fill];
- if (!target) {
- return false;
- }
-
- if (target.visible) {
- return fill;
- }
+ target = sources[fill];
+ if (!target) {
+ return false;
+ }
- visited.push(fill);
- fill = target.fill;
+ if (target.visible) {
+ return fill;
}
- return false;
+ visited.push(fill);
+ fill = target.fill;
}
- function createMapper(source) {
- var fill = source.fill;
- var type = 'dataset';
-
- if (fill === false) {
- return null;
- }
+ return false;
+}
- if (!isFinite(fill)) {
- type = 'boundary';
- }
+function createMapper(source) {
+ var fill = source.fill;
+ var type = 'dataset';
- return mappers[type](source);
+ if (fill === false) {
+ return null;
}
- function isDrawable(point) {
- return point && !point.skip;
+ if (!isFinite(fill)) {
+ type = 'boundary';
}
- function drawArea(ctx, curve0, curve1, len0, len1) {
- var i;
+ return mappers[type](source);
+}
- if (!len0 || !len1) {
- return;
- }
+function isDrawable(point) {
+ return point && !point.skip;
+}
- // building first area curve (normal)
- ctx.moveTo(curve0[0].x, curve0[0].y);
- for (i = 1; i < len0; ++i) {
- helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
- }
+function drawArea(ctx, curve0, curve1, len0, len1) {
+ var i;
- // joining the two area curves
- ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
+ if (!len0 || !len1) {
+ return;
+ }
- // building opposite area curve (reverse)
- for (i = len1 - 1; i > 0; --i) {
- helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
- }
+ // building first area curve (normal)
+ ctx.moveTo(curve0[0].x, curve0[0].y);
+ for (i = 1; i < len0; ++i) {
+ helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
}
- function doFill(ctx, points, mapper, view, color, loop) {
- var count = points.length;
- var span = view.spanGaps;
- var curve0 = [];
- var curve1 = [];
- var len0 = 0;
- var len1 = 0;
- var i, ilen, index, p0, p1, d0, d1;
-
- ctx.beginPath();
-
- for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
- index = i % count;
- p0 = points[index]._view;
- p1 = mapper(p0, index, view);
- d0 = isDrawable(p0);
- d1 = isDrawable(p1);
-
- if (d0 && d1) {
- len0 = curve0.push(p0);
- len1 = curve1.push(p1);
- } else if (len0 && len1) {
- if (!span) {
- drawArea(ctx, curve0, curve1, len0, len1);
- len0 = len1 = 0;
- curve0 = [];
- curve1 = [];
- } else {
- if (d0) {
- curve0.push(p0);
- }
- if (d1) {
- curve1.push(p1);
- }
+ // joining the two area curves
+ ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
+
+ // building opposite area curve (reverse)
+ for (i = len1 - 1; i > 0; --i) {
+ helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
+ }
+}
+
+function doFill(ctx, points, mapper, view, color, loop) {
+ var count = points.length;
+ var span = view.spanGaps;
+ var curve0 = [];
+ var curve1 = [];
+ var len0 = 0;
+ var len1 = 0;
+ var i, ilen, index, p0, p1, d0, d1;
+
+ ctx.beginPath();
+
+ for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
+ index = i % count;
+ p0 = points[index]._view;
+ p1 = mapper(p0, index, view);
+ d0 = isDrawable(p0);
+ d1 = isDrawable(p1);
+
+ if (d0 && d1) {
+ len0 = curve0.push(p0);
+ len1 = curve1.push(p1);
+ } else if (len0 && len1) {
+ if (!span) {
+ drawArea(ctx, curve0, curve1, len0, len1);
+ len0 = len1 = 0;
+ curve0 = [];
+ curve1 = [];
+ } else {
+ if (d0) {
+ curve0.push(p0);
+ }
+ if (d1) {
+ curve1.push(p1);
}
}
}
-
- drawArea(ctx, curve0, curve1, len0, len1);
-
- ctx.closePath();
- ctx.fillStyle = color;
- ctx.fill();
}
- return {
- id: 'filler',
-
- afterDatasetsUpdate: function(chart, options) {
- var count = (chart.data.datasets || []).length;
- var propagate = options.propagate;
- var sources = [];
- var meta, i, el, source;
-
- for (i = 0; i < count; ++i) {
- meta = chart.getDatasetMeta(i);
- el = meta.dataset;
- source = null;
-
- if (el && el._model && el instanceof elements.Line) {
- source = {
- visible: chart.isDatasetVisible(i),
- fill: decodeFill(el, i, count),
- chart: chart,
- el: el
- };
- }
-
- meta.$filler = source;
- sources.push(source);
+ drawArea(ctx, curve0, curve1, len0, len1);
+
+ ctx.closePath();
+ ctx.fillStyle = color;
+ ctx.fill();
+}
+
+module.exports = {
+ id: 'filler',
+
+ afterDatasetsUpdate: function(chart, options) {
+ var count = (chart.data.datasets || []).length;
+ var propagate = options.propagate;
+ var sources = [];
+ var meta, i, el, source;
+
+ for (i = 0; i < count; ++i) {
+ meta = chart.getDatasetMeta(i);
+ el = meta.dataset;
+ source = null;
+
+ if (el && el._model && el instanceof elements.Line) {
+ source = {
+ visible: chart.isDatasetVisible(i),
+ fill: decodeFill(el, i, count),
+ chart: chart,
+ el: el
+ };
}
- for (i = 0; i < count; ++i) {
- source = sources[i];
- if (!source) {
- continue;
- }
+ meta.$filler = source;
+ sources.push(source);
+ }
- source.fill = resolveTarget(sources, i, propagate);
- source.boundary = computeBoundary(source);
- source.mapper = createMapper(source);
+ for (i = 0; i < count; ++i) {
+ source = sources[i];
+ if (!source) {
+ continue;
}
- },
- beforeDatasetDraw: function(chart, args) {
- var meta = args.meta.$filler;
- if (!meta) {
- return;
- }
+ source.fill = resolveTarget(sources, i, propagate);
+ source.boundary = computeBoundary(source);
+ source.mapper = createMapper(source);
+ }
+ },
- var ctx = chart.ctx;
- var el = meta.el;
- var view = el._view;
- var points = el._children || [];
- var mapper = meta.mapper;
- var color = view.backgroundColor || defaults.global.defaultColor;
-
- if (mapper && color && points.length) {
- helpers.canvas.clipArea(ctx, chart.chartArea);
- doFill(ctx, points, mapper, view, color, el._loop);
- helpers.canvas.unclipArea(ctx);
- }
+ beforeDatasetDraw: function(chart, args) {
+ var meta = args.meta.$filler;
+ if (!meta) {
+ return;
+ }
+
+ var ctx = chart.ctx;
+ var el = meta.el;
+ var view = el._view;
+ var points = el._children || [];
+ var mapper = meta.mapper;
+ var color = view.backgroundColor || defaults.global.defaultColor;
+
+ if (mapper && color && points.length) {
+ helpers.canvas.clipArea(ctx, chart.chartArea);
+ doFill(ctx, points, mapper, view, color, el._loop);
+ helpers.canvas.unclipArea(ctx);
}
- };
+ }
};
var helpers = require('../helpers/index');
var layout = require('../core/core.layout');
+var noop = helpers.noop;
+
defaults._set('global', {
legend: {
display: true,
}
});
-module.exports = function(Chart) {
-
- var noop = helpers.noop;
-
- /**
- * Helper function to get the box width based on the usePointStyle option
- * @param labelopts {Object} the label options on the legend
- * @param fontSize {Number} the label font size
- * @return {Number} width of the color box area
- */
- function getBoxWidth(labelOpts, fontSize) {
- return labelOpts.usePointStyle ?
- fontSize * Math.SQRT2 :
- labelOpts.boxWidth;
- }
-
- Chart.Legend = Element.extend({
-
- initialize: function(config) {
- helpers.extend(this, config);
-
- // Contains hit boxes for each dataset (in dataset order)
- this.legendHitBoxes = [];
-
- // Are we in doughnut mode which has a different data type
- this.doughnutMode = false;
- },
-
- // These methods are ordered by lifecycle. Utilities then follow.
- // Any function defined here is inherited by all legend types.
- // Any function can be extended by the legend type
-
- beforeUpdate: noop,
- update: function(maxWidth, maxHeight, margins) {
- var me = this;
-
- // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
- me.beforeUpdate();
-
- // Absorb the master measurements
- me.maxWidth = maxWidth;
- me.maxHeight = maxHeight;
- me.margins = margins;
-
- // Dimensions
- me.beforeSetDimensions();
- me.setDimensions();
- me.afterSetDimensions();
- // Labels
- me.beforeBuildLabels();
- me.buildLabels();
- me.afterBuildLabels();
-
- // Fit
- me.beforeFit();
- me.fit();
- me.afterFit();
- //
- me.afterUpdate();
-
- return me.minSize;
- },
- afterUpdate: noop,
+/**
+ * Helper function to get the box width based on the usePointStyle option
+ * @param labelopts {Object} the label options on the legend
+ * @param fontSize {Number} the label font size
+ * @return {Number} width of the color box area
+ */
+function getBoxWidth(labelOpts, fontSize) {
+ return labelOpts.usePointStyle ?
+ fontSize * Math.SQRT2 :
+ labelOpts.boxWidth;
+}
+
+/**
+ * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
+ */
+var Legend = Element.extend({
+
+ initialize: function(config) {
+ helpers.extend(this, config);
+
+ // Contains hit boxes for each dataset (in dataset order)
+ this.legendHitBoxes = [];
+
+ // Are we in doughnut mode which has a different data type
+ this.doughnutMode = false;
+ },
+ // These methods are ordered by lifecycle. Utilities then follow.
+ // Any function defined here is inherited by all legend types.
+ // Any function can be extended by the legend type
+
+ beforeUpdate: noop,
+ update: function(maxWidth, maxHeight, margins) {
+ var me = this;
+
+ // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
+ me.beforeUpdate();
+
+ // Absorb the master measurements
+ me.maxWidth = maxWidth;
+ me.maxHeight = maxHeight;
+ me.margins = margins;
+
+ // Dimensions
+ me.beforeSetDimensions();
+ me.setDimensions();
+ me.afterSetDimensions();
+ // Labels
+ me.beforeBuildLabels();
+ me.buildLabels();
+ me.afterBuildLabels();
+
+ // Fit
+ me.beforeFit();
+ me.fit();
+ me.afterFit();
//
+ me.afterUpdate();
- beforeSetDimensions: noop,
- setDimensions: function() {
- var me = this;
- // Set the unconstrained dimension before label rotation
- if (me.isHorizontal()) {
- // Reset position before calculating rotation
- me.width = me.maxWidth;
- me.left = 0;
- me.right = me.width;
- } else {
- me.height = me.maxHeight;
+ return me.minSize;
+ },
+ afterUpdate: noop,
+
+ //
+
+ beforeSetDimensions: noop,
+ setDimensions: function() {
+ var me = this;
+ // Set the unconstrained dimension before label rotation
+ if (me.isHorizontal()) {
+ // Reset position before calculating rotation
+ me.width = me.maxWidth;
+ me.left = 0;
+ me.right = me.width;
+ } else {
+ me.height = me.maxHeight;
+
+ // Reset position before calculating rotation
+ me.top = 0;
+ me.bottom = me.height;
+ }
- // Reset position before calculating rotation
- me.top = 0;
- me.bottom = me.height;
- }
+ // Reset padding
+ me.paddingLeft = 0;
+ me.paddingTop = 0;
+ me.paddingRight = 0;
+ me.paddingBottom = 0;
+
+ // Reset minSize
+ me.minSize = {
+ width: 0,
+ height: 0
+ };
+ },
+ afterSetDimensions: noop,
- // Reset padding
- me.paddingLeft = 0;
- me.paddingTop = 0;
- me.paddingRight = 0;
- me.paddingBottom = 0;
+ //
- // Reset minSize
- me.minSize = {
- width: 0,
- height: 0
- };
- },
- afterSetDimensions: noop,
+ beforeBuildLabels: noop,
+ buildLabels: function() {
+ var me = this;
+ var labelOpts = me.options.labels || {};
+ var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
- //
+ if (labelOpts.filter) {
+ legendItems = legendItems.filter(function(item) {
+ return labelOpts.filter(item, me.chart.data);
+ });
+ }
- beforeBuildLabels: noop,
- buildLabels: function() {
- var me = this;
- var labelOpts = me.options.labels || {};
- var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
+ if (me.options.reverse) {
+ legendItems.reverse();
+ }
- if (labelOpts.filter) {
- legendItems = legendItems.filter(function(item) {
- return labelOpts.filter(item, me.chart.data);
- });
- }
+ me.legendItems = legendItems;
+ },
+ afterBuildLabels: noop,
+
+ //
+
+ beforeFit: noop,
+ fit: function() {
+ var me = this;
+ var opts = me.options;
+ var labelOpts = opts.labels;
+ var display = opts.display;
+
+ var ctx = me.ctx;
+
+ var globalDefault = defaults.global;
+ var valueOrDefault = helpers.valueOrDefault;
+ var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
+ var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
+ var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
+ var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
+
+ // Reset hit boxes
+ var hitboxes = me.legendHitBoxes = [];
+
+ var minSize = me.minSize;
+ var isHorizontal = me.isHorizontal();
+
+ if (isHorizontal) {
+ minSize.width = me.maxWidth; // fill all the width
+ minSize.height = display ? 10 : 0;
+ } else {
+ minSize.width = display ? 10 : 0;
+ minSize.height = me.maxHeight; // fill all the height
+ }
- if (me.options.reverse) {
- legendItems.reverse();
- }
+ // Increase sizes here
+ if (display) {
+ ctx.font = labelFont;
- me.legendItems = legendItems;
- },
- afterBuildLabels: noop,
+ if (isHorizontal) {
+ // Labels
- //
+ // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
+ var lineWidths = me.lineWidths = [0];
+ var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
- beforeFit: noop,
- fit: function() {
- var me = this;
- var opts = me.options;
- var labelOpts = opts.labels;
- var display = opts.display;
+ ctx.textAlign = 'left';
+ ctx.textBaseline = 'top';
- var ctx = me.ctx;
+ helpers.each(me.legendItems, function(legendItem, i) {
+ var boxWidth = getBoxWidth(labelOpts, fontSize);
+ var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
- var globalDefault = defaults.global;
- var valueOrDefault = helpers.valueOrDefault;
- var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
- var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
- var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
- var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
+ if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
+ totalHeight += fontSize + (labelOpts.padding);
+ lineWidths[lineWidths.length] = me.left;
+ }
- // Reset hit boxes
- var hitboxes = me.legendHitBoxes = [];
+ // Store the hitbox width and height here. Final position will be updated in `draw`
+ hitboxes[i] = {
+ left: 0,
+ top: 0,
+ width: width,
+ height: fontSize
+ };
- var minSize = me.minSize;
- var isHorizontal = me.isHorizontal();
+ lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
+ });
- if (isHorizontal) {
- minSize.width = me.maxWidth; // fill all the width
- minSize.height = display ? 10 : 0;
- } else {
- minSize.width = display ? 10 : 0;
- minSize.height = me.maxHeight; // fill all the height
- }
+ minSize.height += totalHeight;
- // Increase sizes here
- if (display) {
- ctx.font = labelFont;
+ } else {
+ var vPadding = labelOpts.padding;
+ var columnWidths = me.columnWidths = [];
+ var totalWidth = labelOpts.padding;
+ var currentColWidth = 0;
+ var currentColHeight = 0;
+ var itemHeight = fontSize + vPadding;
- if (isHorizontal) {
- // Labels
+ helpers.each(me.legendItems, function(legendItem, i) {
+ var boxWidth = getBoxWidth(labelOpts, fontSize);
+ var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
- // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
- var lineWidths = me.lineWidths = [0];
- var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
+ // If too tall, go to new column
+ if (currentColHeight + itemHeight > minSize.height) {
+ totalWidth += currentColWidth + labelOpts.padding;
+ columnWidths.push(currentColWidth); // previous column width
- ctx.textAlign = 'left';
- ctx.textBaseline = 'top';
+ currentColWidth = 0;
+ currentColHeight = 0;
+ }
- helpers.each(me.legendItems, function(legendItem, i) {
- var boxWidth = getBoxWidth(labelOpts, fontSize);
- var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
+ // Get max width
+ currentColWidth = Math.max(currentColWidth, itemWidth);
+ currentColHeight += itemHeight;
- if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
- totalHeight += fontSize + (labelOpts.padding);
- lineWidths[lineWidths.length] = me.left;
- }
+ // Store the hitbox width and height here. Final position will be updated in `draw`
+ hitboxes[i] = {
+ left: 0,
+ top: 0,
+ width: itemWidth,
+ height: fontSize
+ };
+ });
- // Store the hitbox width and height here. Final position will be updated in `draw`
- hitboxes[i] = {
- left: 0,
- top: 0,
- width: width,
- height: fontSize
- };
+ totalWidth += currentColWidth;
+ columnWidths.push(currentColWidth);
+ minSize.width += totalWidth;
+ }
+ }
- lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
- });
+ me.width = minSize.width;
+ me.height = minSize.height;
+ },
+ afterFit: noop,
- minSize.height += totalHeight;
+ // Shared Methods
+ isHorizontal: function() {
+ return this.options.position === 'top' || this.options.position === 'bottom';
+ },
- } else {
- var vPadding = labelOpts.padding;
- var columnWidths = me.columnWidths = [];
- var totalWidth = labelOpts.padding;
- var currentColWidth = 0;
- var currentColHeight = 0;
- var itemHeight = fontSize + vPadding;
-
- helpers.each(me.legendItems, function(legendItem, i) {
- var boxWidth = getBoxWidth(labelOpts, fontSize);
- var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
-
- // If too tall, go to new column
- if (currentColHeight + itemHeight > minSize.height) {
- totalWidth += currentColWidth + labelOpts.padding;
- columnWidths.push(currentColWidth); // previous column width
-
- currentColWidth = 0;
- currentColHeight = 0;
- }
-
- // Get max width
- currentColWidth = Math.max(currentColWidth, itemWidth);
- currentColHeight += itemHeight;
-
- // Store the hitbox width and height here. Final position will be updated in `draw`
- hitboxes[i] = {
- left: 0,
- top: 0,
- width: itemWidth,
- height: fontSize
- };
- });
-
- totalWidth += currentColWidth;
- columnWidths.push(currentColWidth);
- minSize.width += totalWidth;
+ // Actually draw the legend on the canvas
+ draw: function() {
+ var me = this;
+ var opts = me.options;
+ var labelOpts = opts.labels;
+ var globalDefault = defaults.global;
+ var lineDefault = globalDefault.elements.line;
+ var legendWidth = me.width;
+ var lineWidths = me.lineWidths;
+
+ if (opts.display) {
+ var ctx = me.ctx;
+ var valueOrDefault = helpers.valueOrDefault;
+ var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
+ var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
+ var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
+ var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
+ var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
+ var cursor;
+
+ // Canvas setup
+ ctx.textAlign = 'left';
+ ctx.textBaseline = 'middle';
+ ctx.lineWidth = 0.5;
+ ctx.strokeStyle = fontColor; // for strikethrough effect
+ ctx.fillStyle = fontColor; // render in correct colour
+ ctx.font = labelFont;
+
+ var boxWidth = getBoxWidth(labelOpts, fontSize);
+ var hitboxes = me.legendHitBoxes;
+
+ // current position
+ var drawLegendBox = function(x, y, legendItem) {
+ if (isNaN(boxWidth) || boxWidth <= 0) {
+ return;
}
- }
-
- me.width = minSize.width;
- me.height = minSize.height;
- },
- afterFit: noop,
- // Shared Methods
- isHorizontal: function() {
- return this.options.position === 'top' || this.options.position === 'bottom';
- },
+ // Set the ctx for the box
+ ctx.save();
- // Actually draw the legend on the canvas
- draw: function() {
- var me = this;
- var opts = me.options;
- var labelOpts = opts.labels;
- var globalDefault = defaults.global;
- var lineDefault = globalDefault.elements.line;
- var legendWidth = me.width;
- var lineWidths = me.lineWidths;
-
- if (opts.display) {
- var ctx = me.ctx;
- var valueOrDefault = helpers.valueOrDefault;
- var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
- var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
- var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
- var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
- var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
- var cursor;
-
- // Canvas setup
- ctx.textAlign = 'left';
- ctx.textBaseline = 'middle';
- ctx.lineWidth = 0.5;
- ctx.strokeStyle = fontColor; // for strikethrough effect
- ctx.fillStyle = fontColor; // render in correct colour
- ctx.font = labelFont;
-
- var boxWidth = getBoxWidth(labelOpts, fontSize);
- var hitboxes = me.legendHitBoxes;
-
- // current position
- var drawLegendBox = function(x, y, legendItem) {
- if (isNaN(boxWidth) || boxWidth <= 0) {
- return;
- }
+ ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
+ ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
+ ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
+ ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
+ ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
+ ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
+ var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
- // Set the ctx for the box
- ctx.save();
+ if (ctx.setLineDash) {
+ // IE 9 and 10 do not support line dash
+ ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
+ }
- ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
- ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
- ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
- ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
- ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
- ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
- var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
+ if (opts.labels && opts.labels.usePointStyle) {
+ // Recalculate x and y for drawPoint() because its expecting
+ // x and y to be center of figure (instead of top left)
+ var radius = fontSize * Math.SQRT2 / 2;
+ var offSet = radius / Math.SQRT2;
+ var centerX = x + offSet;
+ var centerY = y + offSet;
- if (ctx.setLineDash) {
- // IE 9 and 10 do not support line dash
- ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
+ // Draw pointStyle as legend symbol
+ helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
+ } else {
+ // Draw box as legend symbol
+ if (!isLineWidthZero) {
+ ctx.strokeRect(x, y, boxWidth, fontSize);
}
+ ctx.fillRect(x, y, boxWidth, fontSize);
+ }
- if (opts.labels && opts.labels.usePointStyle) {
- // Recalculate x and y for drawPoint() because its expecting
- // x and y to be center of figure (instead of top left)
- var radius = fontSize * Math.SQRT2 / 2;
- var offSet = radius / Math.SQRT2;
- var centerX = x + offSet;
- var centerY = y + offSet;
-
- // Draw pointStyle as legend symbol
- helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
- } else {
- // Draw box as legend symbol
- if (!isLineWidthZero) {
- ctx.strokeRect(x, y, boxWidth, fontSize);
- }
- ctx.fillRect(x, y, boxWidth, fontSize);
- }
+ ctx.restore();
+ };
+ var fillText = function(x, y, legendItem, textWidth) {
+ var halfFontSize = fontSize / 2;
+ var xLeft = boxWidth + halfFontSize + x;
+ var yMiddle = y + halfFontSize;
+
+ ctx.fillText(legendItem.text, xLeft, yMiddle);
+
+ if (legendItem.hidden) {
+ // Strikethrough the text if hidden
+ ctx.beginPath();
+ ctx.lineWidth = 2;
+ ctx.moveTo(xLeft, yMiddle);
+ ctx.lineTo(xLeft + textWidth, yMiddle);
+ ctx.stroke();
+ }
+ };
- ctx.restore();
+ // Horizontal
+ var isHorizontal = me.isHorizontal();
+ if (isHorizontal) {
+ cursor = {
+ x: me.left + ((legendWidth - lineWidths[0]) / 2),
+ y: me.top + labelOpts.padding,
+ line: 0
};
- var fillText = function(x, y, legendItem, textWidth) {
- var halfFontSize = fontSize / 2;
- var xLeft = boxWidth + halfFontSize + x;
- var yMiddle = y + halfFontSize;
-
- ctx.fillText(legendItem.text, xLeft, yMiddle);
-
- if (legendItem.hidden) {
- // Strikethrough the text if hidden
- ctx.beginPath();
- ctx.lineWidth = 2;
- ctx.moveTo(xLeft, yMiddle);
- ctx.lineTo(xLeft + textWidth, yMiddle);
- ctx.stroke();
- }
+ } else {
+ cursor = {
+ x: me.left + labelOpts.padding,
+ y: me.top + labelOpts.padding,
+ line: 0
};
+ }
- // Horizontal
- var isHorizontal = me.isHorizontal();
- if (isHorizontal) {
- cursor = {
- x: me.left + ((legendWidth - lineWidths[0]) / 2),
- y: me.top + labelOpts.padding,
- line: 0
- };
- } else {
- cursor = {
- x: me.left + labelOpts.padding,
- y: me.top + labelOpts.padding,
- line: 0
- };
- }
+ var itemHeight = fontSize + labelOpts.padding;
+ helpers.each(me.legendItems, function(legendItem, i) {
+ var textWidth = ctx.measureText(legendItem.text).width;
+ var width = boxWidth + (fontSize / 2) + textWidth;
+ var x = cursor.x;
+ var y = cursor.y;
- var itemHeight = fontSize + labelOpts.padding;
- helpers.each(me.legendItems, function(legendItem, i) {
- var textWidth = ctx.measureText(legendItem.text).width;
- var width = boxWidth + (fontSize / 2) + textWidth;
- var x = cursor.x;
- var y = cursor.y;
-
- if (isHorizontal) {
- if (x + width >= legendWidth) {
- y = cursor.y += itemHeight;
- cursor.line++;
- x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
- }
- } else if (y + itemHeight > me.bottom) {
- x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
- y = cursor.y = me.top + labelOpts.padding;
+ if (isHorizontal) {
+ if (x + width >= legendWidth) {
+ y = cursor.y += itemHeight;
cursor.line++;
+ x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
}
+ } else if (y + itemHeight > me.bottom) {
+ x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
+ y = cursor.y = me.top + labelOpts.padding;
+ cursor.line++;
+ }
- drawLegendBox(x, y, legendItem);
+ drawLegendBox(x, y, legendItem);
- hitboxes[i].left = x;
- hitboxes[i].top = y;
+ hitboxes[i].left = x;
+ hitboxes[i].top = y;
- // Fill the actual label
- fillText(x, y, legendItem, textWidth);
+ // Fill the actual label
+ fillText(x, y, legendItem, textWidth);
- if (isHorizontal) {
- cursor.x += width + (labelOpts.padding);
- } else {
- cursor.y += itemHeight;
- }
+ if (isHorizontal) {
+ cursor.x += width + (labelOpts.padding);
+ } else {
+ cursor.y += itemHeight;
+ }
- });
- }
- },
+ });
+ }
+ },
- /**
- * Handle an event
- * @private
- * @param {IEvent} event - The event to handle
- * @return {Boolean} true if a change occured
- */
- handleEvent: function(e) {
- var me = this;
- var opts = me.options;
- var type = e.type === 'mouseup' ? 'click' : e.type;
- var changed = false;
-
- if (type === 'mousemove') {
- if (!opts.onHover) {
- return;
- }
- } else if (type === 'click') {
- if (!opts.onClick) {
- return;
- }
- } else {
+ /**
+ * Handle an event
+ * @private
+ * @param {IEvent} event - The event to handle
+ * @return {Boolean} true if a change occured
+ */
+ handleEvent: function(e) {
+ var me = this;
+ var opts = me.options;
+ var type = e.type === 'mouseup' ? 'click' : e.type;
+ var changed = false;
+
+ if (type === 'mousemove') {
+ if (!opts.onHover) {
+ return;
+ }
+ } else if (type === 'click') {
+ if (!opts.onClick) {
return;
}
+ } else {
+ return;
+ }
- // Chart event already has relative position in it
- var x = e.x;
- var y = e.y;
-
- if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
- // See if we are touching one of the dataset boxes
- var lh = me.legendHitBoxes;
- for (var i = 0; i < lh.length; ++i) {
- var hitBox = lh[i];
-
- if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
- // Touching an element
- if (type === 'click') {
- // use e.native for backwards compatibility
- opts.onClick.call(me, e.native, me.legendItems[i]);
- changed = true;
- break;
- } else if (type === 'mousemove') {
- // use e.native for backwards compatibility
- opts.onHover.call(me, e.native, me.legendItems[i]);
- changed = true;
- break;
- }
+ // Chart event already has relative position in it
+ var x = e.x;
+ var y = e.y;
+
+ if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
+ // See if we are touching one of the dataset boxes
+ var lh = me.legendHitBoxes;
+ for (var i = 0; i < lh.length; ++i) {
+ var hitBox = lh[i];
+
+ if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
+ // Touching an element
+ if (type === 'click') {
+ // use e.native for backwards compatibility
+ opts.onClick.call(me, e.native, me.legendItems[i]);
+ changed = true;
+ break;
+ } else if (type === 'mousemove') {
+ // use e.native for backwards compatibility
+ opts.onHover.call(me, e.native, me.legendItems[i]);
+ changed = true;
+ break;
}
}
}
-
- return changed;
}
- });
- function createNewLegendAndAttach(chart, legendOpts) {
- var legend = new Chart.Legend({
- ctx: chart.ctx,
- options: legendOpts,
- chart: chart
- });
-
- layout.configure(chart, legend, legendOpts);
- layout.addBox(chart, legend);
- chart.legend = legend;
+ return changed;
}
+});
- return {
- id: 'legend',
+function createNewLegendAndAttach(chart, legendOpts) {
+ var legend = new Legend({
+ ctx: chart.ctx,
+ options: legendOpts,
+ chart: chart
+ });
- beforeInit: function(chart) {
- var legendOpts = chart.options.legend;
+ layout.configure(chart, legend, legendOpts);
+ layout.addBox(chart, legend);
+ chart.legend = legend;
+}
- if (legendOpts) {
- createNewLegendAndAttach(chart, legendOpts);
- }
- },
+module.exports = {
+ id: 'legend',
- beforeUpdate: function(chart) {
- var legendOpts = chart.options.legend;
- var legend = chart.legend;
+ /**
+ * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
+ * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
+ * the plugin, which one will be re-exposed in the chart.js file.
+ * https://github.com/chartjs/Chart.js/pull/2640
+ * @private
+ */
+ _element: Legend,
- if (legendOpts) {
- helpers.mergeIf(legendOpts, defaults.global.legend);
+ beforeInit: function(chart) {
+ var legendOpts = chart.options.legend;
- if (legend) {
- layout.configure(chart, legend, legendOpts);
- legend.options = legendOpts;
- } else {
- createNewLegendAndAttach(chart, legendOpts);
- }
- } else if (legend) {
- layout.removeBox(chart, legend);
- delete chart.legend;
- }
- },
+ if (legendOpts) {
+ createNewLegendAndAttach(chart, legendOpts);
+ }
+ },
+
+ beforeUpdate: function(chart) {
+ var legendOpts = chart.options.legend;
+ var legend = chart.legend;
+
+ if (legendOpts) {
+ helpers.mergeIf(legendOpts, defaults.global.legend);
- afterEvent: function(chart, e) {
- var legend = chart.legend;
if (legend) {
- legend.handleEvent(e);
+ layout.configure(chart, legend, legendOpts);
+ legend.options = legendOpts;
+ } else {
+ createNewLegendAndAttach(chart, legendOpts);
}
+ } else if (legend) {
+ layout.removeBox(chart, legend);
+ delete chart.legend;
}
- };
+ },
+
+ afterEvent: function(chart, e) {
+ var legend = chart.legend;
+ if (legend) {
+ legend.handleEvent(e);
+ }
+ }
};
var helpers = require('../helpers/index');
var layout = require('../core/core.layout');
+var noop = helpers.noop;
+
defaults._set('global', {
title: {
display: false,
}
});
-module.exports = function(Chart) {
-
- var noop = helpers.noop;
-
- Chart.Title = Element.extend({
- initialize: function(config) {
- var me = this;
- helpers.extend(me, config);
-
- // Contains hit boxes for each dataset (in dataset order)
- me.legendHitBoxes = [];
- },
-
- // These methods are ordered by lifecycle. Utilities then follow.
-
- beforeUpdate: noop,
- update: function(maxWidth, maxHeight, margins) {
- var me = this;
-
- // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
- me.beforeUpdate();
-
- // Absorb the master measurements
- me.maxWidth = maxWidth;
- me.maxHeight = maxHeight;
- me.margins = margins;
-
- // Dimensions
- me.beforeSetDimensions();
- me.setDimensions();
- me.afterSetDimensions();
- // Labels
- me.beforeBuildLabels();
- me.buildLabels();
- me.afterBuildLabels();
-
- // Fit
- me.beforeFit();
- me.fit();
- me.afterFit();
- //
- me.afterUpdate();
-
- return me.minSize;
-
- },
- afterUpdate: noop,
-
- //
-
- beforeSetDimensions: noop,
- setDimensions: function() {
- var me = this;
- // Set the unconstrained dimension before label rotation
- if (me.isHorizontal()) {
- // Reset position before calculating rotation
- me.width = me.maxWidth;
- me.left = 0;
- me.right = me.width;
- } else {
- me.height = me.maxHeight;
-
- // Reset position before calculating rotation
- me.top = 0;
- me.bottom = me.height;
- }
-
- // Reset padding
- me.paddingLeft = 0;
- me.paddingTop = 0;
- me.paddingRight = 0;
- me.paddingBottom = 0;
-
- // Reset minSize
- me.minSize = {
- width: 0,
- height: 0
- };
- },
- afterSetDimensions: noop,
-
+/**
+ * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
+ */
+var Title = Element.extend({
+ initialize: function(config) {
+ var me = this;
+ helpers.extend(me, config);
+
+ // Contains hit boxes for each dataset (in dataset order)
+ me.legendHitBoxes = [];
+ },
+
+ // These methods are ordered by lifecycle. Utilities then follow.
+
+ beforeUpdate: noop,
+ update: function(maxWidth, maxHeight, margins) {
+ var me = this;
+
+ // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
+ me.beforeUpdate();
+
+ // Absorb the master measurements
+ me.maxWidth = maxWidth;
+ me.maxHeight = maxHeight;
+ me.margins = margins;
+
+ // Dimensions
+ me.beforeSetDimensions();
+ me.setDimensions();
+ me.afterSetDimensions();
+ // Labels
+ me.beforeBuildLabels();
+ me.buildLabels();
+ me.afterBuildLabels();
+
+ // Fit
+ me.beforeFit();
+ me.fit();
+ me.afterFit();
//
+ me.afterUpdate();
+
+ return me.minSize;
+
+ },
+ afterUpdate: noop,
+
+ //
+
+ beforeSetDimensions: noop,
+ setDimensions: function() {
+ var me = this;
+ // Set the unconstrained dimension before label rotation
+ if (me.isHorizontal()) {
+ // Reset position before calculating rotation
+ me.width = me.maxWidth;
+ me.left = 0;
+ me.right = me.width;
+ } else {
+ me.height = me.maxHeight;
+
+ // Reset position before calculating rotation
+ me.top = 0;
+ me.bottom = me.height;
+ }
- beforeBuildLabels: noop,
- buildLabels: noop,
- afterBuildLabels: noop,
-
- //
+ // Reset padding
+ me.paddingLeft = 0;
+ me.paddingTop = 0;
+ me.paddingRight = 0;
+ me.paddingBottom = 0;
+
+ // Reset minSize
+ me.minSize = {
+ width: 0,
+ height: 0
+ };
+ },
+ afterSetDimensions: noop,
+
+ //
+
+ beforeBuildLabels: noop,
+ buildLabels: noop,
+ afterBuildLabels: noop,
+
+ //
+
+ beforeFit: noop,
+ fit: function() {
+ var me = this;
+ var valueOrDefault = helpers.valueOrDefault;
+ var opts = me.options;
+ var display = opts.display;
+ var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
+ var minSize = me.minSize;
+ var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
+ var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
+ var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
+
+ if (me.isHorizontal()) {
+ minSize.width = me.maxWidth; // fill all the width
+ minSize.height = textSize;
+ } else {
+ minSize.width = textSize;
+ minSize.height = me.maxHeight; // fill all the height
+ }
- beforeFit: noop,
- fit: function() {
- var me = this;
- var valueOrDefault = helpers.valueOrDefault;
- var opts = me.options;
- var display = opts.display;
- var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
- var minSize = me.minSize;
- var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
+ me.width = minSize.width;
+ me.height = minSize.height;
+
+ },
+ afterFit: noop,
+
+ // Shared Methods
+ isHorizontal: function() {
+ var pos = this.options.position;
+ return pos === 'top' || pos === 'bottom';
+ },
+
+ // Actually draw the title block on the canvas
+ draw: function() {
+ var me = this;
+ var ctx = me.ctx;
+ var valueOrDefault = helpers.valueOrDefault;
+ var opts = me.options;
+ var globalDefaults = defaults.global;
+
+ if (opts.display) {
+ var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
+ var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
+ var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
+ var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
- var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
-
+ var offset = lineHeight / 2 + opts.padding;
+ var rotation = 0;
+ var top = me.top;
+ var left = me.left;
+ var bottom = me.bottom;
+ var right = me.right;
+ var maxWidth, titleX, titleY;
+
+ ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
+ ctx.font = titleFont;
+
+ // Horizontal
if (me.isHorizontal()) {
- minSize.width = me.maxWidth; // fill all the width
- minSize.height = textSize;
+ titleX = left + ((right - left) / 2); // midpoint of the width
+ titleY = top + offset;
+ maxWidth = right - left;
} else {
- minSize.width = textSize;
- minSize.height = me.maxHeight; // fill all the height
+ titleX = opts.position === 'left' ? left + offset : right - offset;
+ titleY = top + ((bottom - top) / 2);
+ maxWidth = bottom - top;
+ rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
}
- me.width = minSize.width;
- me.height = minSize.height;
-
- },
- afterFit: noop,
-
- // Shared Methods
- isHorizontal: function() {
- var pos = this.options.position;
- return pos === 'top' || pos === 'bottom';
- },
-
- // Actually draw the title block on the canvas
- draw: function() {
- var me = this;
- var ctx = me.ctx;
- var valueOrDefault = helpers.valueOrDefault;
- var opts = me.options;
- var globalDefaults = defaults.global;
-
- if (opts.display) {
- var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
- var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
- var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
- var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
- var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
- var offset = lineHeight / 2 + opts.padding;
- var rotation = 0;
- var top = me.top;
- var left = me.left;
- var bottom = me.bottom;
- var right = me.right;
- var maxWidth, titleX, titleY;
-
- ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
- ctx.font = titleFont;
-
- // Horizontal
- if (me.isHorizontal()) {
- titleX = left + ((right - left) / 2); // midpoint of the width
- titleY = top + offset;
- maxWidth = right - left;
- } else {
- titleX = opts.position === 'left' ? left + offset : right - offset;
- titleY = top + ((bottom - top) / 2);
- maxWidth = bottom - top;
- rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
+ ctx.save();
+ ctx.translate(titleX, titleY);
+ ctx.rotate(rotation);
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+
+ var text = opts.text;
+ if (helpers.isArray(text)) {
+ var y = 0;
+ for (var i = 0; i < text.length; ++i) {
+ ctx.fillText(text[i], 0, y, maxWidth);
+ y += lineHeight;
}
-
- ctx.save();
- ctx.translate(titleX, titleY);
- ctx.rotate(rotation);
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
-
- var text = opts.text;
- if (helpers.isArray(text)) {
- var y = 0;
- for (var i = 0; i < text.length; ++i) {
- ctx.fillText(text[i], 0, y, maxWidth);
- y += lineHeight;
- }
- } else {
- ctx.fillText(text, 0, 0, maxWidth);
- }
-
- ctx.restore();
+ } else {
+ ctx.fillText(text, 0, 0, maxWidth);
}
+
+ ctx.restore();
}
+ }
+});
+
+function createNewTitleBlockAndAttach(chart, titleOpts) {
+ var title = new Title({
+ ctx: chart.ctx,
+ options: titleOpts,
+ chart: chart
});
- function createNewTitleBlockAndAttach(chart, titleOpts) {
- var title = new Chart.Title({
- ctx: chart.ctx,
- options: titleOpts,
- chart: chart
- });
+ layout.configure(chart, title, titleOpts);
+ layout.addBox(chart, title);
+ chart.titleBlock = title;
+}
- layout.configure(chart, title, titleOpts);
- layout.addBox(chart, title);
- chart.titleBlock = title;
- }
+module.exports = {
+ id: 'title',
- return {
- id: 'title',
+ /**
+ * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
+ * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
+ * the plugin, which one will be re-exposed in the chart.js file.
+ * https://github.com/chartjs/Chart.js/pull/2640
+ * @private
+ */
+ _element: Title,
- beforeInit: function(chart) {
- var titleOpts = chart.options.title;
+ beforeInit: function(chart) {
+ var titleOpts = chart.options.title;
- if (titleOpts) {
- createNewTitleBlockAndAttach(chart, titleOpts);
- }
- },
+ if (titleOpts) {
+ createNewTitleBlockAndAttach(chart, titleOpts);
+ }
+ },
- beforeUpdate: function(chart) {
- var titleOpts = chart.options.title;
- var titleBlock = chart.titleBlock;
+ beforeUpdate: function(chart) {
+ var titleOpts = chart.options.title;
+ var titleBlock = chart.titleBlock;
- if (titleOpts) {
- helpers.mergeIf(titleOpts, defaults.global.title);
+ if (titleOpts) {
+ helpers.mergeIf(titleOpts, defaults.global.title);
- if (titleBlock) {
- layout.configure(chart, titleBlock, titleOpts);
- titleBlock.options = titleOpts;
- } else {
- createNewTitleBlockAndAttach(chart, titleOpts);
- }
- } else if (titleBlock) {
- layout.removeBox(chart, titleBlock);
- delete chart.titleBlock;
+ if (titleBlock) {
+ layout.configure(chart, titleBlock, titleOpts);
+ titleBlock.options = titleOpts;
+ } else {
+ createNewTitleBlockAndAttach(chart, titleOpts);
}
+ } else if (titleBlock) {
+ layout.removeBox(chart, titleBlock);
+ delete chart.titleBlock;
}
- };
+ }
};
expect(Chart.pluginService).toBe(Chart.plugins);
});
});
+
+ describe('Chart.Legend', function() {
+ it('should be defined and an instance of Chart.Element', function() {
+ var legend = new Chart.Legend({});
+ expect(Chart.Legend).toBeDefined();
+ expect(legend).not.toBe(undefined);
+ expect(legend instanceof Chart.Element).toBeTruthy();
+ });
+ });
+
+ describe('Chart.Title', function() {
+ it('should be defined and an instance of Chart.Element', function() {
+ var title = new Chart.Title({});
+ expect(Chart.Title).toBeDefined();
+ expect(title).not.toBe(undefined);
+ expect(title instanceof Chart.Element).toBeTruthy();
+ });
+ });
});
});
// Test the rectangle element
describe('Legend block tests', function() {
- it('Should be constructed', function() {
- var legend = new Chart.Legend({});
- expect(legend).not.toBe(undefined);
- });
-
it('should have the correct default config', function() {
expect(Chart.defaults.global.legend).toEqual({
display: true,
// Test the rectangle element
describe('Title block tests', function() {
- it('Should be constructed', function() {
- var title = new Chart.Title({});
- expect(title).not.toBe(undefined);
- });
-
it('Should have the correct default config', function() {
expect(Chart.defaults.global.title).toEqual({
display: false,