]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Make the main controller importable (#5969)
authorSimon Brunel <simonbrunel@users.noreply.github.com>
Wed, 9 Jan 2019 13:40:11 +0000 (14:40 +0100)
committerGitHub <noreply@github.com>
Wed, 9 Jan 2019 13:40:11 +0000 (14:40 +0100)
Merge `core/core.js` in `core/core.controller.js`, split default options next to their associated code and deprecate `Chart.types` (not sure what it was for).

src/chart.js
src/core/core.controller.js
src/core/core.defaults.js
src/core/core.js [deleted file]
src/core/core.layouts.js
test/specs/global.deprecations.tests.js

index b60129f2cf5c90c855e06d18005603af37d24687..d74a6d82efba8219b39cc1c8ecff2857510dff87 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * @namespace Chart
  */
-var Chart = require('./core/core')();
+var Chart = require('./core/core.controller');
 
 Chart.helpers = require('./helpers/index');
 
@@ -24,8 +24,6 @@ Chart.scaleService = require('./core/core.scaleService');
 Chart.Ticks = require('./core/core.ticks');
 Chart.Tooltip = require('./core/core.tooltip');
 
-require('./core/core.controller')(Chart);
-
 // Register built-in scales
 var scales = require('./scales');
 Chart.helpers.each(scales, function(scale, type) {
index 0a97f6aada5f5fef3cebfd3457f608496c95b210..b0175e8a67f621d35b1ef10e0f1bf81867672021 100644 (file)
@@ -14,947 +14,974 @@ var Tooltip = require('./core.tooltip');
 
 var valueOrDefault = helpers.valueOrDefault;
 
-module.exports = function(Chart) {
-
-       // Create a dictionary of chart types, to allow for extension of existing types
-       Chart.types = {};
+defaults._set('global', {
+       elements: {},
+       events: [
+               'mousemove',
+               'mouseout',
+               'click',
+               'touchstart',
+               'touchmove'
+       ],
+       hover: {
+               onHover: null,
+               mode: 'nearest',
+               intersect: true,
+               animationDuration: 400
+       },
+       onClick: null,
+       maintainAspectRatio: true,
+       responsive: true,
+       responsiveAnimationDuration: 0
+});
+
+function initConfig(config) {
+       config = config || {};
+
+       // Do NOT use configMerge() for the data object because this method merges arrays
+       // and so would change references to labels and datasets, preventing data updates.
+       var data = config.data = config.data || {};
+       data.datasets = data.datasets || [];
+       data.labels = data.labels || [];
+
+       config.options = helpers.configMerge(
+               defaults.global,
+               defaults[config.type],
+               config.options || {});
+
+       return config;
+}
+
+function updateConfig(chart) {
+       var newOptions = chart.options;
+
+       helpers.each(chart.scales, function(scale) {
+               layouts.removeBox(chart, scale);
+       });
 
-       // Store a reference to each instance - allowing us to globally resize chart instances on window resize.
-       // Destroy method on the chart will remove the instance of the chart from this reference.
-       Chart.instances = {};
+       newOptions = helpers.configMerge(
+               defaults.global,
+               defaults[chart.config.type],
+               newOptions);
 
-       /**
-        * Initializes the given config with global and chart default values.
-        */
-       function initConfig(config) {
-               config = config || {};
+       chart.options = chart.config.options = newOptions;
+       chart.ensureScalesHaveIDs();
+       chart.buildOrUpdateScales();
 
-               // Do NOT use configMerge() for the data object because this method merges arrays
-               // and so would change references to labels and datasets, preventing data updates.
-               var data = config.data = config.data || {};
-               data.datasets = data.datasets || [];
-               data.labels = data.labels || [];
+       // Tooltip
+       chart.tooltip._options = newOptions.tooltips;
+       chart.tooltip.initialize();
+}
 
-               config.options = helpers.configMerge(
-                       defaults.global,
-                       defaults[config.type],
-                       config.options || {});
+function positionIsHorizontal(position) {
+       return position === 'top' || position === 'bottom';
+}
 
-               return config;
-       }
+var Chart = function(item, config) {
+       this.construct(item, config);
+       return this;
+};
 
+helpers.extend(Chart.prototype, /** @lends Chart */ {
        /**
-        * Updates the config of the chart
-        * @param chart {Chart} chart to update the options for
+        * @private
         */
-       function updateConfig(chart) {
-               var newOptions = chart.options;
-
-               helpers.each(chart.scales, function(scale) {
-                       layouts.removeBox(chart, scale);
-               });
-
-               newOptions = helpers.configMerge(
-                       Chart.defaults.global,
-                       Chart.defaults[chart.config.type],
-                       newOptions);
-
-               chart.options = chart.config.options = newOptions;
-               chart.ensureScalesHaveIDs();
-               chart.buildOrUpdateScales();
-               // Tooltip
-               chart.tooltip._options = newOptions.tooltips;
-               chart.tooltip.initialize();
-       }
-
-       function positionIsHorizontal(position) {
-               return position === 'top' || position === 'bottom';
-       }
+       construct: function(item, config) {
+               var me = this;
+
+               config = initConfig(config);
+
+               var context = platform.acquireContext(item, config);
+               var canvas = context && context.canvas;
+               var height = canvas && canvas.height;
+               var width = canvas && canvas.width;
+
+               me.id = helpers.uid();
+               me.ctx = context;
+               me.canvas = canvas;
+               me.config = config;
+               me.width = width;
+               me.height = height;
+               me.aspectRatio = height ? width / height : null;
+               me.options = config.options;
+               me._bufferedRender = false;
 
-       helpers.extend(Chart.prototype, /** @lends Chart */ {
                /**
+                * Provided for backward compatibility, Chart and Chart.Controller have been merged,
+                * the "instance" still need to be defined since it might be called from plugins.
+                * @prop Chart#chart
+                * @deprecated since version 2.6.0
+                * @todo remove at version 3
                 * @private
                 */
-               construct: function(item, config) {
-                       var me = this;
-
-                       config = initConfig(config);
-
-                       var context = platform.acquireContext(item, config);
-                       var canvas = context && context.canvas;
-                       var height = canvas && canvas.height;
-                       var width = canvas && canvas.width;
-
-                       me.id = helpers.uid();
-                       me.ctx = context;
-                       me.canvas = canvas;
-                       me.config = config;
-                       me.width = width;
-                       me.height = height;
-                       me.aspectRatio = height ? width / height : null;
-                       me.options = config.options;
-                       me._bufferedRender = false;
-
-                       /**
-                        * Provided for backward compatibility, Chart and Chart.Controller have been merged,
-                        * the "instance" still need to be defined since it might be called from plugins.
-                        * @prop Chart#chart
-                        * @deprecated since version 2.6.0
-                        * @todo remove at version 3
-                        * @private
-                        */
-                       me.chart = me;
-                       me.controller = me; // chart.chart.controller #inception
-
-                       // Add the chart instance to the global namespace
-                       Chart.instances[me.id] = me;
-
-                       // Define alias to the config data: `chart.data === chart.config.data`
-                       Object.defineProperty(me, 'data', {
-                               get: function() {
-                                       return me.config.data;
-                               },
-                               set: function(value) {
-                                       me.config.data = value;
-                               }
-                       });
+               me.chart = me;
+               me.controller = me; // chart.chart.controller #inception
 
-                       if (!context || !canvas) {
-                               // The given item is not a compatible context2d element, let's return before finalizing
-                               // the chart initialization but after setting basic chart / controller properties that
-                               // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
-                               // https://github.com/chartjs/Chart.js/issues/2807
-                               console.error("Failed to create chart: can't acquire context from the given item");
-                               return;
+               // Add the chart instance to the global namespace
+               Chart.instances[me.id] = me;
+
+               // Define alias to the config data: `chart.data === chart.config.data`
+               Object.defineProperty(me, 'data', {
+                       get: function() {
+                               return me.config.data;
+                       },
+                       set: function(value) {
+                               me.config.data = value;
                        }
+               });
 
-                       me.initialize();
-                       me.update();
-               },
+               if (!context || !canvas) {
+                       // The given item is not a compatible context2d element, let's return before finalizing
+                       // the chart initialization but after setting basic chart / controller properties that
+                       // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
+                       // https://github.com/chartjs/Chart.js/issues/2807
+                       console.error("Failed to create chart: can't acquire context from the given item");
+                       return;
+               }
 
-               /**
-                * @private
-                */
-               initialize: function() {
-                       var me = this;
+               me.initialize();
+               me.update();
+       },
 
-                       // Before init plugin notification
-                       plugins.notify(me, 'beforeInit');
+       /**
+        * @private
+        */
+       initialize: function() {
+               var me = this;
 
-                       helpers.retinaScale(me, me.options.devicePixelRatio);
+               // Before init plugin notification
+               plugins.notify(me, 'beforeInit');
 
-                       me.bindEvents();
+               helpers.retinaScale(me, me.options.devicePixelRatio);
 
-                       if (me.options.responsive) {
-                               // Initial resize before chart draws (must be silent to preserve initial animations).
-                               me.resize(true);
-                       }
+               me.bindEvents();
 
-                       // Make sure scales have IDs and are built before we build any controllers.
-                       me.ensureScalesHaveIDs();
-                       me.buildOrUpdateScales();
-                       me.initToolTip();
+               if (me.options.responsive) {
+                       // Initial resize before chart draws (must be silent to preserve initial animations).
+                       me.resize(true);
+               }
 
-                       // After init plugin notification
-                       plugins.notify(me, 'afterInit');
+               // Make sure scales have IDs and are built before we build any controllers.
+               me.ensureScalesHaveIDs();
+               me.buildOrUpdateScales();
+               me.initToolTip();
 
-                       return me;
-               },
+               // After init plugin notification
+               plugins.notify(me, 'afterInit');
 
-               clear: function() {
-                       helpers.canvas.clear(this);
-                       return this;
-               },
+               return me;
+       },
 
-               stop: function() {
-                       // Stops any current animation loop occurring
-                       animations.cancelAnimation(this);
-                       return this;
-               },
+       clear: function() {
+               helpers.canvas.clear(this);
+               return this;
+       },
 
-               resize: function(silent) {
-                       var me = this;
-                       var options = me.options;
-                       var canvas = me.canvas;
-                       var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
+       stop: function() {
+               // Stops any current animation loop occurring
+               animations.cancelAnimation(this);
+               return this;
+       },
 
-                       // the canvas render width and height will be casted to integers so make sure that
-                       // the canvas display style uses the same integer values to avoid blurring effect.
+       resize: function(silent) {
+               var me = this;
+               var options = me.options;
+               var canvas = me.canvas;
+               var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
 
-                       // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
-                       var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
-                       var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
+               // the canvas render width and height will be casted to integers so make sure that
+               // the canvas display style uses the same integer values to avoid blurring effect.
 
-                       if (me.width === newWidth && me.height === newHeight) {
-                               return;
-                       }
+               // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
+               var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
+               var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
 
-                       canvas.width = me.width = newWidth;
-                       canvas.height = me.height = newHeight;
-                       canvas.style.width = newWidth + 'px';
-                       canvas.style.height = newHeight + 'px';
+               if (me.width === newWidth && me.height === newHeight) {
+                       return;
+               }
 
-                       helpers.retinaScale(me, options.devicePixelRatio);
+               canvas.width = me.width = newWidth;
+               canvas.height = me.height = newHeight;
+               canvas.style.width = newWidth + 'px';
+               canvas.style.height = newHeight + 'px';
 
-                       if (!silent) {
-                               // Notify any plugins about the resize
-                               var newSize = {width: newWidth, height: newHeight};
-                               plugins.notify(me, 'resize', [newSize]);
+               helpers.retinaScale(me, options.devicePixelRatio);
 
-                               // Notify of resize
-                               if (me.options.onResize) {
-                                       me.options.onResize(me, newSize);
-                               }
+               if (!silent) {
+                       // Notify any plugins about the resize
+                       var newSize = {width: newWidth, height: newHeight};
+                       plugins.notify(me, 'resize', [newSize]);
 
-                               me.stop();
-                               me.update({
-                                       duration: me.options.responsiveAnimationDuration
-                               });
+                       // Notify of resize
+                       if (me.options.onResize) {
+                               me.options.onResize(me, newSize);
                        }
-               },
-
-               ensureScalesHaveIDs: function() {
-                       var options = this.options;
-                       var scalesOptions = options.scales || {};
-                       var scaleOptions = options.scale;
 
-                       helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
-                               xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
+                       me.stop();
+                       me.update({
+                               duration: me.options.responsiveAnimationDuration
                        });
+               }
+       },
 
-                       helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
-                               yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
-                       });
+       ensureScalesHaveIDs: function() {
+               var options = this.options;
+               var scalesOptions = options.scales || {};
+               var scaleOptions = options.scale;
 
-                       if (scaleOptions) {
-                               scaleOptions.id = scaleOptions.id || 'scale';
-                       }
-               },
+               helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
+                       xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
+               });
 
-               /**
-                * Builds a map of scale ID to scale object for future lookup.
-                */
-               buildOrUpdateScales: function() {
-                       var me = this;
-                       var options = me.options;
-                       var scales = me.scales || {};
-                       var items = [];
-                       var updated = Object.keys(scales).reduce(function(obj, id) {
-                               obj[id] = false;
-                               return obj;
-                       }, {});
-
-                       if (options.scales) {
-                               items = items.concat(
-                                       (options.scales.xAxes || []).map(function(xAxisOptions) {
-                                               return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
-                                       }),
-                                       (options.scales.yAxes || []).map(function(yAxisOptions) {
-                                               return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
-                                       })
-                               );
-                       }
+               helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
+                       yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
+               });
 
-                       if (options.scale) {
-                               items.push({
-                                       options: options.scale,
-                                       dtype: 'radialLinear',
-                                       isDefault: true,
-                                       dposition: 'chartArea'
-                               });
-                       }
+               if (scaleOptions) {
+                       scaleOptions.id = scaleOptions.id || 'scale';
+               }
+       },
 
-                       helpers.each(items, function(item) {
-                               var scaleOptions = item.options;
-                               var id = scaleOptions.id;
-                               var scaleType = valueOrDefault(scaleOptions.type, item.dtype);
+       /**
+        * Builds a map of scale ID to scale object for future lookup.
+        */
+       buildOrUpdateScales: function() {
+               var me = this;
+               var options = me.options;
+               var scales = me.scales || {};
+               var items = [];
+               var updated = Object.keys(scales).reduce(function(obj, id) {
+                       obj[id] = false;
+                       return obj;
+               }, {});
+
+               if (options.scales) {
+                       items = items.concat(
+                               (options.scales.xAxes || []).map(function(xAxisOptions) {
+                                       return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
+                               }),
+                               (options.scales.yAxes || []).map(function(yAxisOptions) {
+                                       return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
+                               })
+                       );
+               }
 
-                               if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
-                                       scaleOptions.position = item.dposition;
-                               }
+               if (options.scale) {
+                       items.push({
+                               options: options.scale,
+                               dtype: 'radialLinear',
+                               isDefault: true,
+                               dposition: 'chartArea'
+                       });
+               }
 
-                               updated[id] = true;
-                               var scale = null;
-                               if (id in scales && scales[id].type === scaleType) {
-                                       scale = scales[id];
-                                       scale.options = scaleOptions;
-                                       scale.ctx = me.ctx;
-                                       scale.chart = me;
-                               } else {
-                                       var scaleClass = scaleService.getScaleConstructor(scaleType);
-                                       if (!scaleClass) {
-                                               return;
-                                       }
-                                       scale = new scaleClass({
-                                               id: id,
-                                               type: scaleType,
-                                               options: scaleOptions,
-                                               ctx: me.ctx,
-                                               chart: me
-                                       });
-                                       scales[scale.id] = scale;
-                               }
+               helpers.each(items, function(item) {
+                       var scaleOptions = item.options;
+                       var id = scaleOptions.id;
+                       var scaleType = valueOrDefault(scaleOptions.type, item.dtype);
 
-                               scale.mergeTicksOptions();
+                       if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
+                               scaleOptions.position = item.dposition;
+                       }
 
-                               // TODO(SB): I think we should be able to remove this custom case (options.scale)
-                               // and consider it as a regular scale part of the "scales"" map only! This would
-                               // make the logic easier and remove some useless? custom code.
-                               if (item.isDefault) {
-                                       me.scale = scale;
-                               }
-                       });
-                       // clear up discarded scales
-                       helpers.each(updated, function(hasUpdated, id) {
-                               if (!hasUpdated) {
-                                       delete scales[id];
+                       updated[id] = true;
+                       var scale = null;
+                       if (id in scales && scales[id].type === scaleType) {
+                               scale = scales[id];
+                               scale.options = scaleOptions;
+                               scale.ctx = me.ctx;
+                               scale.chart = me;
+                       } else {
+                               var scaleClass = scaleService.getScaleConstructor(scaleType);
+                               if (!scaleClass) {
+                                       return;
                                }
-                       });
+                               scale = new scaleClass({
+                                       id: id,
+                                       type: scaleType,
+                                       options: scaleOptions,
+                                       ctx: me.ctx,
+                                       chart: me
+                               });
+                               scales[scale.id] = scale;
+                       }
 
-                       me.scales = scales;
+                       scale.mergeTicksOptions();
 
-                       scaleService.addScalesToLayout(this);
-               },
+                       // TODO(SB): I think we should be able to remove this custom case (options.scale)
+                       // and consider it as a regular scale part of the "scales"" map only! This would
+                       // make the logic easier and remove some useless? custom code.
+                       if (item.isDefault) {
+                               me.scale = scale;
+                       }
+               });
+               // clear up discarded scales
+               helpers.each(updated, function(hasUpdated, id) {
+                       if (!hasUpdated) {
+                               delete scales[id];
+                       }
+               });
 
-               buildOrUpdateControllers: function() {
-                       var me = this;
-                       var types = [];
-                       var newControllers = [];
+               me.scales = scales;
 
-                       helpers.each(me.data.datasets, function(dataset, datasetIndex) {
-                               var meta = me.getDatasetMeta(datasetIndex);
-                               var type = dataset.type || me.config.type;
+               scaleService.addScalesToLayout(this);
+       },
 
-                               if (meta.type && meta.type !== type) {
-                                       me.destroyDatasetMeta(datasetIndex);
-                                       meta = me.getDatasetMeta(datasetIndex);
-                               }
-                               meta.type = type;
+       buildOrUpdateControllers: function() {
+               var me = this;
+               var newControllers = [];
 
-                               types.push(meta.type);
+               helpers.each(me.data.datasets, function(dataset, datasetIndex) {
+                       var meta = me.getDatasetMeta(datasetIndex);
+                       var type = dataset.type || me.config.type;
 
-                               if (meta.controller) {
-                                       meta.controller.updateIndex(datasetIndex);
-                                       meta.controller.linkScales();
-                               } else {
-                                       var ControllerClass = controllers[meta.type];
-                                       if (ControllerClass === undefined) {
-                                               throw new Error('"' + meta.type + '" is not a chart type.');
-                                       }
+                       if (meta.type && meta.type !== type) {
+                               me.destroyDatasetMeta(datasetIndex);
+                               meta = me.getDatasetMeta(datasetIndex);
+                       }
+                       meta.type = type;
 
-                                       meta.controller = new ControllerClass(me, datasetIndex);
-                                       newControllers.push(meta.controller);
+                       if (meta.controller) {
+                               meta.controller.updateIndex(datasetIndex);
+                               meta.controller.linkScales();
+                       } else {
+                               var ControllerClass = controllers[meta.type];
+                               if (ControllerClass === undefined) {
+                                       throw new Error('"' + meta.type + '" is not a chart type.');
                                }
-                       }, me);
 
-                       return newControllers;
-               },
+                               meta.controller = new ControllerClass(me, datasetIndex);
+                               newControllers.push(meta.controller);
+                       }
+               }, me);
 
-               /**
-                * Reset the elements of all datasets
-                * @private
-                */
-               resetElements: function() {
-                       var me = this;
-                       helpers.each(me.data.datasets, function(dataset, datasetIndex) {
-                               me.getDatasetMeta(datasetIndex).controller.reset();
-                       }, me);
-               },
+               return newControllers;
+       },
 
-               /**
-               * Resets the chart back to it's state before the initial animation
-               */
-               reset: function() {
-                       this.resetElements();
-                       this.tooltip.initialize();
-               },
-
-               update: function(config) {
-                       var me = this;
-
-                       if (!config || typeof config !== 'object') {
-                               // backwards compatibility
-                               config = {
-                                       duration: config,
-                                       lazy: arguments[1]
-                               };
-                       }
+       /**
+        * Reset the elements of all datasets
+        * @private
+        */
+       resetElements: function() {
+               var me = this;
+               helpers.each(me.data.datasets, function(dataset, datasetIndex) {
+                       me.getDatasetMeta(datasetIndex).controller.reset();
+               }, me);
+       },
 
-                       updateConfig(me);
+       /**
+       * Resets the chart back to it's state before the initial animation
+       */
+       reset: function() {
+               this.resetElements();
+               this.tooltip.initialize();
+       },
+
+       update: function(config) {
+               var me = this;
+
+               if (!config || typeof config !== 'object') {
+                       // backwards compatibility
+                       config = {
+                               duration: config,
+                               lazy: arguments[1]
+                       };
+               }
 
-                       // plugins options references might have change, let's invalidate the cache
-                       // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
-                       plugins._invalidate(me);
+               updateConfig(me);
 
-                       if (plugins.notify(me, 'beforeUpdate') === false) {
-                               return;
-                       }
+               // plugins options references might have change, let's invalidate the cache
+               // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
+               plugins._invalidate(me);
 
-                       // In case the entire data object changed
-                       me.tooltip._data = me.data;
+               if (plugins.notify(me, 'beforeUpdate') === false) {
+                       return;
+               }
 
-                       // Make sure dataset controllers are updated and new controllers are reset
-                       var newControllers = me.buildOrUpdateControllers();
+               // In case the entire data object changed
+               me.tooltip._data = me.data;
 
-                       // Make sure all dataset controllers have correct meta data counts
-                       helpers.each(me.data.datasets, function(dataset, datasetIndex) {
-                               me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
-                       }, me);
+               // Make sure dataset controllers are updated and new controllers are reset
+               var newControllers = me.buildOrUpdateControllers();
 
-                       me.updateLayout();
+               // Make sure all dataset controllers have correct meta data counts
+               helpers.each(me.data.datasets, function(dataset, datasetIndex) {
+                       me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
+               }, me);
 
-                       // Can only reset the new controllers after the scales have been updated
-                       if (me.options.animation && me.options.animation.duration) {
-                               helpers.each(newControllers, function(controller) {
-                                       controller.reset();
-                               });
-                       }
+               me.updateLayout();
 
-                       me.updateDatasets();
+               // Can only reset the new controllers after the scales have been updated
+               if (me.options.animation && me.options.animation.duration) {
+                       helpers.each(newControllers, function(controller) {
+                               controller.reset();
+                       });
+               }
 
-                       // Need to reset tooltip in case it is displayed with elements that are removed
-                       // after update.
-                       me.tooltip.initialize();
+               me.updateDatasets();
 
-                       // Last active contains items that were previously in the tooltip.
-                       // When we reset the tooltip, we need to clear it
-                       me.lastActive = [];
+               // Need to reset tooltip in case it is displayed with elements that are removed
+               // after update.
+               me.tooltip.initialize();
 
-                       // Do this before render so that any plugins that need final scale updates can use it
-                       plugins.notify(me, 'afterUpdate');
+               // Last active contains items that were previously in the tooltip.
+               // When we reset the tooltip, we need to clear it
+               me.lastActive = [];
 
-                       if (me._bufferedRender) {
-                               me._bufferedRequest = {
-                                       duration: config.duration,
-                                       easing: config.easing,
-                                       lazy: config.lazy
-                               };
-                       } else {
-                               me.render(config);
-                       }
-               },
+               // Do this before render so that any plugins that need final scale updates can use it
+               plugins.notify(me, 'afterUpdate');
 
-               /**
-                * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
-                * hook, in which case, plugins will not be called on `afterLayout`.
-                * @private
-                */
-               updateLayout: function() {
-                       var me = this;
+               if (me._bufferedRender) {
+                       me._bufferedRequest = {
+                               duration: config.duration,
+                               easing: config.easing,
+                               lazy: config.lazy
+                       };
+               } else {
+                       me.render(config);
+               }
+       },
 
-                       if (plugins.notify(me, 'beforeLayout') === false) {
-                               return;
-                       }
+       /**
+        * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
+        * hook, in which case, plugins will not be called on `afterLayout`.
+        * @private
+        */
+       updateLayout: function() {
+               var me = this;
 
-                       layouts.update(this, this.width, this.height);
+               if (plugins.notify(me, 'beforeLayout') === false) {
+                       return;
+               }
 
-                       /**
-                        * Provided for backward compatibility, use `afterLayout` instead.
-                        * @method IPlugin#afterScaleUpdate
-                        * @deprecated since version 2.5.0
-                        * @todo remove at version 3
-                        * @private
-                        */
-                       plugins.notify(me, 'afterScaleUpdate');
-                       plugins.notify(me, 'afterLayout');
-               },
+               layouts.update(this, this.width, this.height);
 
                /**
-                * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
-                * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
+                * Provided for backward compatibility, use `afterLayout` instead.
+                * @method IPlugin#afterScaleUpdate
+                * @deprecated since version 2.5.0
+                * @todo remove at version 3
                 * @private
                 */
-               updateDatasets: function() {
-                       var me = this;
+               plugins.notify(me, 'afterScaleUpdate');
+               plugins.notify(me, 'afterLayout');
+       },
 
-                       if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
-                               return;
-                       }
+       /**
+        * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
+        * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
+        * @private
+        */
+       updateDatasets: function() {
+               var me = this;
 
-                       for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
-                               me.updateDataset(i);
-                       }
+               if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
+                       return;
+               }
 
-                       plugins.notify(me, 'afterDatasetsUpdate');
-               },
+               for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
+                       me.updateDataset(i);
+               }
 
-               /**
-                * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
-                * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
-                * @private
-                */
-               updateDataset: function(index) {
-                       var me = this;
-                       var meta = me.getDatasetMeta(index);
-                       var args = {
-                               meta: meta,
-                               index: index
-                       };
+               plugins.notify(me, 'afterDatasetsUpdate');
+       },
 
-                       if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
-                               return;
-                       }
+       /**
+        * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
+        * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
+        * @private
+        */
+       updateDataset: function(index) {
+               var me = this;
+               var meta = me.getDatasetMeta(index);
+               var args = {
+                       meta: meta,
+                       index: index
+               };
+
+               if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
+                       return;
+               }
 
-                       meta.controller.update();
+               meta.controller.update();
 
-                       plugins.notify(me, 'afterDatasetUpdate', [args]);
-               },
+               plugins.notify(me, 'afterDatasetUpdate', [args]);
+       },
 
-               render: function(config) {
-                       var me = this;
+       render: function(config) {
+               var me = this;
 
-                       if (!config || typeof config !== 'object') {
-                               // backwards compatibility
-                               config = {
-                                       duration: config,
-                                       lazy: arguments[1]
-                               };
-                       }
+               if (!config || typeof config !== 'object') {
+                       // backwards compatibility
+                       config = {
+                               duration: config,
+                               lazy: arguments[1]
+                       };
+               }
 
-                       var animationOptions = me.options.animation;
-                       var duration = valueOrDefault(config.duration, animationOptions && animationOptions.duration);
-                       var lazy = config.lazy;
+               var animationOptions = me.options.animation;
+               var duration = valueOrDefault(config.duration, animationOptions && animationOptions.duration);
+               var lazy = config.lazy;
 
-                       if (plugins.notify(me, 'beforeRender') === false) {
-                               return;
-                       }
+               if (plugins.notify(me, 'beforeRender') === false) {
+                       return;
+               }
 
-                       var onComplete = function(animation) {
-                               plugins.notify(me, 'afterRender');
-                               helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
-                       };
+               var onComplete = function(animation) {
+                       plugins.notify(me, 'afterRender');
+                       helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
+               };
 
-                       if (animationOptions && duration) {
-                               var animation = new Animation({
-                                       numSteps: duration / 16.66, // 60 fps
-                                       easing: config.easing || animationOptions.easing,
+               if (animationOptions && duration) {
+                       var animation = new Animation({
+                               numSteps: duration / 16.66, // 60 fps
+                               easing: config.easing || animationOptions.easing,
 
-                                       render: function(chart, animationObject) {
-                                               var easingFunction = helpers.easing.effects[animationObject.easing];
-                                               var currentStep = animationObject.currentStep;
-                                               var stepDecimal = currentStep / animationObject.numSteps;
+                               render: function(chart, animationObject) {
+                                       var easingFunction = helpers.easing.effects[animationObject.easing];
+                                       var currentStep = animationObject.currentStep;
+                                       var stepDecimal = currentStep / animationObject.numSteps;
 
-                                               chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
-                                       },
+                                       chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
+                               },
 
-                                       onAnimationProgress: animationOptions.onProgress,
-                                       onAnimationComplete: onComplete
-                               });
+                               onAnimationProgress: animationOptions.onProgress,
+                               onAnimationComplete: onComplete
+                       });
 
-                               animations.addAnimation(me, animation, duration, lazy);
-                       } else {
-                               me.draw();
+                       animations.addAnimation(me, animation, duration, lazy);
+               } else {
+                       me.draw();
 
-                               // See https://github.com/chartjs/Chart.js/issues/3781
-                               onComplete(new Animation({numSteps: 0, chart: me}));
-                       }
+                       // See https://github.com/chartjs/Chart.js/issues/3781
+                       onComplete(new Animation({numSteps: 0, chart: me}));
+               }
 
-                       return me;
-               },
+               return me;
+       },
 
-               draw: function(easingValue) {
-                       var me = this;
+       draw: function(easingValue) {
+               var me = this;
 
-                       me.clear();
+               me.clear();
 
-                       if (helpers.isNullOrUndef(easingValue)) {
-                               easingValue = 1;
-                       }
+               if (helpers.isNullOrUndef(easingValue)) {
+                       easingValue = 1;
+               }
 
-                       me.transition(easingValue);
+               me.transition(easingValue);
 
-                       if (me.width <= 0 || me.height <= 0) {
-                               return;
-                       }
+               if (me.width <= 0 || me.height <= 0) {
+                       return;
+               }
 
-                       if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
-                               return;
-                       }
+               if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
+                       return;
+               }
 
-                       // Draw all the scales
-                       helpers.each(me.boxes, function(box) {
-                               box.draw(me.chartArea);
-                       }, me);
+               // Draw all the scales
+               helpers.each(me.boxes, function(box) {
+                       box.draw(me.chartArea);
+               }, me);
 
-                       if (me.scale) {
-                               me.scale.draw();
-                       }
+               if (me.scale) {
+                       me.scale.draw();
+               }
 
-                       me.drawDatasets(easingValue);
-                       me._drawTooltip(easingValue);
+               me.drawDatasets(easingValue);
+               me._drawTooltip(easingValue);
 
-                       plugins.notify(me, 'afterDraw', [easingValue]);
-               },
+               plugins.notify(me, 'afterDraw', [easingValue]);
+       },
 
-               /**
-                * @private
-                */
-               transition: function(easingValue) {
-                       var me = this;
+       /**
+        * @private
+        */
+       transition: function(easingValue) {
+               var me = this;
 
-                       for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
-                               if (me.isDatasetVisible(i)) {
-                                       me.getDatasetMeta(i).controller.transition(easingValue);
-                               }
+               for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
+                       if (me.isDatasetVisible(i)) {
+                               me.getDatasetMeta(i).controller.transition(easingValue);
                        }
+               }
 
-                       me.tooltip.transition(easingValue);
-               },
+               me.tooltip.transition(easingValue);
+       },
 
-               /**
-                * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
-                * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
-                * @private
-                */
-               drawDatasets: function(easingValue) {
-                       var me = this;
+       /**
+        * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
+        * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
+        * @private
+        */
+       drawDatasets: function(easingValue) {
+               var me = this;
 
-                       if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
-                               return;
-                       }
+               if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
+                       return;
+               }
 
-                       // Draw datasets reversed to support proper line stacking
-                       for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
-                               if (me.isDatasetVisible(i)) {
-                                       me.drawDataset(i, easingValue);
-                               }
+               // Draw datasets reversed to support proper line stacking
+               for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
+                       if (me.isDatasetVisible(i)) {
+                               me.drawDataset(i, easingValue);
                        }
+               }
 
-                       plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
-               },
+               plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
+       },
 
-               /**
-                * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
-                * hook, in which case, plugins will not be called on `afterDatasetDraw`.
-                * @private
-                */
-               drawDataset: function(index, easingValue) {
-                       var me = this;
-                       var meta = me.getDatasetMeta(index);
-                       var args = {
-                               meta: meta,
-                               index: index,
-                               easingValue: easingValue
-                       };
+       /**
+        * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
+        * hook, in which case, plugins will not be called on `afterDatasetDraw`.
+        * @private
+        */
+       drawDataset: function(index, easingValue) {
+               var me = this;
+               var meta = me.getDatasetMeta(index);
+               var args = {
+                       meta: meta,
+                       index: index,
+                       easingValue: easingValue
+               };
+
+               if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
+                       return;
+               }
 
-                       if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
-                               return;
-                       }
+               meta.controller.draw(easingValue);
 
-                       meta.controller.draw(easingValue);
+               plugins.notify(me, 'afterDatasetDraw', [args]);
+       },
 
-                       plugins.notify(me, 'afterDatasetDraw', [args]);
-               },
+       /**
+        * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
+        * hook, in which case, plugins will not be called on `afterTooltipDraw`.
+        * @private
+        */
+       _drawTooltip: function(easingValue) {
+               var me = this;
+               var tooltip = me.tooltip;
+               var args = {
+                       tooltip: tooltip,
+                       easingValue: easingValue
+               };
+
+               if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
+                       return;
+               }
 
-               /**
-                * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
-                * hook, in which case, plugins will not be called on `afterTooltipDraw`.
-                * @private
-                */
-               _drawTooltip: function(easingValue) {
-                       var me = this;
-                       var tooltip = me.tooltip;
-                       var args = {
-                               tooltip: tooltip,
-                               easingValue: easingValue
-                       };
+               tooltip.draw();
 
-                       if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
-                               return;
-                       }
+               plugins.notify(me, 'afterTooltipDraw', [args]);
+       },
 
-                       tooltip.draw();
+       // Get the single element that was clicked on
+       // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
+       getElementAtEvent: function(e) {
+               return Interaction.modes.single(this, e);
+       },
 
-                       plugins.notify(me, 'afterTooltipDraw', [args]);
-               },
+       getElementsAtEvent: function(e) {
+               return Interaction.modes.label(this, e, {intersect: true});
+       },
 
-               // Get the single element that was clicked on
-               // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
-               getElementAtEvent: function(e) {
-                       return Interaction.modes.single(this, e);
-               },
+       getElementsAtXAxis: function(e) {
+               return Interaction.modes['x-axis'](this, e, {intersect: true});
+       },
 
-               getElementsAtEvent: function(e) {
-                       return Interaction.modes.label(this, e, {intersect: true});
-               },
+       getElementsAtEventForMode: function(e, mode, options) {
+               var method = Interaction.modes[mode];
+               if (typeof method === 'function') {
+                       return method(this, e, options);
+               }
 
-               getElementsAtXAxis: function(e) {
-                       return Interaction.modes['x-axis'](this, e, {intersect: true});
-               },
+               return [];
+       },
 
-               getElementsAtEventForMode: function(e, mode, options) {
-                       var method = Interaction.modes[mode];
-                       if (typeof method === 'function') {
-                               return method(this, e, options);
-                       }
+       getDatasetAtEvent: function(e) {
+               return Interaction.modes.dataset(this, e, {intersect: true});
+       },
 
-                       return [];
-               },
+       getDatasetMeta: function(datasetIndex) {
+               var me = this;
+               var dataset = me.data.datasets[datasetIndex];
+               if (!dataset._meta) {
+                       dataset._meta = {};
+               }
 
-               getDatasetAtEvent: function(e) {
-                       return Interaction.modes.dataset(this, e, {intersect: true});
-               },
+               var meta = dataset._meta[me.id];
+               if (!meta) {
+                       meta = dataset._meta[me.id] = {
+                               type: null,
+                               data: [],
+                               dataset: null,
+                               controller: null,
+                               hidden: null,                   // See isDatasetVisible() comment
+                               xAxisID: null,
+                               yAxisID: null
+                       };
+               }
 
-               getDatasetMeta: function(datasetIndex) {
-                       var me = this;
-                       var dataset = me.data.datasets[datasetIndex];
-                       if (!dataset._meta) {
-                               dataset._meta = {};
-                       }
+               return meta;
+       },
 
-                       var meta = dataset._meta[me.id];
-                       if (!meta) {
-                               meta = dataset._meta[me.id] = {
-                                       type: null,
-                                       data: [],
-                                       dataset: null,
-                                       controller: null,
-                                       hidden: null,                   // See isDatasetVisible() comment
-                                       xAxisID: null,
-                                       yAxisID: null
-                               };
+       getVisibleDatasetCount: function() {
+               var count = 0;
+               for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
+                       if (this.isDatasetVisible(i)) {
+                               count++;
                        }
+               }
+               return count;
+       },
 
-                       return meta;
-               },
-
-               getVisibleDatasetCount: function() {
-                       var count = 0;
-                       for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
-                               if (this.isDatasetVisible(i)) {
-                                       count++;
-                               }
-                       }
-                       return count;
-               },
+       isDatasetVisible: function(datasetIndex) {
+               var meta = this.getDatasetMeta(datasetIndex);
 
-               isDatasetVisible: function(datasetIndex) {
-                       var meta = this.getDatasetMeta(datasetIndex);
+               // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
+               // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
+               return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
+       },
 
-                       // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
-                       // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
-                       return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
-               },
+       generateLegend: function() {
+               return this.options.legendCallback(this);
+       },
 
-               generateLegend: function() {
-                       return this.options.legendCallback(this);
-               },
+       /**
+        * @private
+        */
+       destroyDatasetMeta: function(datasetIndex) {
+               var id = this.id;
+               var dataset = this.data.datasets[datasetIndex];
+               var meta = dataset._meta && dataset._meta[id];
+
+               if (meta) {
+                       meta.controller.destroy();
+                       delete dataset._meta[id];
+               }
+       },
 
-               /**
-                * @private
-                */
-               destroyDatasetMeta: function(datasetIndex) {
-                       var id = this.id;
-                       var dataset = this.data.datasets[datasetIndex];
-                       var meta = dataset._meta && dataset._meta[id];
-
-                       if (meta) {
-                               meta.controller.destroy();
-                               delete dataset._meta[id];
-                       }
-               },
+       destroy: function() {
+               var me = this;
+               var canvas = me.canvas;
+               var i, ilen;
 
-               destroy: function() {
-                       var me = this;
-                       var canvas = me.canvas;
-                       var i, ilen;
+               me.stop();
 
-                       me.stop();
+               // dataset controllers need to cleanup associated data
+               for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
+                       me.destroyDatasetMeta(i);
+               }
 
-                       // dataset controllers need to cleanup associated data
-                       for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
-                               me.destroyDatasetMeta(i);
-                       }
+               if (canvas) {
+                       me.unbindEvents();
+                       helpers.canvas.clear(me);
+                       platform.releaseContext(me.ctx);
+                       me.canvas = null;
+                       me.ctx = null;
+               }
 
-                       if (canvas) {
-                               me.unbindEvents();
-                               helpers.canvas.clear(me);
-                               platform.releaseContext(me.ctx);
-                               me.canvas = null;
-                               me.ctx = null;
-                       }
+               plugins.notify(me, 'destroy');
 
-                       plugins.notify(me, 'destroy');
+               delete Chart.instances[me.id];
+       },
 
-                       delete Chart.instances[me.id];
-               },
+       toBase64Image: function() {
+               return this.canvas.toDataURL.apply(this.canvas, arguments);
+       },
 
-               toBase64Image: function() {
-                       return this.canvas.toDataURL.apply(this.canvas, arguments);
-               },
+       initToolTip: function() {
+               var me = this;
+               me.tooltip = new Tooltip({
+                       _chart: me,
+                       _chartInstance: me, // deprecated, backward compatibility
+                       _data: me.data,
+                       _options: me.options.tooltips
+               }, me);
+       },
 
-               initToolTip: function() {
-                       var me = this;
-                       me.tooltip = new Tooltip({
-                               _chart: me,
-                               _chartInstance: me, // deprecated, backward compatibility
-                               _data: me.data,
-                               _options: me.options.tooltips
-                       }, me);
-               },
+       /**
+        * @private
+        */
+       bindEvents: function() {
+               var me = this;
+               var listeners = me._listeners = {};
+               var listener = function() {
+                       me.eventHandler.apply(me, arguments);
+               };
+
+               helpers.each(me.options.events, function(type) {
+                       platform.addEventListener(me, type, listener);
+                       listeners[type] = listener;
+               });
 
-               /**
-                * @private
-                */
-               bindEvents: function() {
-                       var me = this;
-                       var listeners = me._listeners = {};
-                       var listener = function() {
-                               me.eventHandler.apply(me, arguments);
+               // Elements used to detect size change should not be injected for non responsive charts.
+               // See https://github.com/chartjs/Chart.js/issues/2210
+               if (me.options.responsive) {
+                       listener = function() {
+                               me.resize();
                        };
 
-                       helpers.each(me.options.events, function(type) {
-                               platform.addEventListener(me, type, listener);
-                               listeners[type] = listener;
-                       });
-
-                       // Elements used to detect size change should not be injected for non responsive charts.
-                       // See https://github.com/chartjs/Chart.js/issues/2210
-                       if (me.options.responsive) {
-                               listener = function() {
-                                       me.resize();
-                               };
-
-                               platform.addEventListener(me, 'resize', listener);
-                               listeners.resize = listener;
-                       }
-               },
+                       platform.addEventListener(me, 'resize', listener);
+                       listeners.resize = listener;
+               }
+       },
 
-               /**
-                * @private
-                */
-               unbindEvents: function() {
-                       var me = this;
-                       var listeners = me._listeners;
-                       if (!listeners) {
-                               return;
-                       }
+       /**
+        * @private
+        */
+       unbindEvents: function() {
+               var me = this;
+               var listeners = me._listeners;
+               if (!listeners) {
+                       return;
+               }
 
-                       delete me._listeners;
-                       helpers.each(listeners, function(listener, type) {
-                               platform.removeEventListener(me, type, listener);
-                       });
-               },
+               delete me._listeners;
+               helpers.each(listeners, function(listener, type) {
+                       platform.removeEventListener(me, type, listener);
+               });
+       },
 
-               updateHoverStyle: function(elements, mode, enabled) {
-                       var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
-                       var element, i, ilen;
+       updateHoverStyle: function(elements, mode, enabled) {
+               var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
+               var element, i, ilen;
 
-                       for (i = 0, ilen = elements.length; i < ilen; ++i) {
-                               element = elements[i];
-                               if (element) {
-                                       this.getDatasetMeta(element._datasetIndex).controller[method](element);
-                               }
+               for (i = 0, ilen = elements.length; i < ilen; ++i) {
+                       element = elements[i];
+                       if (element) {
+                               this.getDatasetMeta(element._datasetIndex).controller[method](element);
                        }
-               },
-
-               /**
-                * @private
-                */
-               eventHandler: function(e) {
-                       var me = this;
-                       var tooltip = me.tooltip;
+               }
+       },
 
-                       if (plugins.notify(me, 'beforeEvent', [e]) === false) {
-                               return;
-                       }
+       /**
+        * @private
+        */
+       eventHandler: function(e) {
+               var me = this;
+               var tooltip = me.tooltip;
 
-                       // Buffer any update calls so that renders do not occur
-                       me._bufferedRender = true;
-                       me._bufferedRequest = null;
-
-                       var changed = me.handleEvent(e);
-                       // for smooth tooltip animations issue #4989
-                       // the tooltip should be the source of change
-                       // Animation check workaround:
-                       // tooltip._start will be null when tooltip isn't animating
-                       if (tooltip) {
-                               changed = tooltip._start
-                                       ? tooltip.handleEvent(e)
-                                       : changed | tooltip.handleEvent(e);
-                       }
+               if (plugins.notify(me, 'beforeEvent', [e]) === false) {
+                       return;
+               }
 
-                       plugins.notify(me, 'afterEvent', [e]);
-
-                       var bufferedRequest = me._bufferedRequest;
-                       if (bufferedRequest) {
-                               // If we have an update that was triggered, we need to do a normal render
-                               me.render(bufferedRequest);
-                       } else if (changed && !me.animating) {
-                               // If entering, leaving, or changing elements, animate the change via pivot
-                               me.stop();
-
-                               // We only need to render at this point. Updating will cause scales to be
-                               // recomputed generating flicker & using more memory than necessary.
-                               me.render({
-                                       duration: me.options.hover.animationDuration,
-                                       lazy: true
-                               });
-                       }
+               // Buffer any update calls so that renders do not occur
+               me._bufferedRender = true;
+               me._bufferedRequest = null;
+
+               var changed = me.handleEvent(e);
+               // for smooth tooltip animations issue #4989
+               // the tooltip should be the source of change
+               // Animation check workaround:
+               // tooltip._start will be null when tooltip isn't animating
+               if (tooltip) {
+                       changed = tooltip._start
+                               ? tooltip.handleEvent(e)
+                               : changed | tooltip.handleEvent(e);
+               }
 
-                       me._bufferedRender = false;
-                       me._bufferedRequest = null;
+               plugins.notify(me, 'afterEvent', [e]);
 
-                       return me;
-               },
+               var bufferedRequest = me._bufferedRequest;
+               if (bufferedRequest) {
+                       // If we have an update that was triggered, we need to do a normal render
+                       me.render(bufferedRequest);
+               } else if (changed && !me.animating) {
+                       // If entering, leaving, or changing elements, animate the change via pivot
+                       me.stop();
 
-               /**
-                * Handle an event
-                * @private
-                * @param {IEvent} event the event to handle
-                * @return {Boolean} true if the chart needs to re-render
-                */
-               handleEvent: function(e) {
-                       var me = this;
-                       var options = me.options || {};
-                       var hoverOptions = options.hover;
-                       var changed = false;
+                       // We only need to render at this point. Updating will cause scales to be
+                       // recomputed generating flicker & using more memory than necessary.
+                       me.render({
+                               duration: me.options.hover.animationDuration,
+                               lazy: true
+                       });
+               }
 
-                       me.lastActive = me.lastActive || [];
+               me._bufferedRender = false;
+               me._bufferedRequest = null;
 
-                       // Find Active Elements for hover and tooltips
-                       if (e.type === 'mouseout') {
-                               me.active = [];
-                       } else {
-                               me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
-                       }
+               return me;
+       },
 
-                       // Invoke onHover hook
-                       // Need to call with native event here to not break backwards compatibility
-                       helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
+       /**
+        * Handle an event
+        * @private
+        * @param {IEvent} event the event to handle
+        * @return {Boolean} true if the chart needs to re-render
+        */
+       handleEvent: function(e) {
+               var me = this;
+               var options = me.options || {};
+               var hoverOptions = options.hover;
+               var changed = false;
+
+               me.lastActive = me.lastActive || [];
+
+               // Find Active Elements for hover and tooltips
+               if (e.type === 'mouseout') {
+                       me.active = [];
+               } else {
+                       me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
+               }
 
-                       if (e.type === 'mouseup' || e.type === 'click') {
-                               if (options.onClick) {
-                                       // Use e.native here for backwards compatibility
-                                       options.onClick.call(me, e.native, me.active);
-                               }
-                       }
+               // Invoke onHover hook
+               // Need to call with native event here to not break backwards compatibility
+               helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
 
-                       // Remove styling for last active (even if it may still be active)
-                       if (me.lastActive.length) {
-                               me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
+               if (e.type === 'mouseup' || e.type === 'click') {
+                       if (options.onClick) {
+                               // Use e.native here for backwards compatibility
+                               options.onClick.call(me, e.native, me.active);
                        }
+               }
 
-                       // Built in hover styling
-                       if (me.active.length && hoverOptions.mode) {
-                               me.updateHoverStyle(me.active, hoverOptions.mode, true);
-                       }
+               // Remove styling for last active (even if it may still be active)
+               if (me.lastActive.length) {
+                       me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
+               }
 
-                       changed = !helpers.arrayEquals(me.active, me.lastActive);
+               // Built in hover styling
+               if (me.active.length && hoverOptions.mode) {
+                       me.updateHoverStyle(me.active, hoverOptions.mode, true);
+               }
 
-                       // Remember Last Actives
-                       me.lastActive = me.active;
+               changed = !helpers.arrayEquals(me.active, me.lastActive);
 
-                       return changed;
-               }
-       });
+               // Remember Last Actives
+               me.lastActive = me.active;
 
-       /**
-        * Provided for backward compatibility, use Chart instead.
-        * @class Chart.Controller
-        * @deprecated since version 2.6.0
-        * @todo remove at version 3
-        * @private
-        */
-       Chart.Controller = Chart;
-};
+               return changed;
+       }
+});
+
+/**
+ * NOTE(SB) We actually don't use this container anymore but we need to keep it
+ * for backward compatibility. Though, it can still be useful for plugins that
+ * would need to work on multiple charts?!
+ */
+Chart.instances = {};
+
+module.exports = Chart;
+
+// DEPRECATIONS
+
+/**
+ * Provided for backward compatibility, use Chart instead.
+ * @class Chart.Controller
+ * @deprecated since version 2.6
+ * @todo remove at version 3
+ * @private
+ */
+Chart.Controller = Chart;
+
+/**
+ * Provided for backward compatibility, not available anymore.
+ * @namespace Chart
+ * @deprecated since version 2.8
+ * @todo remove at version 3
+ * @private
+ */
+Chart.types = {};
index 4a2bb8e1b40cdb9665036493496bda205f7ce6f7..279491debaedc0b70e2dc8e3727b50c6a4cdba62 100644 (file)
@@ -2,7 +2,7 @@
 
 var helpers = require('../helpers/helpers.core');
 
-module.exports = {
+var defaults = {
        /**
         * @private
         */
@@ -10,3 +10,15 @@ module.exports = {
                return helpers.merge(this[scope] || (this[scope] = {}), values);
        }
 };
+
+defaults._set('global', {
+       defaultColor: 'rgba(0,0,0,0.1)',
+       defaultFontColor: '#666',
+       defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
+       defaultFontSize: 12,
+       defaultFontStyle: 'normal',
+       defaultLineHeight: 1.2,
+       showLines: true
+});
+
+module.exports = defaults;
diff --git a/src/core/core.js b/src/core/core.js
deleted file mode 100644 (file)
index 8860b9b..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-'use strict';
-
-var defaults = require('./core.defaults');
-
-defaults._set('global', {
-       responsive: true,
-       responsiveAnimationDuration: 0,
-       maintainAspectRatio: true,
-       events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
-       hover: {
-               onHover: null,
-               mode: 'nearest',
-               intersect: true,
-               animationDuration: 400
-       },
-       onClick: null,
-       defaultColor: 'rgba(0,0,0,0.1)',
-       defaultFontColor: '#666',
-       defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
-       defaultFontSize: 12,
-       defaultFontStyle: 'normal',
-       defaultLineHeight: 1.2,
-       showLines: true,
-
-       // Element defaults defined in element extensions
-       elements: {},
-
-       // Layout options such as padding
-       layout: {
-               padding: {
-                       top: 0,
-                       right: 0,
-                       bottom: 0,
-                       left: 0
-               }
-       }
-});
-
-module.exports = function() {
-
-       // Occupy the global variable of Chart, and create a simple base class
-       var Chart = function(item, config) {
-               this.construct(item, config);
-               return this;
-       };
-
-       Chart.Chart = Chart;
-
-       return Chart;
-};
index b99612bbebd9aaa911a6510032745cf7f0aa64f2..d212731f7efd7c506392a329aacfe419e6b006c0 100644 (file)
@@ -1,5 +1,6 @@
 'use strict';
 
+var defaults = require('./core.defaults');
 var helpers = require('../helpers/index');
 
 function filterByPosition(array, position) {
@@ -25,6 +26,17 @@ function sortByWeight(array, reverse) {
        });
 }
 
+defaults._set('global', {
+       layout: {
+               padding: {
+                       top: 0,
+                       right: 0,
+                       bottom: 0,
+                       left: 0
+               }
+       }
+});
+
 /**
  * @interface ILayoutItem
  * @prop {String} position - The position of the item in the chart layout. Possible values are
index 822b234b4173056ced42e08815b2d34cf033d815..d358742a5941a69f942018ca8b0a50c227b43bb2 100644 (file)
@@ -14,7 +14,6 @@ describe('Deprecations', function() {
 
                        describe('Chart.' + klass, function() {
                                it('should be defined as a function', function() {
-                                       expect(Chart[klass]).toBeDefined();
                                        expect(typeof Chart[klass]).toBe('function');
                                });
                                it('should create a chart of type "' + type + '"', function() {
@@ -26,19 +25,23 @@ describe('Deprecations', function() {
                });
 
                describe('Chart.helpers.aliasPixel', function() {
-                       it('should be defined and a function', function() {
-                               expect(Chart.helpers.aliasPixel).toBeDefined();
+                       it('should be defined as a function', function() {
                                expect(typeof Chart.helpers.aliasPixel).toBe('function');
                        });
                });
 
                describe('Chart.LinearScaleBase', function() {
                        it('should be defined and inherit from Chart.Scale', function() {
-                               expect(Chart.LinearScaleBase).toBeDefined();
                                expect(typeof Chart.LinearScaleBase).toBe('function');
                                expect(Chart.LinearScaleBase.prototype instanceof Chart.Scale).toBeTruthy();
                        });
                });
+
+               describe('Chart.types', function() {
+                       it('should be defined as an empty object', function() {
+                               expect(Chart.types).toEqual({});
+                       });
+               });
        });
 
        describe('Version 2.7.3', function() {
@@ -103,7 +106,6 @@ describe('Deprecations', function() {
 
                describe('Chart.helpers.indexOf', function() {
                        it('should be defined and a function', function() {
-                               expect(Chart.helpers.indexOf).toBeDefined();
                                expect(typeof Chart.helpers.indexOf).toBe('function');
                        });
                        it('should returns the correct index', function() {
@@ -144,7 +146,6 @@ describe('Deprecations', function() {
 
                describe('Chart.helpers.drawRoundedRectangle', function() {
                        it('should be defined and a function', function() {
-                               expect(Chart.helpers.drawRoundedRectangle).toBeDefined();
                                expect(typeof Chart.helpers.drawRoundedRectangle).toBe('function');
                        });
                        it('should call Chart.helpers.canvas.roundedRect', function() {
@@ -161,7 +162,6 @@ describe('Deprecations', function() {
 
                describe('Chart.helpers.addEvent', function() {
                        it('should be defined and a function', function() {
-                               expect(Chart.helpers.addEvent).toBeDefined();
                                expect(typeof Chart.helpers.addEvent).toBe('function');
                        });
                        it('should correctly add event listener', function() {
@@ -174,7 +174,6 @@ describe('Deprecations', function() {
 
                describe('Chart.helpers.removeEvent', function() {
                        it('should be defined and a function', function() {
-                               expect(Chart.helpers.removeEvent).toBeDefined();
                                expect(typeof Chart.helpers.removeEvent).toBe('function');
                        });
                        it('should correctly remove event listener', function() {