]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Rewrite animation logic (#6845)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Fri, 27 Dec 2019 22:13:24 +0000 (00:13 +0200)
committerEvert Timberg <evert.timberg+github@gmail.com>
Fri, 27 Dec 2019 22:13:24 +0000 (17:13 -0500)
* Rewrite animation logic

* Review update 1

* Review update 2

* Review update 3

* Add 'none' to api.md

59 files changed:
.eslintrc.yml
docs/configuration/animations.md
docs/configuration/tooltip.md
docs/developers/api.md
docs/developers/charts.md
docs/developers/updates.md
docs/general/interactions/README.md
docs/general/performance.md
docs/general/responsive.md
docs/getting-started/v3-migration.md
samples/animations/delay.html [new file with mode: 0644]
samples/animations/drop.html [new file with mode: 0644]
samples/animations/loop.html [new file with mode: 0644]
samples/charts/bubble.html
samples/samples.js
src/controllers/controller.bar.js
src/controllers/controller.bubble.js
src/controllers/controller.doughnut.js
src/controllers/controller.line.js
src/controllers/controller.polarArea.js
src/controllers/controller.radar.js
src/core/core.animation.js
src/core/core.animations.js
src/core/core.animator.js [new file with mode: 0644]
src/core/core.controller.js
src/core/core.datasetController.js
src/core/core.element.js
src/core/core.interaction.js
src/core/core.plugins.js
src/core/core.tooltip.js
src/elements/element.arc.js
src/elements/element.line.js
src/elements/element.point.js
src/elements/element.rectangle.js
src/helpers/helpers.curve.js
src/index.js
src/plugins/plugin.filler.js
src/plugins/plugin.legend.js
test/fixtures/controller.line/clip/default-y-max.png
test/fixtures/controller.line/clip/default-y.png
test/fixtures/core.tooltip/opacity.js
test/specs/controller.bar.tests.js
test/specs/controller.bubble.tests.js
test/specs/controller.doughnut.tests.js
test/specs/controller.line.tests.js
test/specs/controller.polarArea.tests.js
test/specs/controller.radar.tests.js
test/specs/controller.scatter.tests.js [moved from test/specs/controller.scatter.test.js with 92% similarity]
test/specs/core.controller.tests.js
test/specs/core.element.tests.js [deleted file]
test/specs/core.interaction.tests.js
test/specs/core.tooltip.tests.js
test/specs/element.arc.tests.js
test/specs/element.point.tests.js
test/specs/element.rectangle.tests.js
test/specs/global.defaults.tests.js
test/specs/helpers.curve.tests.js
test/specs/plugin.legend.tests.js
test/utils.js

index ace7ae7a470af7bcdf61a3808e670fca5b21bf0e..2a65bd04291e761c1d420739d02cd4d576bafdd1 100644 (file)
@@ -1,6 +1,7 @@
 extends: chartjs
 
 env:
+  es6: true
   browser: true
   node: true
 
index 40dd8ec3ff7f20fe2c999b0648a062172f9c1ef6..8131941d7e75c1e7a0bdb6ebbf863aae4478045f 100644 (file)
@@ -10,12 +10,33 @@ The following animation options are available. The global options for are define
 | ---- | ---- | ------- | -----------
 | `duration` | `number` | `1000` | The number of milliseconds an animation takes.
 | `easing` | `string` | `'easeOutQuart'` | Easing function to use. [more...](#easing)
+| `debug` | `boolean` | `undefined` | Running animation count + FPS display in upper left corner of the chart.
 | `onProgress` | `function` | `null` | Callback called on each step of an animation. [more...](#animation-callbacks)
-| `onComplete` | `function` | `null` | Callback called at the end of an animation. [more...](#animation-callbacks)
+| `onComplete` | `function` | `null` | Callback called when all animations are completed. [more...](#animation-callbacks)
+| `delay` | `number` | `undefined` | Delay before starting the animations.
+| `loop` | `boolean` | `undefined` | If set to `true`, loop the animations loop endlessly.
+| `type` | `string` | `typeof property` | Type of property, determines the interpolator used. Possible values: `'number'`, '`color`'.
+| `from`  | <code>number&#124;Color</code> | `undefined` | Start value for the animation. Current value is used when `undefined`
+| `active` | `object` | `{ duration: 400 }` | Option overrides for `active` animations (hover)
+| `resize` | `object` | `{ duration: 0 }` | Option overrides for `resize` animations.
+| [property] | `object` | `undefined` | Option overrides for [property].
+| [collection] | `object` | `undefined` | Option overrides for multiple properties, identified by `properties` array.
+
+Default collections:
+| Name | option | value
+| `numbers` | `type` | `'number'`
+| | `properties` | `['x', 'y', 'borderWidth', 'radius', 'tension']`
+| `colors` | `type` | `'color'`
+| | `properties` | `['borderColor', 'backgroundColor']`
+
+Direct property configuration overrides configuration of same property in a collection.
+
+These defaults can be overridden in `options.animation` and `dataset.animation`.
 
 ## Easing
 
 Available options are:
+
 * `'linear'`
 * `'easeInQuad'`
 * `'easeOutQuad'`
@@ -52,34 +73,23 @@ See [Robert Penner's easing equations](http://robertpenner.com/easing/).
 
 ## Animation Callbacks
 
-The `onProgress` and `onComplete` callbacks are useful for synchronizing an external draw to the chart animation. The callback is passed a `Chart.Animation` instance:
+The `onProgress` and `onComplete` callbacks are useful for synchronizing an external draw to the chart animation. The callback is passed following object:
 
 ```javascript
 {
     // Chart object
     chart: Chart,
 
-    // Current Animation frame number
+    // Number of animations still in progress
     currentStep: number,
 
-    // Number of animation frames
+    // Total number of animations at the start of current animation
     numSteps: number,
-
-    // Animation easing to use
-    easing: string,
-
-    // Function that renders the chart
-    render: function,
-
-    // User callback
-    onAnimationProgress: function,
-
-    // User callback
-    onAnimationComplete: function
 }
 ```
 
 The following example fills a progress bar during the chart animation.
+
 ```javascript
 var chart = new Chart(ctx, {
     type: 'line',
index 26b1d6a7103de7e6eedecb1350f4589e29761dc3..6e086a1671682037e1617abfd85dc43b7692511f 100644 (file)
@@ -356,7 +356,7 @@ The tooltip model contains parameters that can be used to render the tooltip.
 
     // 0 opacity is a hidden tooltip
     opacity: number,
-    legendColorBackground: Color,
+    multiKeyBackground: Color,
     displayColors: boolean,
     borderColor: Color,
     borderWidth: number
index e248db3a5722ec3a22f098cb4b5d3ff76a7a844d..01b10801c34157fcbb6990901a375e778a59e92f 100644 (file)
@@ -17,32 +17,23 @@ This must be called before the canvas is reused for a new chart.
 myLineChart.destroy();
 ```
 
-## .update(config)
+## .update(mode)
 
 Triggers an update of the chart. This can be safely called after updating the data object. This will update all scales, legends, and then re-render the chart.
 
 ```javascript
-// duration is the time for the animation of the redraw in milliseconds
-// lazy is a boolean. if true, the animation can be interrupted by other animations
 myLineChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
 myLineChart.update(); // Calling update now animates the position of March from 90 to 50.
 ```
 
 > **Note:** replacing the data reference (e.g. `myLineChart.data = {datasets: [...]}` only works starting **version 2.6**. Prior that, replacing the entire data object could be achieved with the following workaround: `myLineChart.config.data = {datasets: [...]}`.
 
-A `config` object can be provided with additional configuration for the update process. This is useful when `update` is manually called inside an event handler and some different animation is desired.
-
-The following properties are supported:
-* **duration** (number): Time for the animation of the redraw in milliseconds
-* **lazy** (boolean): If true, the animation can be interrupted by other animations
-* **easing** (string): The animation easing function. See [Animation Easing](../configuration/animations.md) for possible values.
+A `mode` string can be provided to indicate what should be updated and what animation configuration should be used. Core calls this method using any of `undefined`, `'reset'`, `'resize'` or `'active'`. `'none'` is also a supported mode for skipping animations for single update.
 
 Example:
+
 ```javascript
-myChart.update({
-    duration: 800,
-    easing: 'easeOutBounce'
-});
+myChart.update();
 ```
 
 See [Updating Charts](updates.md) for more details.
@@ -55,25 +46,13 @@ Reset the chart to it's state before the initial animation. A new animation can
 myLineChart.reset();
 ```
 
-## .render(config)
+## .render()
 
 Triggers a redraw of all chart elements. Note, this does not update elements for new data. Use `.update()` in that case.
 
-See `.update(config)` for more details on the config object.
-
-```javascript
-// duration is the time for the animation of the redraw in milliseconds
-// lazy is a boolean. if true, the animation can be interrupted by other animations
-myLineChart.render({
-    duration: 800,
-    lazy: false,
-    easing: 'easeOutBounce'
-});
-```
-
 ## .stop()
 
-Use this to stop any current animation loop. This will pause the chart during any current animation frame. Call `.render()` to re-animate.
+Use this to stop any current animation. This will pause the chart during any current animation frame. Call `.render()` to re-animate.
 
 ```javascript
 // Stops the charts animation loop at its current frame
@@ -175,5 +154,5 @@ Extensive examples of usage are available in the [Chart.js tests](https://github
 
 ```javascript
 var meta = myChart.getDatasetMeta(0);
-var x = meta.data[0]._model.x;
+var x = meta.data[0].x;
 ```
index 2e2e5c231c9d4d368f34ca71b65192fd2bbbf7f7..c96350373dbe413d10d6ec520036ce210885be5a 100644 (file)
@@ -94,13 +94,13 @@ var custom = Chart.controllers.bubble.extend({
         // Now we can do some custom drawing for this dataset. Here we'll draw a red box around the first point in each dataset
         var meta = this.getMeta();
         var pt0 = meta.data[0];
-        var radius = pt0._view.radius;
+        var radius = pt0.radius;
 
         var ctx = this.chart.chart.ctx;
         ctx.save();
         ctx.strokeStyle = 'red';
         ctx.lineWidth = 1;
-        ctx.strokeRect(pt0._view.x - radius, pt0._view.y - radius, 2 * radius, 2 * radius);
+        ctx.strokeRect(pt0.x - radius, pt0.y - radius, 2 * radius, 2 * radius);
         ctx.restore();
     }
 });
index e50b26004a5e252379113ea9b9fe0d99127de3aa..8b7da7b0ed6676f709a72659d8884faa3b806931 100644 (file)
@@ -97,4 +97,4 @@ Code sample for updating options can be found in [toggle-scale-type.html](../../
 
 ## Preventing Animations
 
-Sometimes when a chart updates, you may not want an animation. To achieve this you can call `update` with a duration of `0`. This will render the chart synchronously and without an animation.
+Sometimes when a chart updates, you may not want an animation. To achieve this you can call `update` with `'none'` as mode.
index 9b5ad6d3271a54161250530ac8345e407e49fdee..be5ec8e239d4e6748dbff68bbe377e76a8740a77 100644 (file)
@@ -7,4 +7,3 @@ The hover configuration is passed into the `options.hover` namespace. The global
 | `mode` | `string` | `'nearest'` | Sets which elements appear in the tooltip. See [Interaction Modes](./modes.md#interaction-modes) for details.
 | `intersect` | `boolean` | `true` | if true, the hover mode only applies when the mouse position intersects an item on the chart.
 | `axis` | `string` | `'x'` | Can be set to `'x'`, `'y'`, or `'xy'` to define which directions are used in calculating distances. Defaults to `'x'` for `'index'` mode and `'xy'` in `dataset` and `'nearest'` modes.
-| `animationDuration` | `number` | `400` | Duration in milliseconds it takes to animate hover style changes.
index 8c0dc11c78e0df3cef6530e8551c82a340834224..1d970a6f9bbb0ff864246bb5980daa28ceb423f6 100644 (file)
@@ -23,13 +23,7 @@ new Chart(ctx, {
     type: 'line',
     data: data,
     options: {
-        animation: {
-            duration: 0 // general animation time
-        },
-        hover: {
-            animationDuration: 0 // duration of animations when hovering an item
-        },
-        responsiveAnimationDuration: 0 // animation duration after a resize
+        animation: false
     }
 });
 ```
index 319709a5a16e3a63c7210a59f5faea7a1dbe8a63..81569476bb0f62dd4c302b3f7198683da4dc0839 100644 (file)
@@ -14,7 +14,6 @@ Chart.js provides a [few options](#configuration-options) to enable responsivene
 | Name | Type | Default | Description
 | ---- | ---- | ------- | -----------
 | `responsive` | `boolean` | `true` | Resizes the chart canvas when its container does ([important note...](#important-note)).
-| `responsiveAnimationDuration` | `number` | `0` | Duration in milliseconds it takes to animate to new size after a resize event.
 | `maintainAspectRatio` | `boolean` | `true` | Maintain the original canvas aspect ratio `(width / height)` when resizing.
 | `aspectRatio` | `number` | `2` | Canvas aspect ratio (i.e. `width / height`, a value of 1 representing a square canvas). Note that this option is ignored if the height is explicitly defined either as attribute or via the style.
 | `onResize` | `function` | `null` | Called when a resize occurs. Gets passed two arguments: the chart instance and the new size.
index b55ad538150eef26cf5056c34fd7e888e67a7f66..ee719fe989c5060e7a225ba830c81574c1765054 100644 (file)
@@ -49,6 +49,13 @@ Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released
 * `scales.[x/y]Axes.time.max` was renamed to `scales[id].max`
 * `scales.[x/y]Axes.time.min` was renamed to `scales[id].min`
 
+### Animations
+
+Animation system was completely rewritten in Chart.js v3. Each property can now be animated separately. Please see [animations](../configuration/animations.md) docs for details.
+
+* `hover.animationDuration` is now configured in `animation.active.duration`
+* `responsiveAnimationDuration` is now configured in `animation.resize.duration`
+
 ## Developer migration
 
 ### Removed
@@ -90,10 +97,8 @@ Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released
 
 * `Chart.data.datasets[datasetIndex]._meta`
 * `Element._ctx`
-* `Element._model.datasetLabel`
-* `Element._model.label`
-* `Point._model.tension`
-* `Point._model.steppedLine`
+* `Element._model`
+* `Element._view`
 * `TimeScale._getPixelForOffset`
 * `TimeScale.getLabelWidth`
 
@@ -108,7 +113,6 @@ Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released
 * `helpers.log10` was renamed to `helpers.math.log10`
 * `helpers.almostEquals` was renamed to `helpers.math.almostEquals`
 * `helpers.almostWhole` was renamed to `helpers.math.almostWhole`
-* `helpers._decimalPlaces` was renamed to `helpers.math._decimalPlaces`
 * `helpers.distanceBetweenPoints` was renamed to `helpers.math.distanceBetweenPoints`
 * `helpers.isNumber` was renamed to `helpers.math.isNumber`
 * `helpers.sign` was renamed to `helpers.math.sign`
@@ -129,10 +133,12 @@ Chart.js 3.0 introduces a number of breaking changes. Chart.js 2.0 was released
 * `TimeScale.getLabelCapacity` was renamed to `TimeScale._getLabelCapacity`
 * `TimeScale.tickFormatFunction` was renamed to `TimeScale._tickFormatFunction`
 * `TimeScale.getPixelForOffset` was renamed to `TimeScale._getPixelForOffset`
+* `Tooltip.options.legendColorBackgroupd` was renamed to `Tooltip.options.multiKeyBackground`
 
 #### Renamed private APIs
 
 * `helpers._alignPixel` was renamed to `helpers.canvas._alignPixel`
+* `helpers._decimalPlaces` was renamed to `helpers.math._decimalPlaces`
 
 ### Changed
 
diff --git a/samples/animations/delay.html b/samples/animations/delay.html
new file mode 100644 (file)
index 0000000..38f24c8
--- /dev/null
@@ -0,0 +1,122 @@
+<!doctype html>
+<html>
+
+<head>
+       <title>Stacked Bar Chart</title>
+       <script src="../../dist/Chart.min.js"></script>
+       <script src="../utils.js"></script>
+       <style>
+       canvas {
+               -moz-user-select: none;
+               -webkit-user-select: none;
+               -ms-user-select: none;
+       }
+       </style>
+</head>
+
+<body>
+       <div style="width: 75%">
+               <canvas id="canvas"></canvas>
+       </div>
+       <button id="randomizeData">Randomize Data</button>
+       <script>
+               var barChartData = {
+                       labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
+                       datasets: [{
+                               label: 'Dataset 1',
+                               backgroundColor: window.chartColors.red,
+                               data: [
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor()
+                               ]
+                       }, {
+                               label: 'Dataset 2',
+                               backgroundColor: window.chartColors.blue,
+                               data: [
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor()
+                               ]
+                       }, {
+                               label: 'Dataset 3',
+                               backgroundColor: window.chartColors.green,
+                               data: [
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor(),
+                                       randomScalingFactor()
+                               ]
+                       }]
+
+               };
+               window.onload = function() {
+                       var ctx = document.getElementById('canvas').getContext('2d');
+                       var started = {};
+                       window.myBar = new Chart(ctx, {
+                               type: 'bar',
+                               data: barChartData,
+                               options: {
+                                       animation: (context) => {
+                                               if (context.active) {
+                                                       return {
+                                                               duration: 400
+                                                       };
+                                               }
+                                               var delay = 0;
+                                               var dsIndex = context.datasetIndex;
+                                               var index = context.dataIndex;
+                                               if (!started[index + dsIndex * 1000]) {
+                                                       delay = index * 300 + dsIndex * 100;
+                                                       started[index + dsIndex * 1000] = true;
+                                               }
+                                               return {
+                                                       easing: 'linear',
+                                                       duration: 600,
+                                                       delay
+                                               };
+                                       },
+                                       title: {
+                                               display: true,
+                                               text: 'Chart.js Bar Chart - Stacked'
+                                       },
+                                       tooltips: {
+                                               mode: 'index',
+                                               intersect: false
+                                       },
+                                       responsive: true,
+                                       scales: {
+                                               x: {
+                                                       stacked: true,
+                                               },
+                                               y: {
+                                                       stacked: true
+                                               }
+                                       }
+                               }
+                       });
+               };
+
+               document.getElementById('randomizeData').addEventListener('click', function() {
+                       barChartData.datasets.forEach(function(dataset) {
+                               dataset.data = dataset.data.map(function() {
+                                       return randomScalingFactor();
+                               });
+                       });
+                       window.myBar.update();
+               });
+       </script>
+</body>
+
+</html>
diff --git a/samples/animations/drop.html b/samples/animations/drop.html
new file mode 100644 (file)
index 0000000..c4db7ae
--- /dev/null
@@ -0,0 +1,175 @@
+<!doctype html>
+<html>
+
+<head>
+       <title>Line Chart</title>
+       <script src="../../dist/Chart.min.js"></script>
+       <script src="../utils.js"></script>
+       <style>
+       canvas{
+               -moz-user-select: none;
+               -webkit-user-select: none;
+               -ms-user-select: none;
+       }
+       </style>
+</head>
+
+<body>
+       <div style="width:75%;">
+               <canvas id="canvas"></canvas>
+       </div>
+       <br>
+       <br>
+       <button id="randomizeData">Randomize Data</button>
+       <button id="addDataset">Add Dataset</button>
+       <button id="removeDataset">Remove Dataset</button>
+       <button id="addData">Add Data</button>
+       <button id="removeData">Remove Data</button>
+       <script>
+               var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+               var config = {
+                       type: 'line',
+                       data: {
+                               labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
+                               datasets: [{
+                                       label: 'My First dataset',
+                                       animation: {
+                                               y: {
+                                                       duration: 2000,
+                                                       delay: 100
+                                               }
+                                       },
+                                       backgroundColor: window.chartColors.red,
+                                       borderColor: window.chartColors.red,
+                                       data: [
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor()
+                                       ],
+                                       fill: false,
+                               }, {
+                                       label: 'My Second dataset',
+                                       fill: false,
+                                       backgroundColor: window.chartColors.blue,
+                                       borderColor: window.chartColors.blue,
+                                       data: [
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor()
+                                       ],
+                               }]
+                       },
+                       options: {
+                               animation: {
+                                       y: {
+                                               easing: 'easeInOutElastic',
+                                               from: 0
+                                       }
+                               },
+                               responsive: true,
+                               title: {
+                                       display: true,
+                                       text: 'Chart.js Line Chart'
+                               },
+                               tooltips: {
+                                       mode: 'index',
+                                       intersect: false,
+                               },
+                               hover: {
+                                       mode: 'nearest',
+                                       intersect: true
+                               },
+                               scales: {
+                                       x: {
+                                               display: true,
+                                               scaleLabel: {
+                                                       display: true,
+                                                       labelString: 'Month'
+                                               }
+                                       },
+                                       y: {
+                                               display: true,
+                                               scaleLabel: {
+                                                       display: true,
+                                                       labelString: 'Value'
+                                               }
+                                       }
+                               }
+                       }
+               };
+
+               window.onload = function() {
+                       var ctx = document.getElementById('canvas').getContext('2d');
+                       window.myLine = new Chart(ctx, config);
+               };
+
+               document.getElementById('randomizeData').addEventListener('click', function() {
+                       config.data.datasets.forEach(function(dataset) {
+                               dataset.data = dataset.data.map(function() {
+                                       return randomScalingFactor();
+                               });
+
+                       });
+
+                       window.myLine.update();
+               });
+
+               var colorNames = Object.keys(window.chartColors);
+               document.getElementById('addDataset').addEventListener('click', function() {
+                       var colorName = colorNames[config.data.datasets.length % colorNames.length];
+                       var newColor = window.chartColors[colorName];
+                       var newDataset = {
+                               label: 'Dataset ' + config.data.datasets.length,
+                               backgroundColor: newColor,
+                               borderColor: newColor,
+                               data: [],
+                               fill: false
+                       };
+
+                       for (var index = 0; index < config.data.labels.length; ++index) {
+                               newDataset.data.push(randomScalingFactor());
+                       }
+
+                       config.data.datasets.push(newDataset);
+                       window.myLine.update();
+               });
+
+               document.getElementById('addData').addEventListener('click', function() {
+                       if (config.data.datasets.length > 0) {
+                               var month = MONTHS[config.data.labels.length % MONTHS.length];
+                               config.data.labels.push(month);
+
+                               config.data.datasets.forEach(function(dataset) {
+                                       dataset.data.push(randomScalingFactor());
+                               });
+
+                               window.myLine.update();
+                       }
+               });
+
+               document.getElementById('removeDataset').addEventListener('click', function() {
+                       config.data.datasets.splice(0, 1);
+                       window.myLine.update();
+               });
+
+               document.getElementById('removeData').addEventListener('click', function() {
+                       config.data.labels.splice(-1, 1); // remove the label first
+
+                       config.data.datasets.forEach(function(dataset) {
+                               dataset.data.pop();
+                       });
+
+                       window.myLine.update();
+               });
+       </script>
+</body>
+
+</html>
diff --git a/samples/animations/loop.html b/samples/animations/loop.html
new file mode 100644 (file)
index 0000000..15d743a
--- /dev/null
@@ -0,0 +1,181 @@
+<!doctype html>
+<html>
+
+<head>
+       <title>Line Chart</title>
+       <script src="../../dist/Chart.min.js"></script>
+       <script src="../utils.js"></script>
+       <style>
+       canvas{
+               -moz-user-select: none;
+               -webkit-user-select: none;
+               -ms-user-select: none;
+       }
+       </style>
+</head>
+
+<body>
+       <div style="width:75%;">
+               <canvas id="canvas"></canvas>
+       </div>
+       <br>
+       <br>
+       <button id="randomizeData">Randomize Data</button>
+       <button id="addDataset">Add Dataset</button>
+       <button id="removeDataset">Remove Dataset</button>
+       <button id="addData">Add Data</button>
+       <button id="removeData">Remove Data</button>
+       <script>
+               var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+               var config = {
+                       type: 'line',
+                       data: {
+                               labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
+                               datasets: [{
+                                       label: 'My First dataset',
+                                       fill: false,
+                                       backgroundColor: window.chartColors.red,
+                                       borderColor: window.chartColors.red,
+                                       data: [
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor()
+                                       ],
+                               }, {
+                                       label: 'My Second dataset',
+                                       fill: false,
+                                       backgroundColor: window.chartColors.blue,
+                                       borderColor: window.chartColors.blue,
+                                       data: [
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor(),
+                                               randomScalingFactor()
+                                       ],
+                               }]
+                       },
+                       options: {
+                               animation: (context) => {
+                                       if (context.active) {
+                                               return {
+                                                       radius: {
+                                                               duration: 400,
+                                                               loop: true
+                                                       }
+                                               };
+                                       }
+                                       return Chart.defaults.global.animation;
+                               },
+                               elements: {
+                                       point: {
+                                               hoverRadius: 6
+                                       }
+                               },
+                               responsive: true,
+                               title: {
+                                       display: true,
+                                       text: 'Chart.js Line Chart'
+                               },
+                               tooltips: {
+                                       mode: 'nearest',
+                                       axis: 'x',
+                                       intersect: false,
+                               },
+                               hover: {
+                                       mode: 'nearest',
+                                       axis: 'x',
+                                       intersect: false
+                               },
+                               scales: {
+                                       x: {
+                                               display: true,
+                                               scaleLabel: {
+                                                       display: true,
+                                                       labelString: 'Month'
+                                               }
+                                       },
+                                       y: {
+                                               display: true,
+                                               scaleLabel: {
+                                                       display: true,
+                                                       labelString: 'Value'
+                                               }
+                                       }
+                               }
+                       }
+               };
+
+               window.onload = function() {
+                       var ctx = document.getElementById('canvas').getContext('2d');
+                       window.myLine = new Chart(ctx, config);
+               };
+
+               document.getElementById('randomizeData').addEventListener('click', function() {
+                       config.data.datasets.forEach(function(dataset) {
+                               dataset.data = dataset.data.map(function() {
+                                       return randomScalingFactor();
+                               });
+
+                       });
+
+                       window.myLine.update();
+               });
+
+               var colorNames = Object.keys(window.chartColors);
+               document.getElementById('addDataset').addEventListener('click', function() {
+                       var colorName = colorNames[config.data.datasets.length % colorNames.length];
+                       var newColor = window.chartColors[colorName];
+                       var newDataset = {
+                               label: 'Dataset ' + config.data.datasets.length,
+                               backgroundColor: newColor,
+                               borderColor: newColor,
+                               data: [],
+                               fill: false
+                       };
+
+                       for (var index = 0; index < config.data.labels.length; ++index) {
+                               newDataset.data.push(randomScalingFactor());
+                       }
+
+                       config.data.datasets.push(newDataset);
+                       window.myLine.update();
+               });
+
+               document.getElementById('addData').addEventListener('click', function() {
+                       if (config.data.datasets.length > 0) {
+                               var month = MONTHS[config.data.labels.length % MONTHS.length];
+                               config.data.labels.push(month);
+
+                               config.data.datasets.forEach(function(dataset) {
+                                       dataset.data.push(randomScalingFactor());
+                               });
+
+                               window.myLine.update();
+                       }
+               });
+
+               document.getElementById('removeDataset').addEventListener('click', function() {
+                       config.data.datasets.splice(0, 1);
+                       window.myLine.update();
+               });
+
+               document.getElementById('removeData').addEventListener('click', function() {
+                       config.data.labels.splice(-1, 1); // remove the label first
+
+                       config.data.datasets.forEach(function(dataset) {
+                               dataset.data.pop();
+                       });
+
+                       window.myLine.update();
+               });
+       </script>
+</body>
+
+</html>
index b604a5ac12e8937fdb9407cff89a7b2fe82f878d..19cae60bb7c2457bebe828439f32f61da52899b9 100644 (file)
@@ -29,9 +29,6 @@
                var addedCount = 0;
                var color = Chart.helpers.color;
                var bubbleChartData = {
-                       animation: {
-                               duration: 10000
-                       },
                        datasets: [{
                                label: 'My First dataset',
                                backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
index d1cae567280590c076636620d909093f6dea0d6a..d95a3fe7e4fb1db39da6dd69e2d69a6e91c63e5d 100644 (file)
@@ -65,6 +65,9 @@
        }, {
                title: 'Other charts',
                items: [{
+                       title: 'Bubble',
+                       path: 'charts/bubble.html'
+               }, {
                        title: 'Scatter',
                        path: 'charts/scatter/basic.html'
                }, {
                        title: 'Radar Chart',
                        path: 'scriptable/radar.html'
                }]
+       }, {
+               title: 'Animations',
+               items: [{
+                       title: 'Delay',
+                       path: 'animations/delay.html'
+               }, {
+                       title: 'Drop',
+                       path: 'animations/drop.html'
+               }, {
+                       title: 'Loop',
+                       path: 'animations/loop.html'
+               }]
        }, {
                title: 'Advanced',
                items: [{
index 7b00ea00f890fb9899224c010a101c9ae219665c..7755cd14ff7ab7f3d76f425f2d391cd7335347f3 100644 (file)
@@ -30,7 +30,13 @@ defaults._set('global', {
        datasets: {
                bar: {
                        categoryPercentage: 0.8,
-                       barPercentage: 0.9
+                       barPercentage: 0.9,
+                       animation: {
+                               numbers: {
+                                       type: 'number',
+                                       properties: ['x', 'y', 'base', 'width', 'height']
+                               }
+                       }
                }
        }
 });
@@ -267,50 +273,53 @@ module.exports = DatasetController.extend({
                meta.bar = true;
        },
 
-       update: function(reset) {
+       update: function(mode) {
                const me = this;
                const rects = me._cachedMeta.data;
 
-               me.updateElements(rects, 0, rects.length, reset);
+               me.updateElements(rects, 0, rects.length, mode);
        },
 
-       updateElements: function(rectangles, start, count, reset) {
+       updateElements: function(rectangles, start, count, mode) {
                const me = this;
+               const reset = mode === 'reset';
                const vscale = me._cachedMeta.vScale;
                const base = vscale.getBasePixel();
                const horizontal = vscale.isHorizontal();
                const ruler = me.getRuler();
+               const firstOpts = me._resolveDataElementOptions(start, mode);
+               const sharedOptions = me._getSharedOptions(mode, rectangles[start], firstOpts);
+               const includeOptions = me._includeOptions(mode, sharedOptions);
+
                let i;
 
                for (i = 0; i < start + count; i++) {
-                       const rectangle = rectangles[i];
-                       const options = me._resolveDataElementOptions(i);
+                       const options = me._resolveDataElementOptions(i, mode);
                        const vpixels = me.calculateBarValuePixels(i, options);
                        const ipixels = me.calculateBarIndexPixels(i, ruler, options);
 
-                       rectangle._model = {
-                               backgroundColor: options.backgroundColor,
-                               borderColor: options.borderColor,
-                               borderSkipped: options.borderSkipped,
-                               borderWidth: options.borderWidth
+                       const properties = {
+                               horizontal,
+                               base: reset ? base : vpixels.base,
+                               x: horizontal ? reset ? base : vpixels.head : ipixels.center,
+                               y: horizontal ? ipixels.center : reset ? base : vpixels.head,
+                               height: horizontal ? ipixels.size : undefined,
+                               width: horizontal ? undefined : ipixels.size
                        };
 
-                       const model = rectangle._model;
-
                        // all borders are drawn for floating bar
+                       /* TODO: float bars border skipping magic
                        if (me._getParsed(i)._custom) {
                                model.borderSkipped = null;
                        }
-
-                       model.horizontal = horizontal;
-                       model.base = reset ? base : vpixels.base;
-                       model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
-                       model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
-                       model.height = horizontal ? ipixels.size : undefined;
-                       model.width = horizontal ? undefined : ipixels.size;
-
-                       rectangle.pivot(me.chart._animationsDisabled);
+                       */
+                       if (includeOptions) {
+                               properties.options = options;
+                       }
+                       me._updateElement(rectangles[i], i, properties, mode);
                }
+
+               me._updateSharedOptions(sharedOptions, mode);
        },
 
        /**
index 1cf947b24aea10a5e2afac22a664b4d6f0e8aea8..21cacfb78489878014ef4ee38a7fcef1e83abe8e 100644 (file)
@@ -1,14 +1,18 @@
 'use strict';
 
-var DatasetController = require('../core/core.datasetController');
-var defaults = require('../core/core.defaults');
-var elements = require('../elements/index');
-var helpers = require('../helpers/index');
+const DatasetController = require('../core/core.datasetController');
+const defaults = require('../core/core.defaults');
+const elements = require('../elements/index');
+const helpers = require('../helpers/index');
 
-var valueOrDefault = helpers.valueOrDefault;
-var resolve = helpers.options.resolve;
+const resolve = helpers.options.resolve;
 
 defaults._set('bubble', {
+       animation: {
+               numbers: {
+                       properties: ['x', 'y', 'borderWidth', 'radius']
+               }
+       },
        scales: {
                x: {
                        type: 'linear',
@@ -43,11 +47,8 @@ module.exports = DatasetController.extend({
                'backgroundColor',
                'borderColor',
                'borderWidth',
-               'hoverBackgroundColor',
-               'hoverBorderColor',
-               'hoverBorderWidth',
-               'hoverRadius',
                'hitRadius',
+               'radius',
                'pointStyle',
                'rotation'
        ],
@@ -77,15 +78,14 @@ module.exports = DatasetController.extend({
         * @private
         */
        _getMaxOverflow: function() {
-               var me = this;
-               var meta = me._cachedMeta;
-               var data = meta.data || [];
-               if (!data.length) {
-                       return false;
+               const me = this;
+               const meta = me._cachedMeta;
+               let i = (meta.data || []).length - 1;
+               let max = 0;
+               for (; i >= 0; --i) {
+                       max = Math.max(max, me.getStyle(i, true).radius);
                }
-               var firstPoint = data[0].size();
-               var lastPoint = data[data.length - 1].size();
-               return Math.max(firstPoint, lastPoint) / 2;
+               return max > 0 && max;
        },
 
        /**
@@ -109,72 +109,56 @@ module.exports = DatasetController.extend({
        /**
         * @protected
         */
-       update: function(reset) {
+       update: function(mode) {
                const me = this;
                const points = me._cachedMeta.data;
 
                // Update Points
-               me.updateElements(points, 0, points.length, reset);
+               me.updateElements(points, 0, points.length, mode);
        },
 
        /**
         * @protected
         */
-       updateElements: function(points, start, count, reset) {
+       updateElements: function(points, start, count, mode) {
                const me = this;
+               const reset = mode === 'reset';
                const {xScale, yScale} = me._cachedMeta;
+               const firstOpts = me._resolveDataElementOptions(start, mode);
+               const sharedOptions = me._getSharedOptions(mode, points[start], firstOpts);
+               const includeOptions = me._includeOptions(mode, sharedOptions);
                let i;
 
                for (i = start; i < start + count; i++) {
                        const point = points[i];
-                       const options = me._resolveDataElementOptions(i);
                        const parsed = !reset && me._getParsed(i);
                        const x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(parsed[xScale.id]);
                        const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(parsed[yScale.id]);
-
-                       point._options = options;
-                       point._model = {
-                               backgroundColor: options.backgroundColor,
-                               borderColor: options.borderColor,
-                               borderWidth: options.borderWidth,
-                               hitRadius: options.hitRadius,
-                               pointStyle: options.pointStyle,
-                               rotation: options.rotation,
-                               radius: reset ? 0 : options.radius,
-                               skip: isNaN(x) || isNaN(y),
-                               x: x,
-                               y: y,
+                       const properties = {
+                               x,
+                               y,
+                               skip: isNaN(x) || isNaN(y)
                        };
 
-                       point.pivot(me.chart._animationsDisabled);
-               }
-       },
+                       if (includeOptions) {
+                               properties.options = i === start ? firstOpts
+                                       : me._resolveDataElementOptions(i, mode);
 
-       /**
-        * @protected
-        */
-       setHoverStyle: function(point) {
-               var model = point._model;
-               var options = point._options;
-               var getHoverColor = helpers.getHoverColor;
-
-               point.$previousStyle = {
-                       backgroundColor: model.backgroundColor,
-                       borderColor: model.borderColor,
-                       borderWidth: model.borderWidth,
-                       radius: model.radius
-               };
+                               if (reset) {
+                                       properties.options.radius = 0;
+                               }
+                       }
 
-               model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
-               model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
-               model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
-               model.radius = options.radius + options.hoverRadius;
+                       me._updateElement(point, i, properties, mode);
+               }
+
+               me._updateSharedOptions(sharedOptions, mode);
        },
 
        /**
         * @private
         */
-       _resolveDataElementOptions: function(index) {
+       _resolveDataElementOptions: function(index, mode) {
                var me = this;
                var chart = me.chart;
                var dataset = me.getDataset();
@@ -190,12 +174,16 @@ module.exports = DatasetController.extend({
                };
 
                // In case values were cached (and thus frozen), we need to clone the values
-               if (me._cachedDataOpts === values) {
-                       values = helpers.extend({}, values);
+               if (values.$shared) {
+                       values = helpers.extend({}, values, {$shared: false});
                }
 
+
                // Custom radius resolution
-               values.radius = resolve([
+               if (mode !== 'active') {
+                       values.radius = 0;
+               }
+               values.radius += resolve([
                        parsed && parsed._custom,
                        me._config.radius,
                        chart.options.elements.point.radius
index ad5fa86ea8e74f6aebc8db5339266d303899f9a3..954c2ec792c67cf3d69c0eddc33a597647bc1213 100644 (file)
@@ -13,6 +13,10 @@ var HALF_PI = PI / 2;
 
 defaults._set('doughnut', {
        animation: {
+               numbers: {
+                       type: 'number',
+                       properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius']
+               },
                // Boolean - Whether we animate the rotation of the Doughnut
                animateRotate: true,
                // Boolean - Whether we animate scaling the Doughnut from the centre
@@ -160,7 +164,7 @@ module.exports = DatasetController.extend({
                return ringIndex;
        },
 
-       update: function(reset) {
+       update: function(mode) {
                var me = this;
                var chart = me.chart;
                var chartArea = chart.chartArea;
@@ -200,7 +204,7 @@ module.exports = DatasetController.extend({
                }
 
                for (i = 0, ilen = arcs.length; i < ilen; ++i) {
-                       arcs[i]._options = me._resolveDataElementOptions(i);
+                       arcs[i]._options = me._resolveDataElementOptions(i, mode);
                }
 
                chart.borderWidth = me.getMaxBorderWidth();
@@ -217,57 +221,45 @@ module.exports = DatasetController.extend({
                me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
                me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);
 
-               me.updateElements(arcs, 0, arcs.length, reset);
+               me.updateElements(arcs, 0, arcs.length, mode);
        },
 
-       updateElements: function(arcs, start, count, reset) {
+       updateElements: function(arcs, start, count, mode) {
                const me = this;
+               const reset = mode === 'reset';
                const chart = me.chart;
                const chartArea = chart.chartArea;
                const opts = chart.options;
                const animationOpts = opts.animation;
                const centerX = (chartArea.left + chartArea.right) / 2;
                const centerY = (chartArea.top + chartArea.bottom) / 2;
-               const startAngle = opts.rotation; // non reset case handled later
-               const endAngle = opts.rotation; // non reset case handled later
                const meta = me.getMeta();
                const innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
                const outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
+               let startAngle = opts.rotation;
                let i;
 
                for (i = 0; i < start + count; ++i) {
                        const arc = arcs[i];
                        const circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(meta._parsed[i] * opts.circumference / DOUBLE_PI);
                        const options = arc._options || {};
-                       const model = {
-                               // Desired view properties
-                               backgroundColor: options.backgroundColor,
-                               borderColor: options.borderColor,
-                               borderWidth: options.borderWidth,
-                               borderAlign: options.borderAlign,
+                       if (i < start) {
+                               startAngle += circumference;
+                               continue;
+                       }
+                       const properties = {
                                x: centerX + chart.offsetX,
                                y: centerY + chart.offsetY,
-                               startAngle: startAngle,
-                               endAngle: endAngle,
-                               circumference: circumference,
-                               outerRadius: outerRadius,
-                               innerRadius: innerRadius
+                               startAngle,
+                               endAngle: startAngle + circumference,
+                               circumference,
+                               outerRadius,
+                               innerRadius,
+                               options
                        };
+                       startAngle += circumference;
 
-                       arc._model = model;
-
-                       // Set correct angles if not resetting
-                       if (!reset || !animationOpts.animateRotate) {
-                               if (i === 0) {
-                                       model.startAngle = opts.rotation;
-                               } else {
-                                       model.startAngle = me._cachedMeta.data[i - 1]._model.endAngle;
-                               }
-
-                               model.endAngle = model.startAngle + model.circumference;
-                       }
-
-                       arc.pivot(chart._animationsDisabled);
+                       me._updateElement(arc, i, properties, mode);
                }
        },
 
@@ -304,7 +296,7 @@ module.exports = DatasetController.extend({
                var me = this;
                var max = 0;
                var chart = me.chart;
-               var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
+               var i, ilen, meta, controller, options;
 
                if (!arcs) {
                        // Find the outmost visible dataset
@@ -312,8 +304,9 @@ module.exports = DatasetController.extend({
                                if (chart.isDatasetVisible(i)) {
                                        meta = chart.getDatasetMeta(i);
                                        arcs = meta.data;
-                                       if (i !== me.index) {
-                                               controller = meta.controller;
+                                       controller = meta.controller;
+                                       if (controller !== me) {
+                                               controller._configure();
                                        }
                                        break;
                                }
@@ -325,43 +318,14 @@ module.exports = DatasetController.extend({
                }
 
                for (i = 0, ilen = arcs.length; i < ilen; ++i) {
-                       arc = arcs[i];
-                       if (controller) {
-                               controller._configure();
-                               options = controller._resolveDataElementOptions(i);
-                       } else {
-                               options = arc._options;
-                       }
+                       options = controller._resolveDataElementOptions(i);
                        if (options.borderAlign !== 'inner') {
-                               borderWidth = options.borderWidth;
-                               hoverWidth = options.hoverBorderWidth;
-
-                               max = borderWidth > max ? borderWidth : max;
-                               max = hoverWidth > max ? hoverWidth : max;
+                               max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);
                        }
                }
                return max;
        },
 
-       /**
-        * @protected
-        */
-       setHoverStyle: function(arc) {
-               var model = arc._model;
-               var options = arc._options;
-               var getHoverColor = helpers.getHoverColor;
-
-               arc.$previousStyle = {
-                       backgroundColor: model.backgroundColor,
-                       borderColor: model.borderColor,
-                       borderWidth: model.borderWidth,
-               };
-
-               model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
-               model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
-               model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
-       },
-
        /**
         * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
         * @private
index 34d421f98591657927c7052ba37da430381e6e86..5277d9cb021fb0278bdcd4d83ec741b123a22801 100644 (file)
@@ -7,7 +7,6 @@ const helpers = require('../helpers/index');
 
 const valueOrDefault = helpers.valueOrDefault;
 const resolve = helpers.options.resolve;
-const isPointInArea = helpers.canvas._isPointInArea;
 
 defaults._set('line', {
        showLines: true,
@@ -44,6 +43,7 @@ module.exports = DatasetController.extend({
                'borderDashOffset',
                'borderJoinStyle',
                'borderWidth',
+               'capBezierPoints',
                'cubicInterpolationMode',
                'fill'
        ],
@@ -56,6 +56,7 @@ module.exports = DatasetController.extend({
                borderColor: 'pointBorderColor',
                borderWidth: 'pointBorderWidth',
                hitRadius: 'pointHitRadius',
+               hoverHitRadius: 'pointHitRadius',
                hoverBackgroundColor: 'pointHoverBackgroundColor',
                hoverBorderColor: 'pointHoverBorderColor',
                hoverBorderWidth: 'pointHoverBorderWidth',
@@ -65,7 +66,7 @@ module.exports = DatasetController.extend({
                rotation: 'pointRotation'
        },
 
-       update: function(reset) {
+       update: function(mode) {
                const me = this;
                const meta = me._cachedMeta;
                const line = meta.dataset;
@@ -73,62 +74,53 @@ module.exports = DatasetController.extend({
                const options = me.chart.options;
                const config = me._config;
                const showLine = me._showLine = valueOrDefault(config.showLine, options.showLines);
-               let i, ilen;
 
                // Update Line
-               if (showLine) {
-                       // Data
-                       line._children = points;
-                       // Model
-                       line._model = me._resolveDatasetElementOptions();
+               if (showLine && mode !== 'resize') {
 
-                       line.pivot();
-               }
-
-               // Update Points
-               me.updateElements(points, 0, points.length, reset);
+                       const properties = {
+                               _children: points,
+                               options: me._resolveDatasetElementOptions()
+                       };
 
-               if (showLine && line._model.tension !== 0) {
-                       me.updateBezierControlPoints();
+                       me._updateElement(line, undefined, properties, mode);
                }
 
-               // Now pivot the point for animation
-               for (i = 0, ilen = points.length; i < ilen; ++i) {
-                       points[i].pivot(me.chart._animationsDisabled);
+               // Update Points
+               if (meta.visible) {
+                       me.updateElements(points, 0, points.length, mode);
                }
        },
 
-       updateElements: function(points, start, count, reset) {
+       updateElements: function(points, start, count, mode) {
                const me = this;
+               const reset = mode === 'reset';
                const {xScale, yScale, _stacked} = me._cachedMeta;
+               const firstOpts = me._resolveDataElementOptions(start, mode);
+               const sharedOptions = me._getSharedOptions(mode, points[start], firstOpts);
+               const includeOptions = me._includeOptions(mode, sharedOptions);
                let i;
 
                for (i = start; i < start + count; ++i) {
                        const point = points[i];
                        const parsed = me._getParsed(i);
-                       const options = me._resolveDataElementOptions(i);
                        const x = xScale.getPixelForValue(parsed[xScale.id]);
                        const y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(_stacked ? me._applyStack(yScale, parsed) : parsed[yScale.id]);
+                       const properties = {
+                               x,
+                               y,
+                               skip: isNaN(x) || isNaN(y)
+                       };
 
-                       // Utility
-                       point._options = options;
+                       if (includeOptions) {
+                               properties.options = i === start ? firstOpts
+                                       : me._resolveDataElementOptions(i, mode);
+                       }
 
-                       // Desired view properties
-                       point._model = {
-                               x: x,
-                               y: y,
-                               skip: isNaN(x) || isNaN(y),
-                               // Appearance
-                               radius: options.radius,
-                               pointStyle: options.pointStyle,
-                               rotation: options.rotation,
-                               backgroundColor: options.backgroundColor,
-                               borderColor: options.borderColor,
-                               borderWidth: options.borderWidth,
-                               // Tooltip
-                               hitRadius: options.hitRadius
-                       };
+                       me._updateElement(point, i, properties, mode);
                }
+
+               me._updateSharedOptions(sharedOptions, mode);
        },
 
        /**
@@ -161,67 +153,12 @@ module.exports = DatasetController.extend({
                if (!data.length) {
                        return false;
                }
-               const border = me._showLine ? meta.dataset._model.borderWidth : 0;
+               const border = me._showLine && meta.dataset.options.borderWidth || 0;
                const firstPoint = data[0].size();
                const lastPoint = data[data.length - 1].size();
                return Math.max(border, firstPoint, lastPoint) / 2;
        },
 
-       updateBezierControlPoints: function() {
-               const me = this;
-               const chart = me.chart;
-               const meta = me._cachedMeta;
-               const lineModel = meta.dataset._model;
-               const area = chart.chartArea;
-               let points = meta.data || [];
-               let i, ilen;
-
-               // Only consider points that are drawn in case the spanGaps option is used
-               if (lineModel.spanGaps) {
-                       points = points.filter(function(pt) {
-                               return !pt._model.skip;
-                       });
-               }
-
-               function capControlPoint(pt, min, max) {
-                       return Math.max(Math.min(pt, max), min);
-               }
-
-               if (lineModel.cubicInterpolationMode === 'monotone') {
-                       helpers.curve.splineCurveMonotone(points);
-               } else {
-                       for (i = 0, ilen = points.length; i < ilen; ++i) {
-                               const model = points[i]._model;
-                               const controlPoints = helpers.curve.splineCurve(
-                                       points[Math.max(0, i - 1)]._model,
-                                       model,
-                                       points[Math.min(i + 1, ilen - 1)]._model,
-                                       lineModel.tension
-                               );
-                               model.controlPointPreviousX = controlPoints.previous.x;
-                               model.controlPointPreviousY = controlPoints.previous.y;
-                               model.controlPointNextX = controlPoints.next.x;
-                               model.controlPointNextY = controlPoints.next.y;
-                       }
-               }
-
-               if (chart.options.elements.line.capBezierPoints) {
-                       for (i = 0, ilen = points.length; i < ilen; ++i) {
-                               const model = points[i]._model;
-                               if (isPointInArea(model, area)) {
-                                       if (i > 0 && isPointInArea(points[i - 1]._model, area)) {
-                                               model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
-                                               model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
-                                       }
-                                       if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {
-                                               model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
-                                               model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
-                                       }
-                               }
-                       }
-               }
-       },
-
        draw: function() {
                const me = this;
                const ctx = me._ctx;
@@ -233,7 +170,7 @@ module.exports = DatasetController.extend({
                let i = 0;
 
                if (me._showLine) {
-                       meta.dataset.draw(ctx);
+                       meta.dataset.draw(ctx, area);
                }
 
                // Draw the points
@@ -241,25 +178,4 @@ module.exports = DatasetController.extend({
                        points[i].draw(ctx, area);
                }
        },
-
-       /**
-        * @protected
-        */
-       setHoverStyle: function(point) {
-               const model = point._model;
-               const options = point._options;
-               const getHoverColor = helpers.getHoverColor;
-
-               point.$previousStyle = {
-                       backgroundColor: model.backgroundColor,
-                       borderColor: model.borderColor,
-                       borderWidth: model.borderWidth,
-                       radius: model.radius
-               };
-
-               model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
-               model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
-               model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
-               model.radius = valueOrDefault(options.hoverRadius, options.radius);
-       },
 });
index fd6bc877c86f1fc0115f1796cad0c434c577ae44..c472783894949ac6cc0e02a523fea7e5b1838914 100644 (file)
@@ -8,6 +8,14 @@ var helpers = require('../helpers/index');
 var resolve = helpers.options.resolve;
 
 defaults._set('polarArea', {
+       animation: {
+               numbers: {
+                       type: 'number',
+                       properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius']
+               },
+               animateRotate: true,
+               animateScale: true
+       },
        scales: {
                r: {
                        type: 'radialLinear',
@@ -24,12 +32,6 @@ defaults._set('polarArea', {
                }
        },
 
-       // Boolean - Whether to animate the rotation of the chart
-       animation: {
-               animateRotate: true,
-               animateScale: true
-       },
-
        startAngle: -0.5 * Math.PI,
        legendCallback: function(chart) {
                var list = document.createElement('ul');
@@ -135,28 +137,14 @@ module.exports = DatasetController.extend({
                return this._cachedMeta.rAxisID;
        },
 
-       update: function(reset) {
+       update: function(mode) {
                var me = this;
-               var dataset = me.getDataset();
                var meta = me._cachedMeta;
-               var start = me.chart.options.startAngle || 0;
-               var starts = me._starts = [];
-               var angles = me._angles = [];
                var arcs = meta.data;
-               var i, ilen, angle;
 
                me._updateRadius();
 
-               meta.count = me.countVisibleElements();
-
-               for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
-                       starts[i] = start;
-                       angle = me._computeAngle(i);
-                       angles[i] = angle;
-                       start += angle;
-               }
-
-               me.updateElements(arcs, 0, arcs.length, reset);
+               me.updateElements(arcs, 0, arcs.length, mode);
        },
 
        /**
@@ -177,8 +165,9 @@ module.exports = DatasetController.extend({
                me.innerRadius = me.outerRadius - chart.radiusLength;
        },
 
-       updateElements: function(arcs, start, count, reset) {
+       updateElements: function(arcs, start, count, mode) {
                const me = this;
+               const reset = mode === 'reset';
                const chart = me.chart;
                const dataset = me.getDataset();
                const opts = chart.options;
@@ -186,33 +175,43 @@ module.exports = DatasetController.extend({
                const scale = chart.scales.r;
                const centerX = scale.xCenter;
                const centerY = scale.yCenter;
-               var i;
+               const datasetStartAngle = opts.startAngle || 0;
+               let angle = datasetStartAngle;
+               let i;
+
+               me._cachedMeta.count = me.countVisibleElements();
 
-               for (i = 0; i < start + count; i++) {
+               for (i = 0; i < start; ++i) {
+                       angle += me._computeAngle(i);
+               }
+               for (; i < start + count; i++) {
                        const arc = arcs[i];
-                       // var negHalfPI = -0.5 * Math.PI;
-                       const datasetStartAngle = opts.startAngle;
-                       const distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[i]);
-                       const startAngle = me._starts[i];
-                       const endAngle = startAngle + (arc.hidden ? 0 : me._angles[i]);
-
-                       const resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[i]);
-                       const options = arc._options = me._resolveDataElementOptions(i);
-
-                       arc._model = {
-                               backgroundColor: options.backgroundColor,
-                               borderColor: options.borderColor,
-                               borderWidth: options.borderWidth,
-                               borderAlign: options.borderAlign,
+                       let startAngle = angle;
+                       let endAngle = angle + me._computeAngle(i);
+                       let outerRadius = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[i]);
+                       angle = endAngle;
+
+                       if (reset) {
+                               if (animationOpts.animateScale) {
+                                       outerRadius = 0;
+                               }
+                               if (animationOpts.animateRotate) {
+                                       startAngle = datasetStartAngle;
+                                       endAngle = datasetStartAngle;
+                               }
+                       }
+
+                       const properties = {
                                x: centerX,
                                y: centerY,
                                innerRadius: 0,
-                               outerRadius: reset ? resetRadius : distance,
-                               startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
-                               endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle
+                               outerRadius,
+                               startAngle,
+                               endAngle,
+                               options: me._resolveDataElementOptions(i)
                        };
 
-                       arc.pivot(chart._animationsDisabled);
+                       me._updateElement(arc, i, properties, mode);
                }
        },
 
@@ -230,26 +229,6 @@ module.exports = DatasetController.extend({
                return count;
        },
 
-       /**
-        * @protected
-        */
-       setHoverStyle: function(arc) {
-               var model = arc._model;
-               var options = arc._options;
-               var getHoverColor = helpers.getHoverColor;
-               var valueOrDefault = helpers.valueOrDefault;
-
-               arc.$previousStyle = {
-                       backgroundColor: model.backgroundColor,
-                       borderColor: model.borderColor,
-                       borderWidth: model.borderWidth,
-               };
-
-               model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
-               model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
-               model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
-       },
-
        /**
         * @private
         */
index c52f871de599987420cfea7c003b93767862beb7..9f3ae118e58ecc391ebe3d2ec29c82c655e485bf 100644 (file)
@@ -21,14 +21,6 @@ defaults._set('radar', {
        }
 });
 
-function nextItem(collection, index) {
-       return index >= collection.length - 1 ? collection[0] : collection[index + 1];
-}
-
-function previousItem(collection, index) {
-       return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
-}
-
 module.exports = DatasetController.extend({
        datasetElementType: elements.Line,
 
@@ -93,66 +85,49 @@ module.exports = DatasetController.extend({
                };
        },
 
-       update: function(reset) {
+       update: function(mode) {
                var me = this;
                var meta = me._cachedMeta;
                var line = meta.dataset;
                var points = meta.data || [];
-               var animationsDisabled = me.chart._animationsDisabled;
-               var i, ilen;
 
-               // Data
-               line._children = points;
-               line._loop = true;
-               // Model
-               line._model = me._resolveDatasetElementOptions();
+               const properties = {
+                       _children: points,
+                       _loop: true,
+                       options: me._resolveDatasetElementOptions()
+               };
 
-               line.pivot(animationsDisabled);
+               me._updateElement(line, undefined, properties, mode);
 
                // Update Points
-               me.updateElements(points, 0, points.length, reset);
-
-               // Update bezier control points
-               me.updateBezierControlPoints();
+               me.updateElements(points, 0, points.length, mode);
 
-               // Now pivot the point for animation
-               for (i = 0, ilen = points.length; i < ilen; ++i) {
-                       points[i].pivot(animationsDisabled);
-               }
+               line.updateControlPoints(me.chart.chartArea);
        },
 
-       updateElements: function(points, start, count, reset) {
+       updateElements: function(points, start, count, mode) {
                const me = this;
                const dataset = me.getDataset();
                const scale = me.chart.scales.r;
+               const reset = mode === 'reset';
                var i;
 
                for (i = start; i < start + count; i++) {
                        const point = points[i];
-                       const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]);
                        const options = me._resolveDataElementOptions(i);
+                       const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]);
+
                        const x = reset ? scale.xCenter : pointPosition.x;
                        const y = reset ? scale.yCenter : pointPosition.y;
 
-                       // Utility
-                       point._options = options;
-
-                       // Desired view properties
-                       point._model = {
-                               x: x, // value not used in dataset scale, but we want a consistent API between scales
+                       const properties = {
+                               x: x,
                                y: y,
                                skip: isNaN(x) || isNaN(y),
-                               // Appearance
-                               radius: options.radius,
-                               pointStyle: options.pointStyle,
-                               rotation: options.rotation,
-                               backgroundColor: options.backgroundColor,
-                               borderColor: options.borderColor,
-                               borderWidth: options.borderWidth,
-
-                               // Tooltip
-                               hitRadius: options.hitRadius
+                               options,
                        };
+
+                       me._updateElement(point, i, properties, mode);
                }
        },
 
@@ -169,59 +144,5 @@ module.exports = DatasetController.extend({
                values.tension = valueOrDefault(config.lineTension, options.elements.line.tension);
 
                return values;
-       },
-
-       updateBezierControlPoints: function() {
-               var me = this;
-               var meta = me._cachedMeta;
-               var lineModel = meta.dataset._model;
-               var area = me.chart.chartArea;
-               var points = meta.data || [];
-               var i, ilen, model, controlPoints;
-
-               // Only consider points that are drawn in case the spanGaps option is used
-               if (meta.dataset._model.spanGaps) {
-                       points = points.filter(function(pt) {
-                               return !pt._model.skip;
-                       });
-               }
-
-               function capControlPoint(pt, min, max) {
-                       return Math.max(Math.min(pt, max), min);
-               }
-
-               for (i = 0, ilen = points.length; i < ilen; ++i) {
-                       model = points[i]._model;
-                       controlPoints = helpers.curve.splineCurve(
-                               previousItem(points, i)._model,
-                               model,
-                               nextItem(points, i)._model,
-                               lineModel.tension
-                       );
-
-                       // Prevent the bezier going outside of the bounds of the graph
-                       model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
-                       model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
-                       model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
-                       model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
-               }
-       },
-
-       setHoverStyle: function(point) {
-               var model = point._model;
-               var options = point._options;
-               var getHoverColor = helpers.getHoverColor;
-
-               point.$previousStyle = {
-                       backgroundColor: model.backgroundColor,
-                       borderColor: model.borderColor,
-                       borderWidth: model.borderWidth,
-                       radius: model.radius
-               };
-
-               model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
-               model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
-               model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
-               model.radius = valueOrDefault(options.hoverRadius, options.radius);
        }
 });
index ce47f6d735966e848c602c6cbdea2db084545e1a..1e97684da1c40fc92b8356cf86cadfcb5fcac44c 100644 (file)
@@ -1,24 +1,92 @@
 'use strict';
 
-const Element = require('./core.element');
 const helpers = require('../helpers/index');
 
-class Animation extends Element {
+const transparent = 'transparent';
+const interpolators = {
+       number: function(from, to, factor) {
+               return from + (to - from) * factor;
+       },
+       color: function(from, to, factor) {
+               var c0 = helpers.color(from || transparent);
+               var c1 = c0.valid && helpers.color(to || transparent);
+               return c1 && c1.valid
+                       ? c1.mix(c0, factor).rgbaString()
+                       : to;
+       }
+};
+
+class Animation {
+       constructor(cfg, target, prop, to) {
+               const me = this;
+               let from = cfg.from;
+
+               if (from === undefined) {
+                       from = target[prop];
+               }
+               if (to === undefined) {
+                       to = target[prop];
+               }
+
+               if (from === undefined) {
+                       from = to;
+               } else if (to === undefined) {
+                       to = from;
+               }
 
-       constructor(props) {
-               super({
-                       chart: null, // the animation associated chart instance
-                       currentStep: 0, // the current animation step
-                       numSteps: 60, // default number of steps
-                       easing: '', // the easing to use for this animation
-                       render: null, // render function used by the animation service
+               me._active = true;
+               me._fn = cfg.fn || interpolators[cfg.type || typeof from];
+               me._easing = helpers.easing.effects[cfg.easing || 'linear'];
+               me._start = Math.floor(Date.now() + (cfg.delay || 0));
+               me._duration = Math.floor(cfg.duration);
+               me._loop = !!cfg.loop;
+               me._target = target;
+               me._prop = prop;
+               me._from = from;
+               me._to = to;
+       }
+
+       active() {
+               return this._active;
+       }
 
-                       onAnimationProgress: null, // user specified callback to fire on each step of the animation
-                       onAnimationComplete: null, // user specified callback to fire when the animation finishes
-               });
-               helpers.extend(this, props);
+       cancel() {
+               const me = this;
+               if (me._active) {
+                       // update current evaluated value, for smoother animations
+                       me.tick(Date.now());
+                       me._active = false;
+               }
        }
 
+       tick(date) {
+               const me = this;
+               const elapsed = date - me._start;
+               const duration = me._duration;
+               const prop = me._prop;
+               const from = me._from;
+               const loop = me._loop;
+               const to = me._to;
+               let factor;
+
+               me._active = from !== to && (loop || (elapsed < duration));
+
+               if (!me._active) {
+                       me._target[prop] = to;
+                       return;
+               }
+
+               if (elapsed < 0) {
+                       me._target[prop] = from;
+                       return;
+               }
+
+               factor = (elapsed / duration) % 2;
+               factor = loop && factor > 1 ? 2 - factor : factor;
+               factor = me._easing(Math.min(1, Math.max(0, factor)));
+
+               me._target[prop] = me._fn(from, to, factor);
+       }
 }
 
 module.exports = Animation;
index 5e7222a779c8ab73b99f211a1ed0d7989299f46c..86b022af9a579ddae472d5383028fec3056194cc 100644 (file)
 'use strict';
 
-var defaults = require('./core.defaults');
-var helpers = require('../helpers/index');
+const Animator = require('./core.animator');
+const Animation = require('./core.animation');
+const helpers = require('../helpers/index');
+const defaults = require('./core.defaults');
 
 defaults._set('global', {
        animation: {
                duration: 1000,
                easing: 'easeOutQuart',
+               active: {
+                       duration: 400
+               },
+               resize: {
+                       duration: 0
+               },
+               numbers: {
+                       type: 'number',
+                       properties: ['x', 'y', 'borderWidth', 'radius', 'tension']
+               },
+               colors: {
+                       type: 'color',
+                       properties: ['borderColor', 'backgroundColor']
+               },
                onProgress: helpers.noop,
                onComplete: helpers.noop
        }
 });
 
-module.exports = {
-       animations: [],
-       request: null,
-
-       /**
-        * @param {Chart} chart - The chart to animate.
-        * @param {Chart.Animation} animation - The animation that we will animate.
-        * @param {number} duration - The animation duration in ms.
-        * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
-        */
-       addAnimation: function(chart, animation, duration, lazy) {
-               var animations = this.animations;
-               var i, ilen;
-
-               animation.chart = chart;
-               animation.startTime = Date.now();
-               animation.duration = duration;
+function copyOptions(target, values) {
+       let oldOpts = target.options;
+       let newOpts = values.options;
+       if (!oldOpts || !newOpts || newOpts.$shared) {
+               return;
+       }
+       if (oldOpts.$shared) {
+               target.options = helpers.extend({}, oldOpts, newOpts, {$shared: false});
+       } else {
+               helpers.extend(oldOpts, newOpts);
+       }
+       delete values.options;
+}
+
+class Animations {
+       constructor(chart, animations) {
+               this._chart = chart;
+               this._properties = new Map();
+               this.configure(animations);
+       }
 
-               if (!lazy) {
-                       chart.animating = true;
-               }
+       configure(animations) {
+               const animatedProps = this._properties;
+               const animDefaults = Object.fromEntries(Object.entries(animations).filter(({1: value}) => !helpers.isObject(value)));
 
-               for (i = 0, ilen = animations.length; i < ilen; ++i) {
-                       if (animations[i].chart === chart) {
-                               animations[i] = animation;
-                               return;
+               for (let [key, cfg] of Object.entries(animations)) {
+                       if (!helpers.isObject(cfg)) {
+                               continue;
+                       }
+                       for (let prop of cfg.properties || [key]) {
+                               // Can have only one config per animation.
+                               if (!animatedProps.has(prop)) {
+                                       animatedProps.set(prop, helpers.extend({}, animDefaults, cfg));
+                               } else if (prop === key) {
+                                       // Single property targetting config wins over multi-targetting.
+                                       animatedProps.set(prop, helpers.extend({}, animatedProps.get(prop), cfg));
+                               }
                        }
                }
+       }
 
-               animations.push(animation);
-
-               // If there are no animations queued, manually kickstart a digest, for lack of a better word
-               if (animations.length === 1) {
-                       this.requestAnimationFrame();
-               }
-       },
-
-       cancelAnimation: function(chart) {
-               var index = helpers.findIndex(this.animations, function(animation) {
-                       return animation.chart === chart;
-               });
+       /**
+        * Utility to handle animation of `options`.
+        * This should not be called, when animating $shared options to $shared new options.
+        * @private
+        * @todo if new options are $shared, target.options should be replaced with those new shared
+        *  options after all animations have completed
+        */
+       _animateOptions(target, values) {
+               const newOptions = values.options;
+               let animations = [];
 
-               if (index !== -1) {
-                       this.animations.splice(index, 1);
-                       chart.animating = false;
+               if (!newOptions) {
+                       return animations;
                }
-       },
-
-       requestAnimationFrame: function() {
-               var me = this;
-               if (me.request === null) {
-                       // Skip animation frame requests until the active one is executed.
-                       // This can happen when processing mouse events, e.g. 'mousemove'
-                       // and 'mouseout' events will trigger multiple renders.
-                       me.request = helpers.requestAnimFrame.call(window, function() {
-                               me.request = null;
-                               me.startDigest();
-                       });
+               let options = target.options;
+               if (options) {
+                       if (options.$shared) {
+                               // If the current / old options are $shared, meaning other elements are
+                               // using the same options, we need to clone to become unique.
+                               target.options = options = helpers.extend({}, options, {$shared: false, $animations: {}});
+                       }
+                       animations = this._createAnimations(options, newOptions);
+               } else {
+                       target.options = newOptions;
                }
-       },
+               return animations;
+       }
 
        /**
         * @private
         */
-       startDigest: function() {
-               var me = this;
+       _createAnimations(target, values) {
+               const animatedProps = this._properties;
+               const animations = [];
+               const running = target.$animations || (target.$animations = {});
+               const props = Object.keys(values);
+               let i;
+
+               for (i = props.length - 1; i >= 0; --i) {
+                       let prop = props[i];
+                       if (prop.charAt(0) === '$') {
+                               continue;
+                       }
+
+                       if (prop === 'options') {
+                               animations.push.apply(animations, this._animateOptions(target, values));
+                               continue;
+                       }
+                       let value = values[prop];
 
-               me.advance();
+                       const cfg = animatedProps.get(prop);
+                       if (!cfg || !cfg.duration) {
+                               // not animated, set directly to new value
+                               target[prop] = value;
+                               continue;
+                       }
 
-               // Do we have more stuff to animate?
-               if (me.animations.length > 0) {
-                       me.requestAnimationFrame();
+                       let animation = running[prop];
+                       if (animation) {
+                               animation.cancel();
+                       }
+                       running[prop] = animation = new Animation(cfg, target, prop, value);
+                       animations.push(animation);
                }
-       },
+               return animations;
+       }
+
 
        /**
-        * @private
-        */
-       advance: function() {
-               var animations = this.animations;
-               var animation, chart, numSteps, nextStep;
-               var i = 0;
-
-               // 1 animation per chart, so we are looping charts here
-               while (i < animations.length) {
-                       animation = animations[i];
-                       chart = animation.chart;
-                       numSteps = animation.numSteps;
-
-                       // Make sure that currentStep starts at 1
-                       // https://github.com/chartjs/Chart.js/issues/6104
-                       nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1;
-                       animation.currentStep = Math.min(nextStep, numSteps);
-
-                       helpers.callback(animation.render, [chart, animation], chart);
-                       helpers.callback(animation.onAnimationProgress, [animation], chart);
-
-                       if (animation.currentStep >= numSteps) {
-                               helpers.callback(animation.onAnimationComplete, [animation], chart);
-                               chart.animating = false;
-                               animations.splice(i, 1);
-                       } else {
-                               ++i;
-                       }
+        * Update `target` properties to new values, using configured animations
+        * @param {object} target - object to update
+        * @param {object} values - new target properties
+        * @returns {boolean|undefined} - `true` if animations were started
+        **/
+       update(target, values) {
+               if (this._properties.size === 0) {
+                       // Nothing is animated, just apply the new values.
+                       // Options can be shared, need to account for that.
+                       copyOptions(target, values);
+                       // copyOptions removes the `options` from `values`,
+                       // unless it can be directly assigned.
+                       helpers.extend(target, values);
+                       return;
+               }
+
+               const animations = this._createAnimations(target, values);
+
+               if (animations.length) {
+                       Animator.add(this._chart, animations);
+                       return true;
                }
        }
-};
+}
+
+module.exports = Animations;
diff --git a/src/core/core.animator.js b/src/core/core.animator.js
new file mode 100644 (file)
index 0000000..29422d5
--- /dev/null
@@ -0,0 +1,211 @@
+'use strict';
+
+const helpers = require('../helpers/index');
+
+function drawFPS(chart, count, date, lastDate) {
+       const fps = (1000 / (date - lastDate)) | 0;
+       const ctx = chart.ctx;
+       ctx.save();
+       ctx.clearRect(0, 0, 50, 24);
+       ctx.fillStyle = 'black';
+       ctx.textAlign = 'right';
+       if (count) {
+               ctx.fillText(count, 50, 8);
+               ctx.fillText(fps + ' fps', 50, 18);
+       }
+       ctx.restore();
+}
+
+class Animator {
+       constructor() {
+               this._request = null;
+               this._charts = new Map();
+               this._running = false;
+       }
+
+       /**
+        * @private
+        */
+       _notify(chart, anims, date, type) {
+               const callbacks = anims.listeners[type] || [];
+               const numSteps = anims.duration;
+
+               callbacks.forEach(fn => fn({
+                       chart: chart,
+                       numSteps,
+                       currentStep: date - anims.start
+               }));
+       }
+
+       /**
+        * @private
+        */
+       _refresh() {
+               const me = this;
+
+               if (me._request) {
+                       return;
+               }
+               me._running = true;
+
+               me._request = helpers.requestAnimFrame.call(window, function() {
+                       me._update();
+                       me._request = null;
+
+                       if (me._running) {
+                               me._refresh();
+                       }
+               });
+       }
+
+       /**
+        * @private
+        */
+       _update() {
+               const me = this;
+               const date = Date.now();
+               const charts = me._charts;
+               let remaining = 0;
+
+               for (let [chart, anims] of charts) {
+                       if (!anims.running || !anims.items.length) {
+                               continue;
+                       }
+                       const items = anims.items;
+                       let i = items.length - 1;
+                       let draw = false;
+                       let item;
+
+                       for (; i >= 0; --i) {
+                               item = items[i];
+
+                               if (item._active) {
+                                       item.tick(date);
+                                       draw = true;
+                               } else {
+                                       // Remove the item by replacing it with last item and removing the last
+                                       // A lot faster than splice.
+                                       items[i] = items[items.length - 1];
+                                       items.pop();
+                               }
+                       }
+
+                       if (draw) {
+                               chart.draw();
+                               if (chart.options.animation.debug) {
+                                       drawFPS(chart, items.length, date, me._lastDate);
+                               }
+                       }
+
+                       me._notify(chart, anims, date, 'progress');
+
+                       if (!items.length) {
+                               anims.running = false;
+                               me._notify(chart, anims, date, 'complete');
+                       }
+
+                       remaining += items.length;
+               }
+
+               this._lastDate = date;
+
+               if (remaining === 0) {
+                       this._running = false;
+               }
+       }
+
+       _getAnims(chart) {
+               const charts = this._charts;
+               let anims = charts.get(chart);
+               if (!anims) {
+                       anims = {
+                               running: false,
+                               items: [],
+                               listeners: {
+                                       complete: [],
+                                       progress: []
+                               }
+                       };
+                       charts.set(chart, anims);
+               }
+               return anims;
+       }
+
+       /**
+        * @param {Chart} chart
+        * @param {string} event - event name
+        * @param {Function} cb - callback
+        */
+       listen(chart, event, cb) {
+               this._getAnims(chart).listeners[event].push(cb);
+       }
+
+       /**
+        * Add animations
+        * @param {Chart} chart
+        * @param {Animation[]} items - animations
+        */
+       add(chart, items) {
+               if (!items || !items.length) {
+                       return;
+               }
+               this._getAnims(chart).items.push(...items);
+       }
+
+       /**
+        * Counts number of active animations for the chart
+        * @param {Chart} chart
+        */
+       has(chart) {
+               return this._getAnims(chart).items.length > 0;
+       }
+
+       /**
+        * Start animating (all charts)
+        * @param {Chart} chart
+        */
+       start(chart) {
+               const anims = this._charts.get(chart);
+               if (!anims) {
+                       return;
+               }
+               anims.running = true;
+               anims.start = Date.now();
+               anims.duration = anims.items.reduce((acc, cur) => Math.max(acc, cur._duration), 0);
+               this._refresh();
+       }
+
+       running(chart) {
+               if (!this._running) {
+                       return false;
+               }
+               const anims = this._charts.get(chart);
+               if (!anims || !anims.running || !anims.items.length) {
+                       return false;
+               }
+               return true;
+       }
+
+       /**
+        * Stop all animations for the chart
+        * @param {Chart} chart
+        */
+       stop(chart) {
+               const anims = this._charts.get(chart);
+               if (!anims || !anims.items.length) {
+                       return;
+               }
+               const items = anims.items;
+               let i = items.length - 1;
+
+               for (; i >= 0; --i) {
+                       items[i].cancel();
+               }
+               anims.items = [];
+               this._notify(chart, anims, Date.now(), 'complete');
+       }
+}
+
+const instance = new Animator();
+
+module.exports = instance;
index 305d56549f302a5b4bf6e18291ab66d55f829aed..b970f05b3f372bbc4b6c37c6b765ae4f161baaa8 100644 (file)
@@ -1,7 +1,6 @@
 'use strict';
 
-var Animation = require('./core.animation');
-var animations = require('./core.animations');
+var Animator = require('./core.animator');
 var controllers = require('../controllers/index');
 var defaults = require('./core.defaults');
 var helpers = require('../helpers/index');
@@ -26,13 +25,11 @@ defaults._set('global', {
        hover: {
                onHover: null,
                mode: 'nearest',
-               intersect: true,
-               animationDuration: 400
+               intersect: true
        },
        onClick: null,
        maintainAspectRatio: true,
-       responsive: true,
-       responsiveAnimationDuration: 0
+       responsive: true
 });
 
 function mergeScaleConfig(config, options) {
@@ -115,11 +112,7 @@ function initConfig(config) {
 }
 
 function isAnimationDisabled(config) {
-       return !config.animation || !(
-               config.animation.duration ||
-               (config.hover && config.hover.animationDuration) ||
-               config.responsiveAnimationDuration
-       );
+       return !config.animation;
 }
 
 function updateConfig(chart) {
@@ -143,8 +136,6 @@ function updateConfig(chart) {
        chart.ensureScalesHaveIDs();
        chart.buildOrUpdateScales();
 
-       // Tooltip
-       chart.tooltip._options = newOptions.tooltips;
        chart.tooltip.initialize();
 }
 
@@ -161,6 +152,20 @@ function compare2Level(l1, l2) {
        };
 }
 
+function onAnimationsComplete(ctx) {
+       const chart = ctx.chart;
+       const animationOptions = chart.options.animation;
+
+       plugins.notify(chart, 'afterRender');
+       helpers.callback(animationOptions && animationOptions.onComplete, arguments, chart);
+}
+
+function onAnimationProgress(ctx) {
+       const chart = ctx.chart;
+       const animationOptions = chart.options.animation;
+       helpers.callback(animationOptions && animationOptions.onProgress, arguments, chart);
+}
+
 var Chart = function(item, config) {
        this.construct(item, config);
        return this;
@@ -213,6 +218,9 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        return;
                }
 
+               Animator.listen(me, 'complete', onAnimationsComplete);
+               Animator.listen(me, 'progress', onAnimationProgress);
+
                me.initialize();
                me.update();
        },
@@ -249,8 +257,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
        },
 
        stop: function() {
-               // Stops any current animation loop occurring
-               animations.cancelAnimation(this);
+               Animator.stop(this);
                return this;
        },
 
@@ -289,9 +296,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        }
 
                        me.stop();
-                       me.update({
-                               duration: options.responsiveAnimationDuration
-                       });
+                       me.update('resize');
                }
        },
 
@@ -455,11 +460,11 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                this.tooltip.initialize();
        },
 
-       update: function(config) {
+       update: function(mode) {
                var me = this;
                var i, ilen;
 
-               config = config || {};
+               me._updating = true;
 
                updateConfig(me);
 
@@ -471,9 +476,6 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        return;
                }
 
-               // In case the entire data object changed
-               me.tooltip._data = me.data;
-
                // Make sure dataset controllers are updated and new controllers are reset
                var newControllers = me.buildOrUpdateControllers();
 
@@ -485,40 +487,27 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                me.updateLayout();
 
                // Can only reset the new controllers after the scales have been updated
-               if (me.options.animation && me.options.animation.duration) {
+               if (me.options.animation) {
                        helpers.each(newControllers, function(controller) {
                                controller.reset();
                        });
                }
 
-               me.updateDatasets();
-
-               // Need to reset tooltip in case it is displayed with elements that are removed
-               // after update.
-               me.tooltip.initialize();
-
-               // Last active contains items that were previously hovered.
-               me.lastActive = [];
+               me.updateDatasets(mode);
 
                // Do this before render so that any plugins that need final scale updates can use it
                plugins.notify(me, 'afterUpdate');
 
                me._layers.sort(compare2Level('z', '_idx'));
 
-               if (me._bufferedRender) {
-                       me._bufferedRequest = {
-                               duration: config.duration,
-                               easing: config.easing,
-                               lazy: config.lazy
-                       };
-               } else {
-                       me.render(config);
-               }
-
                // Replay last event from before update
                if (me._lastEvent) {
                        me.eventHandler(me._lastEvent);
                }
+
+               me.render();
+
+               me._updating = false;
        },
 
        /**
@@ -557,7 +546,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
         * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
         * @private
         */
-       updateDatasets: function() {
+       updateDatasets: function(mode) {
                var me = this;
 
                if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
@@ -565,7 +554,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                }
 
                for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
-                       me.updateDataset(i);
+                       me.updateDataset(i, mode);
                }
 
                plugins.notify(me, 'afterDatasetsUpdate');
@@ -576,91 +565,52 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
         * 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
-               };
+       updateDataset: function(index, mode) {
+               const me = this;
+               const meta = me.getDatasetMeta(index);
+               const args = {meta, index, mode};
 
                if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
                        return;
                }
 
-               meta.controller._update();
+               meta.controller._update(mode);
 
                plugins.notify(me, 'afterDatasetUpdate', [args]);
        },
 
-       render: function(config) {
-               var me = this;
-
-               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;
-
+       render: function() {
+               const me = this;
+               const animationOptions = me.options.animation;
                if (plugins.notify(me, 'beforeRender') === false) {
                        return;
                }
-
-               var onComplete = function(animation) {
+               var onComplete = function() {
                        plugins.notify(me, 'afterRender');
-                       helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
+                       helpers.callback(animationOptions && animationOptions.onComplete, [], me);
                };
 
-               if (animationOptions && duration) {
-                       var animation = new Animation({
-                               numSteps: duration / 16.66, // 60 fps
-                               easing: config.easing || animationOptions.easing,
-
-                               render: function(chart, animationObject) {
-                                       const easingFunction = helpers.easing.effects[animationObject.easing];
-                                       const stepDecimal = animationObject.currentStep / animationObject.numSteps;
-
-                                       chart.draw(easingFunction(stepDecimal));
-                               },
-
-                               onAnimationProgress: animationOptions.onProgress,
-                               onAnimationComplete: onComplete
-                       });
-
-                       animations.addAnimation(me, animation, duration, lazy);
+               if (Animator.has(me)) {
+                       if (!Animator.running(me)) {
+                               Animator.start(me);
+                       }
                } else {
                        me.draw();
-
-                       // See https://github.com/chartjs/Chart.js/issues/3781
-                       onComplete(new Animation({numSteps: 0, chart: me}));
+                       onComplete();
                }
-
-               return me;
        },
 
-       draw: function(easingValue) {
+       draw: function() {
                var me = this;
                var i, layers;
 
                me.clear();
 
-               if (helpers.isNullOrUndef(easingValue)) {
-                       easingValue = 1;
-               }
-
-               me.transition(easingValue);
-
                if (me.width <= 0 || me.height <= 0) {
                        return;
                }
 
-               if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
+               if (plugins.notify(me, 'beforeDraw') === false) {
                        return;
                }
 
@@ -672,41 +622,16 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        layers[i].draw(me.chartArea);
                }
 
-               me.drawDatasets(easingValue);
+               me.drawDatasets();
 
                // Rest of layers
                for (; i < layers.length; ++i) {
                        layers[i].draw(me.chartArea);
                }
 
-               me._drawTooltip(easingValue);
+               me._drawTooltip();
 
-               plugins.notify(me, 'afterDraw', [easingValue]);
-       },
-
-       /**
-        * @private
-        */
-       transition: function(easingValue) {
-               const me = this;
-               var i, ilen;
-
-               if (!me._animationsDisabled) {
-                       const metas = me._getSortedDatasetMetas();
-                       for (i = 0, ilen = metas.length; i < ilen; ++i) {
-                               let meta = metas[i];
-                               if (meta.visible) {
-                                       meta.controller.transition(easingValue);
-                               }
-                       }
-               }
-
-               me.tooltip.transition(easingValue);
-
-               if (me._lastEvent && me.animating) {
-                       // If, during animation, element under mouse changes, let's react to that.
-                       me.handleEvent(me._lastEvent);
-               }
+               plugins.notify(me, 'afterDraw');
        },
 
        /**
@@ -740,20 +665,20 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
         * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
         * @private
         */
-       drawDatasets: function(easingValue) {
+       drawDatasets: function() {
                var me = this;
                var metasets, i;
 
-               if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
+               if (plugins.notify(me, 'beforeDatasetsDraw') === false) {
                        return;
                }
 
                metasets = me._getSortedVisibleDatasetMetas();
                for (i = metasets.length - 1; i >= 0; --i) {
-                       me.drawDataset(metasets[i], easingValue);
+                       me.drawDataset(metasets[i]);
                }
 
-               plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
+               plugins.notify(me, 'afterDatasetsDraw');
        },
 
        /**
@@ -761,7 +686,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
         * hook, in which case, plugins will not be called on `afterDatasetDraw`.
         * @private
         */
-       drawDataset: function(meta, easingValue) {
+       drawDataset: function(meta) {
                var me = this;
                var ctx = me.ctx;
                var clip = meta._clip;
@@ -770,7 +695,6 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                var args = {
                        meta: meta,
                        index: meta.index,
-                       easingValue: easingValue
                };
 
                if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
@@ -784,7 +708,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom
                });
 
-               meta.controller.draw(easingValue);
+               meta.controller.draw();
 
                helpers.canvas.unclipArea(ctx);
 
@@ -796,19 +720,18 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
         * hook, in which case, plugins will not be called on `afterTooltipDraw`.
         * @private
         */
-       _drawTooltip: function(easingValue) {
+       _drawTooltip: function() {
                var me = this;
                var tooltip = me.tooltip;
                var args = {
-                       tooltip: tooltip,
-                       easingValue: easingValue
+                       tooltip: tooltip
                };
 
                if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
                        return;
                }
 
-               tooltip.draw();
+               tooltip.draw(me.ctx);
 
                plugins.notify(me, 'afterTooltipDraw', [args]);
        },
@@ -925,12 +848,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
        },
 
        initToolTip: function() {
-               var me = this;
-               me.tooltip = new Tooltip({
-                       _chart: me,
-                       _data: me.data,
-                       _options: me.options.tooltips
-               });
+               this.tooltip = new Tooltip({_chart: this});
        },
 
        /**
@@ -1020,48 +938,22 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
         * @private
         */
        eventHandler: function(e) {
-               var me = this;
-               var tooltip = me.tooltip;
+               const me = this;
+               const tooltip = me.tooltip;
 
                if (plugins.notify(me, 'beforeEvent', [e]) === false) {
                        return;
                }
 
-               // Buffer any update calls so that renders do not occur
-               me._bufferedRender = true;
-               me._bufferedRequest = null;
+               me.handleEvent(e);
 
-               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);
+                       tooltip.handleEvent(e);
                }
 
                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
-                       });
-               }
-
-               me._bufferedRender = false;
-               me._bufferedRequest = null;
+               me.render();
 
                return me;
        },
@@ -1086,7 +978,7 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        me._lastEvent = null;
                } else {
                        me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
-                       me._lastEvent = e.type === 'click' ? null : e;
+                       me._lastEvent = e.type === 'click' ? me._lastEvent : e;
                }
 
                // Invoke onHover hook
@@ -1100,8 +992,10 @@ helpers.extend(Chart.prototype, /** @lends Chart */ {
                        }
                }
 
-               me._updateHoverStyles();
                changed = !helpers._elementsEqual(me.active, me.lastActive);
+               if (changed) {
+                       me._updateHoverStyles();
+               }
 
                // Remember Last Actives
                me.lastActive = me.active;
index 2c5e35e9ed4708e9913dddcc5f380e33c8108c8c..3a818477d1bf0fa7db8582c50927393e5f18c50b 100644 (file)
@@ -1,6 +1,7 @@
 'use strict';
 
 var helpers = require('../helpers/index');
+var Animations = require('./core.animations');
 
 var resolve = helpers.options.resolve;
 
@@ -268,6 +269,8 @@ helpers.extend(DatasetController.prototype, {
                me.chart = chart;
                me._ctx = chart.ctx;
                me.index = datasetIndex;
+               me._cachedAnimations = {};
+               me._cachedDataOpts = {};
                me._cachedMeta = meta = me.getMeta();
                me._type = meta.type;
                me._configure();
@@ -347,7 +350,7 @@ helpers.extend(DatasetController.prototype, {
        },
 
        reset: function() {
-               this._update(true);
+               this._update('reset');
        },
 
        /**
@@ -450,7 +453,7 @@ helpers.extend(DatasetController.prototype, {
        },
 
        /**
-        * Returns the merged user-supplied and default dataset-level options
+        * Merges user-supplied and default dataset-level options
         * @private
         */
        _configure: function() {
@@ -740,33 +743,19 @@ helpers.extend(DatasetController.prototype, {
        /**
         * @private
         */
-       _update: function(reset) {
+       _update: function(mode) {
                const me = this;
                const meta = me._cachedMeta;
                me._configure();
-               me._cachedDataOpts = null;
-               me.update(reset);
+               me._cachedAnimations = {};
+               me._cachedDataOpts = {};
+               me.update(mode);
                meta._clip = toClip(helpers.valueOrDefault(me._config.clip, defaultClip(meta.xScale, meta.yScale, me._getMaxOverflow())));
                me._cacheScaleStackStatus();
        },
 
        update: helpers.noop,
 
-       transition: function(easingValue) {
-               const meta = this._cachedMeta;
-               const elements = meta.data || [];
-               const ilen = elements.length;
-               let i = 0;
-
-               for (; i < ilen; ++i) {
-                       elements[i].transition(easingValue);
-               }
-
-               if (meta.dataset) {
-                       meta.dataset.transition(easingValue);
-               }
-       },
-
        draw: function() {
                const ctx = this._ctx;
                const meta = this._cachedMeta;
@@ -783,30 +772,54 @@ helpers.extend(DatasetController.prototype, {
                }
        },
 
+       _addAutomaticHoverColors: function(index, options) {
+               const me = this;
+               const getHoverColor = helpers.getHoverColor;
+               const normalOptions = me.getStyle(index);
+               const missingColors = Object.keys(normalOptions).filter(key => {
+                       return key.indexOf('Color') !== -1 && !(key in options);
+               });
+               let i = missingColors.length - 1;
+               let color;
+               for (; i >= 0; i--) {
+                       color = missingColors[i];
+                       options[color] = getHoverColor(normalOptions[color]);
+               }
+       },
+
        /**
         * Returns a set of predefined style properties that should be used to represent the dataset
         * or the data if the index is specified
         * @param {number} index - data index
         * @return {IStyleInterface} style object
         */
-       getStyle: function(index) {
+       getStyle: function(index, active) {
                const me = this;
                const meta = me._cachedMeta;
                const dataset = meta.dataset;
-               let style;
 
-               if (dataset && index === undefined) {
-                       style = me._resolveDatasetElementOptions();
-               } else {
-                       index = index || 0;
-                       style = me._resolveDataElementOptions(index);
+               if (!me._config) {
+                       me._configure();
                }
 
-               if (style.fill === false || style.fill === null) {
-                       style.backgroundColor = style.borderColor;
+               const options = dataset && index === undefined
+                       ? me._resolveDatasetElementOptions(active)
+                       : me._resolveDataElementOptions(index || 0, active && 'active');
+               if (active) {
+                       me._addAutomaticHoverColors(index, options);
                }
+               return options;
+       },
+
+       _getContext(index, active) {
+               return {
+                       chart: this.chart,
+                       dataIndex: index,
+                       dataset: this.getDataset(),
+                       datasetIndex: this.index,
+                       active
+               };
 
-               return style;
        },
 
        /**
@@ -819,21 +832,19 @@ helpers.extend(DatasetController.prototype, {
                const options = chart.options.elements[me.datasetElementType.prototype._type] || {};
                const elementOptions = me._datasetElementOptions;
                const values = {};
-               const context = {
-                       chart,
-                       dataset: me.getDataset(),
-                       datasetIndex: me.index,
-                       active
-               };
-               let i, ilen, key, readKey;
+               const context = me._getContext(undefined, active);
+               let i, ilen, key, readKey, value;
 
                for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
                        key = elementOptions[i];
                        readKey = active ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
-                       values[key] = resolve([
+                       value = resolve([
                                datasetOpts[readKey],
                                options[readKey]
                        ], context);
+                       if (value !== undefined) {
+                               values[key] = value;
+                       }
                }
 
                return values;
@@ -842,72 +853,152 @@ helpers.extend(DatasetController.prototype, {
        /**
         * @private
         */
-       _resolveDataElementOptions: function(index) {
+       _resolveDataElementOptions: function(index, mode) {
                const me = this;
+               const active = mode === 'active';
                const cached = me._cachedDataOpts;
-               if (cached) {
-                       return cached;
+               if (cached[mode]) {
+                       return cached[mode];
                }
                const chart = me.chart;
                const datasetOpts = me._config;
                const options = chart.options.elements[me.dataElementType.prototype._type] || {};
                const elementOptions = me._dataElementOptions;
                const values = {};
-               const context = {
-                       chart: chart,
-                       dataIndex: index,
-                       dataset: me.getDataset(),
-                       datasetIndex: me.index
-               };
-               const info = {cacheable: true};
-               let keys, i, ilen, key;
+               const context = me._getContext(index, active);
+               const info = {cacheable: !active};
+               let keys, i, ilen, key, value, readKey;
 
                if (helpers.isArray(elementOptions)) {
                        for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
                                key = elementOptions[i];
-                               values[key] = resolve([
-                                       datasetOpts[key],
-                                       options[key]
+                               readKey = active ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
+                               value = resolve([
+                                       datasetOpts[readKey],
+                                       options[readKey]
                                ], context, index, info);
+                               if (value !== undefined) {
+                                       values[key] = value;
+                               }
                        }
                } else {
                        keys = Object.keys(elementOptions);
                        for (i = 0, ilen = keys.length; i < ilen; ++i) {
                                key = keys[i];
-                               values[key] = resolve([
-                                       datasetOpts[elementOptions[key]],
-                                       datasetOpts[key],
-                                       options[key]
+                               readKey = active ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
+                               value = resolve([
+                                       datasetOpts[elementOptions[readKey]],
+                                       datasetOpts[readKey],
+                                       options[readKey]
                                ], context, index, info);
+                               if (value !== undefined) {
+                                       values[key] = value;
+                               }
                        }
                }
 
                if (info.cacheable) {
-                       me._cachedDataOpts = Object.freeze(values);
+                       // `$shared` indicades this set of options can be shared between multiple elements.
+                       // Sharing is used to reduce number of properties to change during animation.
+                       values.$shared = true;
+
+                       // We cache options by `mode`, which can be 'active' for example. This enables us
+                       // to have the 'active' element options and 'default' options to switch between
+                       // when interacting.
+                       cached[mode] = values;
                }
 
                return values;
        },
 
-       removeHoverStyle: function(element) {
-               helpers.merge(element._model, element.$previousStyle || {});
-               delete element.$previousStyle;
+       /**
+        * @private
+        */
+       _resolveAnimations: function(index, mode, active) {
+               const me = this;
+               const chart = me.chart;
+               const cached = me._cachedAnimations;
+               mode = mode || 'default';
+
+               if (cached[mode]) {
+                       return cached[mode];
+               }
+
+               const info = {cacheable: true};
+               const context = me._getContext(index, active);
+               const datasetAnim = resolve([me._config.animation], context, index, info);
+               const chartAnim = resolve([chart.options.animation], context, index, info);
+               let config = helpers.mergeIf({}, [datasetAnim, chartAnim]);
+
+               if (active && config.active) {
+                       config = helpers.extend({}, config, config.active);
+               }
+               if (mode === 'resize' && config.resize) {
+                       config = helpers.extend({}, config, config.resize);
+               }
+
+               const animations = new Animations(chart, config);
+
+               if (info.cacheable) {
+                       cached[mode] = animations && Object.freeze(animations);
+               }
+
+               return animations;
        },
 
-       setHoverStyle: function(element, datasetIndex, index) {
-               const dataset = this.chart.data.datasets[datasetIndex];
-               const model = element._model;
-               const getHoverColor = helpers.getHoverColor;
+       /**
+        * Utility for checking if the options are shared and should be animated separately.
+        * @private
+        */
+       _getSharedOptions: function(mode, el, options) {
+               if (mode !== 'reset' && options && options.$shared && el && el.options && el.options.$shared) {
+                       return {target: el.options, options};
+               }
+       },
 
-               element.$previousStyle = {
-                       backgroundColor: model.backgroundColor,
-                       borderColor: model.borderColor,
-                       borderWidth: model.borderWidth
-               };
+       /**
+        * Utility for determining if `options` should be included in the updated properties
+        * @private
+        */
+       _includeOptions: function(mode, sharedOptions) {
+               return mode !== 'resize' && !sharedOptions;
+       },
+
+       /**
+        * Utility for updating a element with new properties, using animations when appropriate.
+        * @private
+        */
+       _updateElement: function(element, index, properties, mode) {
+               if (mode === 'reset' || mode === 'none') {
+                       helpers.extend(element, properties);
+               } else {
+                       this._resolveAnimations(index, mode).update(element, properties);
+               }
+       },
+
+       /**
+        * Utility to animate the shared options, that are potentially affecting multiple elements.
+        * @private
+        */
+       _updateSharedOptions: function(sharedOptions, mode) {
+               if (sharedOptions) {
+                       this._resolveAnimations(undefined, mode).update(sharedOptions.target, sharedOptions.options);
+               }
+       },
+
+       /**
+        * @private
+        */
+       _setStyle(element, index, mode, active) {
+               this._resolveAnimations(index, mode, active).update(element, {options: this.getStyle(index, active)});
+       },
+
+       removeHoverStyle: function(element, datasetIndex, index) {
+               this._setStyle(element, index, 'active', false);
+       },
 
-               model.backgroundColor = resolve([dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index);
-               model.borderColor = resolve([dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index);
-               model.borderWidth = resolve([dataset.hoverBorderWidth, model.borderWidth], undefined, index);
+       setHoverStyle: function(element, datasetIndex, index) {
+               this._setStyle(element, index, 'active', true);
        },
 
        /**
@@ -917,7 +1008,7 @@ helpers.extend(DatasetController.prototype, {
                const element = this._cachedMeta.dataset;
 
                if (element) {
-                       this.removeHoverStyle(element);
+                       this._setStyle(element, undefined, 'active', false);
                }
        },
 
@@ -926,24 +1017,10 @@ helpers.extend(DatasetController.prototype, {
         */
        _setDatasetHoverStyle: function() {
                const element = this._cachedMeta.dataset;
-               const prev = {};
-               let i, ilen, key, keys, hoverOptions, model;
 
-               if (!element) {
-                       return;
-               }
-
-               model = element._model;
-               hoverOptions = this._resolveDatasetElementOptions(true);
-
-               keys = Object.keys(hoverOptions);
-               for (i = 0, ilen = keys.length; i < ilen; ++i) {
-                       key = keys[i];
-                       prev[key] = model[key];
-                       model[key] = hoverOptions[key];
+               if (element) {
+                       this._setStyle(element, undefined, 'active', true);
                }
-
-               element.$previousStyle = prev;
        },
 
        /**
@@ -986,7 +1063,7 @@ helpers.extend(DatasetController.prototype, {
                }
                me._parse(start, count);
 
-               me.updateElements(data, start, count);
+               me.updateElements(data, start, count, 'reset');
        },
 
        /**
index 159e4721cd0291aec33548cba9965b613c704644..fb2e442c37f899e027b16dcd779eebac75dab280 100644 (file)
 'use strict';
 
-import color from 'chartjs-color';
-import helpers from '../helpers/index';
+import {extend, inherits} from '../helpers/helpers.core';
 import {isNumber} from '../helpers/helpers.math';
 
-function interpolate(start, view, model, ease) {
-       var keys = Object.keys(model);
-       var i, ilen, key, actual, origin, target, type, c0, c1;
-
-       for (i = 0, ilen = keys.length; i < ilen; ++i) {
-               key = keys[i];
-
-               target = model[key];
-
-               // if a value is added to the model after pivot() has been called, the view
-               // doesn't contain it, so let's initialize the view to the target value.
-               if (!Object.prototype.hasOwnProperty.call(view, key)) {
-                       view[key] = target;
-               }
-
-               actual = view[key];
-
-               if (actual === target || key[0] === '_') {
-                       continue;
-               }
-
-               if (!Object.prototype.hasOwnProperty.call(start, key)) {
-                       start[key] = actual;
-               }
-
-               origin = start[key];
-
-               type = typeof target;
-
-               if (type === typeof origin) {
-                       if (type === 'string') {
-                               c0 = color(origin);
-                               if (c0.valid) {
-                                       c1 = color(target);
-                                       if (c1.valid) {
-                                               view[key] = c1.mix(c0, ease).rgbString();
-                                               continue;
-                                       }
-                               }
-                       } else if (helpers.isFinite(origin) && helpers.isFinite(target)) {
-                               view[key] = origin + (target - origin) * ease;
-                               continue;
-                       }
-               }
-
-               view[key] = target;
-       }
-}
-
 class Element {
 
        constructor(configuration) {
-               helpers.extend(this, configuration);
+               extend(this, configuration);
 
                // this.hidden = false; we assume Element has an attribute called hidden, but do not initialize to save memory
        }
 
-       pivot(animationsDisabled) {
-               var me = this;
-               if (animationsDisabled) {
-                       me._view = me._model;
-                       return me;
-               }
-
-               if (!me._view) {
-                       me._view = helpers.extend({}, me._model);
-               }
-               me._start = {};
-               return me;
-       }
-
-       transition(ease) {
-               var me = this;
-               var model = me._model;
-               var start = me._start;
-               var view = me._view;
-
-               // No animation -> No Transition
-               if (!model || ease === 1) {
-                       // _model has to be cloned to _view
-                       // Otherwise, when _model properties are set on hover, _view.* is also set to the same value, and hover animation doesn't occur
-                       me._view = helpers.extend({}, model);
-                       me._start = null;
-                       return me;
-               }
-
-               if (!view) {
-                       view = me._view = {};
-               }
-
-               if (!start) {
-                       start = me._start = {};
-               }
-
-               interpolate(start, view, model, ease);
-
-               return me;
-       }
-
        tooltipPosition() {
                return {
-                       x: this._model.x,
-                       y: this._model.y
+                       x: this.x,
+                       y: this.y
                };
        }
 
        hasValue() {
-               return isNumber(this._model.x) && isNumber(this._model.y);
+               return isNumber(this.x) && isNumber(this.y);
        }
 }
 
-Element.extend = helpers.inherits;
+Element.extend = inherits;
 export default Element;
index f1975cd1e380bd34725cc77ffa31df6dbdf5b211..2224d72429e74aa12aa515744808e9f927deb00d 100644 (file)
@@ -33,7 +33,7 @@ function evaluateAllVisibleItems(chart, handler) {
                ({index, data} = metasets[i]);
                for (let j = 0, jlen = data.length; j < jlen; ++j) {
                        element = data[j];
-                       if (!element._view.skip) {
+                       if (!element.skip) {
                                handler(element, index, j);
                        }
                }
@@ -66,7 +66,7 @@ function evaluateItemsAtIndex(chart, axis, position, handler) {
                const metaset = metasets[i];
                const index = indices[i];
                const element = metaset.data[index];
-               if (!element._view.skip) {
+               if (!element.skip) {
                        handler(element, metaset.index, index);
                }
        }
@@ -193,7 +193,7 @@ export default {
                                const element = meta.data[index];
 
                                // don't count items that are skipped (null data)
-                               if (element && !element._view.skip) {
+                               if (element && !element.skip) {
                                        elements.push({element, datasetIndex: meta.index, index});
                                }
                        });
index cf44af0ace3fb780135d0344d96f07b14bfe7d88..1d68c20bda18163cb18ee41b131e079cbb9328e4 100644 (file)
@@ -272,20 +272,17 @@ module.exports = {
  */
 /**
  * @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.
+ * @desc Called before drawing `chart` at every animation frame. If any plugin returns `false`,
+ * the frame drawing is cancelled untilanother `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.
+ * @desc Called after the `chart` has been drawn. 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.
  */
 /**
@@ -293,7 +290,6 @@ module.exports = {
  * @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.
  */
@@ -302,7 +298,6 @@ module.exports = {
  * @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.
  */
 /**
@@ -314,7 +309,6 @@ module.exports = {
  * @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.
  */
@@ -327,7 +321,6 @@ module.exports = {
  * @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.
  */
 /**
@@ -337,7 +330,6 @@ module.exports = {
  * @param {Chart} chart - The chart instance.
  * @param {object} args - The call arguments.
  * @param {Tooltip} 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.
  */
@@ -348,7 +340,6 @@ module.exports = {
  * @param {Chart} chart - The chart instance.
  * @param {object} args - The call arguments.
  * @param {Tooltip} args.tooltip - The tooltip.
- * @param {number} args.easingValue - The current animation value, between 0.0 and 1.0.
  * @param {object} options - The plugin options.
  */
 /**
index 73122f8df50073ec709f50c5948e6e085db0a31b..eac403f9029f53488a03bd3582eed1d804c5e738 100644 (file)
@@ -3,6 +3,7 @@
 const defaults = require('./core.defaults');
 const Element = require('./core.element');
 const helpers = require('../helpers/index');
+const Animations = require('./core.animations');
 
 const valueOrDefault = helpers.valueOrDefault;
 const getRtlHelper = helpers.rtl.getRtlAdapter;
@@ -37,6 +38,18 @@ defaults._set('global', {
                displayColors: true,
                borderColor: 'rgba(0,0,0,0)',
                borderWidth: 0,
+               animation: {
+                       duration: 400,
+                       easing: 'easeOutQuart',
+                       numbers: {
+                               type: 'number',
+                               properties: ['x', 'y', 'width', 'height'],
+                       },
+                       opacity: {
+                               easing: 'linear',
+                               duration: 200
+                       }
+               },
                callbacks: {
                        // Args are: (tooltipItems, data)
                        beforeTitle: helpers.noop,
@@ -76,15 +89,14 @@ defaults._set('global', {
                        },
                        labelColor: function(tooltipItem, chart) {
                                var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
-                               var activeElement = meta.data[tooltipItem.index];
-                               var view = activeElement.$previousStyle || activeElement._view;
+                               var options = meta.controller.getStyle(tooltipItem.index);
                                return {
-                                       borderColor: view.borderColor,
-                                       backgroundColor: view.backgroundColor
+                                       borderColor: options.borderColor,
+                                       backgroundColor: options.backgroundColor
                                };
                        },
                        labelTextColor: function() {
-                               return this._options.bodyFontColor;
+                               return this.options.bodyFontColor;
                        },
                        afterLabel: helpers.noop,
 
@@ -218,90 +230,61 @@ function createTooltipItem(chart, item) {
 
 /**
  * Helper to get the reset model for the tooltip
- * @param tooltipOpts {object} the tooltip options
+ * @param options {object} the tooltip options
  */
-function getBaseModel(tooltipOpts) {
+function resolveOptions(options) {
        var globalDefaults = defaults.global;
 
-       return {
-               // Positioning
-               xPadding: tooltipOpts.xPadding,
-               yPadding: tooltipOpts.yPadding,
-               xAlign: tooltipOpts.xAlign,
-               yAlign: tooltipOpts.yAlign,
-
-               // Drawing direction and text direction
-               rtl: tooltipOpts.rtl,
-               textDirection: tooltipOpts.textDirection,
-
-               // Body
-               bodyFontColor: tooltipOpts.bodyFontColor,
-               _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
-               _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
-               _bodyAlign: tooltipOpts.bodyAlign,
-               bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
-               bodySpacing: tooltipOpts.bodySpacing,
-
-               // Title
-               titleFontColor: tooltipOpts.titleFontColor,
-               _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
-               _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
-               titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
-               _titleAlign: tooltipOpts.titleAlign,
-               titleSpacing: tooltipOpts.titleSpacing,
-               titleMarginBottom: tooltipOpts.titleMarginBottom,
-
-               // Footer
-               footerFontColor: tooltipOpts.footerFontColor,
-               _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
-               _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
-               footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
-               _footerAlign: tooltipOpts.footerAlign,
-               footerSpacing: tooltipOpts.footerSpacing,
-               footerMarginTop: tooltipOpts.footerMarginTop,
-
-               // Appearance
-               caretSize: tooltipOpts.caretSize,
-               cornerRadius: tooltipOpts.cornerRadius,
-               backgroundColor: tooltipOpts.backgroundColor,
-               opacity: 0,
-               legendColorBackground: tooltipOpts.multiKeyBackground,
-               displayColors: tooltipOpts.displayColors,
-               borderColor: tooltipOpts.borderColor,
-               borderWidth: tooltipOpts.borderWidth
-       };
+       options = helpers.extend({}, globalDefaults.tooltips, options);
+
+       options.bodyFontFamily = valueOrDefault(options.bodyFontFamily, globalDefaults.defaultFontFamily);
+       options.bodyFontStyle = valueOrDefault(options.bodyFontStyle, globalDefaults.defaultFontStyle);
+       options.bodyFontSize = valueOrDefault(options.bodyFontSize, globalDefaults.defaultFontSize);
+
+       options.titleFontFamily = valueOrDefault(options.titleFontFamily, globalDefaults.defaultFontFamily);
+       options.titleFontStyle = valueOrDefault(options.titleFontStyle, globalDefaults.defaultFontStyle);
+       options.titleFontSize = valueOrDefault(options.titleFontSize, globalDefaults.defaultFontSize);
+
+       options.footerFontFamily = valueOrDefault(options.footerFontFamily, globalDefaults.defaultFontFamily);
+       options.footerFontStyle = valueOrDefault(options.footerFontStyle, globalDefaults.defaultFontStyle);
+       options.footerFontSize = valueOrDefault(options.footerFontSize, globalDefaults.defaultFontSize);
+
+       return options;
 }
 
 /**
  * Get the size of the tooltip
  */
-function getTooltipSize(tooltip, model) {
-       var ctx = tooltip._chart.ctx;
+function getTooltipSize(tooltip) {
+       const ctx = tooltip._chart.ctx;
+       const {body, footer, options, title} = tooltip;
+       const {bodyFontSize, footerFontSize, titleFontSize} = options;
+       const titleLineCount = title.length;
+       const footerLineCount = footer.length;
 
-       var height = model.yPadding * 2; // Tooltip Padding
-       var width = 0;
+       let height = options.yPadding * 2; // Tooltip Padding
+       let width = 0;
 
        // Count of all lines in the body
-       var body = model.body;
        var combinedBodyLength = body.reduce(function(count, bodyItem) {
                return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
        }, 0);
-       combinedBodyLength += model.beforeBody.length + model.afterBody.length;
-
-       var titleLineCount = model.title.length;
-       var footerLineCount = model.footer.length;
-       var titleFontSize = model.titleFontSize;
-       var bodyFontSize = model.bodyFontSize;
-       var footerFontSize = model.footerFontSize;
-
-       height += titleLineCount * titleFontSize; // Title Lines
-       height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
-       height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
-       height += combinedBodyLength * bodyFontSize; // Body Lines
-       height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
-       height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
-       height += footerLineCount * (footerFontSize); // Footer Lines
-       height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
+       combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;
+
+       if (titleLineCount) {
+               height += titleLineCount * titleFontSize
+                       + (titleLineCount - 1) * options.titleSpacing
+                       + options.titleMarginBottom;
+       }
+       if (combinedBodyLength) {
+               height += combinedBodyLength * bodyFontSize
+                       + (combinedBodyLength - 1) * options.bodySpacing;
+       }
+       if (footerLineCount) {
+               height += options.footerMarginTop
+                       + footerLineCount * footerFontSize
+                       + (footerLineCount - 1) * options.footerSpacing;
+       }
 
        // Title width
        var widthPadding = 0;
@@ -309,15 +292,15 @@ function getTooltipSize(tooltip, model) {
                width = Math.max(width, ctx.measureText(line).width + widthPadding);
        };
 
-       ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
-       helpers.each(model.title, maxLineWidth);
+       ctx.font = helpers.fontString(titleFontSize, options.titleFontStyle, options.titleFontFamily);
+       helpers.each(tooltip.title, maxLineWidth);
 
        // Body width
-       ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
-       helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
+       ctx.font = helpers.fontString(bodyFontSize, options.bodyFontStyle, options.bodyFontFamily);
+       helpers.each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);
 
        // Body lines may include some extra width due to the color box
-       widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
+       widthPadding = options.displayColors ? (bodyFontSize + 2) : 0;
        helpers.each(body, function(bodyItem) {
                helpers.each(bodyItem.before, maxLineWidth);
                helpers.each(bodyItem.lines, maxLineWidth);
@@ -328,31 +311,27 @@ function getTooltipSize(tooltip, model) {
        widthPadding = 0;
 
        // Footer width
-       ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
-       helpers.each(model.footer, maxLineWidth);
+       ctx.font = helpers.fontString(footerFontSize, options.footerFontStyle, options.footerFontFamily);
+       helpers.each(tooltip.footer, maxLineWidth);
 
        // Add padding
-       width += 2 * model.xPadding;
+       width += 2 * options.xPadding;
 
-       return {
-               width: width,
-               height: height
-       };
+       return {width, height};
 }
 
 /**
  * Helper to get the alignment of a tooltip given the size
  */
-function determineAlignment(tooltip, size) {
-       var model = tooltip._model;
-       var chart = tooltip._chart;
+function determineAlignment(chart, options, size) {
+       const {x, y, width, height} = size;
        var chartArea = chart.chartArea;
        var xAlign = 'center';
        var yAlign = 'center';
 
-       if (model.y < size.height) {
+       if (y < height) {
                yAlign = 'top';
-       } else if (model.y > (chart.height - size.height)) {
+       } else if (y > (chart.height - height)) {
                yAlign = 'bottom';
        }
 
@@ -363,91 +342,80 @@ function determineAlignment(tooltip, size) {
        var midY = (chartArea.top + chartArea.bottom) / 2;
 
        if (yAlign === 'center') {
-               lf = function(x) {
-                       return x <= midX;
-               };
-               rf = function(x) {
-                       return x > midX;
-               };
+               lf = (value) => value <= midX;
+               rf = (value) => value > midX;
        } else {
-               lf = function(x) {
-                       return x <= (size.width / 2);
-               };
-               rf = function(x) {
-                       return x >= (chart.width - (size.width / 2));
-               };
+               lf = (value) => value <= (width / 2);
+               rf = (value) => value >= (chart.width - (width / 2));
        }
 
-       olf = function(x) {
-               return x + size.width + model.caretSize + model.caretPadding > chart.width;
-       };
-       orf = function(x) {
-               return x - size.width - model.caretSize - model.caretPadding < 0;
-       };
-       yf = function(y) {
-               return y <= midY ? 'top' : 'bottom';
-       };
+       olf = (value) => value + width + options.caretSize + options.caretPadding > chart.width;
+       orf = (value) => value - width - options.caretSize - options.caretPadding < 0;
+       yf = (value) => value <= midY ? 'top' : 'bottom';
 
-       if (lf(model.x)) {
+       if (lf(x)) {
                xAlign = 'left';
 
                // Is tooltip too wide and goes over the right side of the chart.?
-               if (olf(model.x)) {
+               if (olf(x)) {
                        xAlign = 'center';
-                       yAlign = yf(model.y);
+                       yAlign = yf(y);
                }
-       } else if (rf(model.x)) {
+       } else if (rf(x)) {
                xAlign = 'right';
 
                // Is tooltip too wide and goes outside left edge of canvas?
-               if (orf(model.x)) {
+               if (orf(x)) {
                        xAlign = 'center';
-                       yAlign = yf(model.y);
+                       yAlign = yf(y);
                }
        }
 
-       var opts = tooltip._options;
        return {
-               xAlign: opts.xAlign ? opts.xAlign : xAlign,
-               yAlign: opts.yAlign ? opts.yAlign : yAlign
+               xAlign: options.xAlign ? options.xAlign : xAlign,
+               yAlign: options.yAlign ? options.yAlign : yAlign
        };
 }
 
-/**
- * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
- */
-function getBackgroundPoint(vm, size, alignment, chart) {
-       // Background Position
-       var x = vm.x;
-       var y = vm.y;
-
-       var caretSize = vm.caretSize;
-       var caretPadding = vm.caretPadding;
-       var cornerRadius = vm.cornerRadius;
-       var xAlign = alignment.xAlign;
-       var yAlign = alignment.yAlign;
-       var paddingAndSize = caretSize + caretPadding;
-       var radiusAndPadding = cornerRadius + caretPadding;
-
+function alignX(size, xAlign, chartWidth) {
+       let {x, width} = size;
        if (xAlign === 'right') {
-               x -= size.width;
+               x -= width;
        } else if (xAlign === 'center') {
-               x -= (size.width / 2);
-               if (x + size.width > chart.width) {
-                       x = chart.width - size.width;
+               x -= (width / 2);
+               if (x + width > chartWidth) {
+                       x = chartWidth - width;
                }
                if (x < 0) {
                        x = 0;
                }
        }
+       return x;
+}
 
+function alignY(size, yAlign, paddingAndSize) {
+       let {y, height} = size;
        if (yAlign === 'top') {
                y += paddingAndSize;
        } else if (yAlign === 'bottom') {
-               y -= size.height + paddingAndSize;
+               y -= height + paddingAndSize;
        } else {
-               y -= (size.height / 2);
+               y -= (height / 2);
        }
+       return y;
+}
+
+/**
+ * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
+ */
+function getBackgroundPoint(options, size, alignment, chart) {
+       const {caretSize, caretPadding, cornerRadius} = options;
+       const {xAlign, yAlign} = alignment;
+       const paddingAndSize = caretSize + caretPadding;
+       const radiusAndPadding = cornerRadius + caretPadding;
+
+       let x = alignX(size, xAlign, chart.width);
+       let y = alignY(size, yAlign, paddingAndSize);
 
        if (yAlign === 'center') {
                if (xAlign === 'left') {
@@ -461,18 +429,16 @@ function getBackgroundPoint(vm, size, alignment, chart) {
                x += radiusAndPadding;
        }
 
-       return {
-               x: x,
-               y: y
-       };
+       return {x, y};
 }
 
-function getAlignedX(vm, align) {
+function getAlignedX(tooltip, align) {
+       const options = tooltip.options;
        return align === 'center'
-               ? vm.x + vm.width / 2
+               ? tooltip.x + tooltip.width / 2
                : align === 'right'
-                       ? vm.x + vm.width - vm.xPadding
-                       : vm.x + vm.xPadding;
+                       ? tooltip.x + tooltip.width - options.xPadding
+                       : tooltip.x + options.xPadding;
 }
 
 /**
@@ -486,36 +452,42 @@ class Tooltip extends Element {
        constructor(config) {
                super(config);
 
-               this.initialize();
+               const me = this;
+               me.opacity = 0;
+               me._active = [];
+               me._lastActive = [];
+               me.initialize();
        }
 
        initialize() {
-               var me = this;
-               me._model = getBaseModel(me._options);
-               me._view = {};
-               me._lastActive = [];
+               const me = this;
+               me.options = resolveOptions(me._chart.options.tooltips);
        }
 
-       transition(easingValue) {
-               var me = this;
-               var options = me._options;
-
-               if (me._lastEvent && me._chart.animating) {
-                       // Let's react to changes during animation
-                       me._active = me._chart.getElementsAtEventForMode(me._lastEvent, options.mode, options);
-                       me.update(true);
-                       me.pivot();
-                       me._lastActive = me.active;
+       /**
+        * @private
+        */
+       _resolveAnimations() {
+               const me = this;
+               const cached = me._cachedAnimations;
+
+               if (cached) {
+                       return cached;
                }
 
-               Element.prototype.transition.call(me, easingValue);
+               const chart = me._chart;
+               const opts = chart.options.animation && me.options.animation;
+               const animations = new Animations(me._chart, opts);
+               me._cachedAnimations = Object.freeze(animations);
+
+               return animations;
        }
 
        // Get the title
        // Args are: (tooltipItem, data)
        getTitle() {
                var me = this;
-               var opts = me._options;
+               var opts = me.options;
                var callbacks = opts.callbacks;
 
                var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
@@ -532,13 +504,13 @@ class Tooltip extends Element {
 
        // Args are: (tooltipItem, data)
        getBeforeBody() {
-               return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
+               return getBeforeAfterBodyLines(this.options.callbacks.beforeBody.apply(this, arguments));
        }
 
        // Args are: (tooltipItem, data)
        getBody(tooltipItems, data) {
                var me = this;
-               var callbacks = me._options.callbacks;
+               var callbacks = me.options.callbacks;
                var bodyItems = [];
 
                helpers.each(tooltipItems, function(tooltipItem) {
@@ -559,14 +531,14 @@ class Tooltip extends Element {
 
        // Args are: (tooltipItem, data)
        getAfterBody() {
-               return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
+               return getBeforeAfterBodyLines(this.options.callbacks.afterBody.apply(this, arguments));
        }
 
        // Get the footer and beforeFooter and afterFooter lines
        // Args are: (tooltipItem, data)
        getFooter() {
                var me = this;
-               var callbacks = me._options.callbacks;
+               var callbacks = me.options.callbacks;
 
                var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
                var footer = callbacks.footer.apply(me, arguments);
@@ -580,138 +552,114 @@ class Tooltip extends Element {
                return lines;
        }
 
-       update(changed) {
-               var me = this;
-               var opts = me._options;
-
-               // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
-               // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
-               // which breaks any animations.
-               var existingModel = me._model;
-               var model = me._model = getBaseModel(opts);
-               var active = me._active;
-
-               var data = me._data;
-
-               // In the case where active.length === 0 we need to keep these at existing values for good animations
-               var alignment = {
-                       xAlign: existingModel.xAlign,
-                       yAlign: existingModel.yAlign
-               };
-               var backgroundPoint = {
-                       x: existingModel.x,
-                       y: existingModel.y
-               };
-               var tooltipSize = {
-                       width: existingModel.width,
-                       height: existingModel.height
-               };
-               var tooltipPosition = {
-                       x: existingModel.caretX,
-                       y: existingModel.caretY
-               };
-
-               var i, len;
+       /**
+        * @private
+        */
+       _createItems() {
+               const me = this;
+               const active = me._active;
+               const options = me.options;
+               const data = me._chart.data;
+               const labelColors = [];
+               const labelTextColors = [];
+               let tooltipItems = [];
+               let i, len;
+
+               for (i = 0, len = active.length; i < len; ++i) {
+                       tooltipItems.push(createTooltipItem(me._chart, active[i]));
+               }
 
-               if (active.length) {
-                       model.opacity = 1;
+               // If the user provided a filter function, use it to modify the tooltip items
+               if (options.filter) {
+                       tooltipItems = tooltipItems.filter(function(a) {
+                               return options.filter(a, data);
+                       });
+               }
 
-                       var labelColors = [];
-                       var labelTextColors = [];
-                       tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);
+               // If the user provided a sorting function, use it to modify the tooltip items
+               if (options.itemSort) {
+                       tooltipItems = tooltipItems.sort(function(a, b) {
+                               return options.itemSort(a, b, data);
+                       });
+               }
 
-                       var tooltipItems = [];
-                       for (i = 0, len = active.length; i < len; ++i) {
-                               tooltipItems.push(createTooltipItem(me._chart, active[i]));
-                       }
+               // Determine colors for boxes
+               helpers.each(tooltipItems, function(tooltipItem) {
+                       labelColors.push(options.callbacks.labelColor.call(me, tooltipItem, me._chart));
+                       labelTextColors.push(options.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
+               });
 
-                       // If the user provided a filter function, use it to modify the tooltip items
-                       if (opts.filter) {
-                               tooltipItems = tooltipItems.filter(function(a) {
-                                       return opts.filter(a, data);
-                               });
-                       }
+               me.labelColors = labelColors;
+               me.labelTextColors = labelTextColors;
+               me.dataPoints = tooltipItems;
+               return tooltipItems;
+       }
 
-                       // If the user provided a sorting function, use it to modify the tooltip items
-                       if (opts.itemSort) {
-                               tooltipItems = tooltipItems.sort(function(a, b) {
-                                       return opts.itemSort(a, b, data);
-                               });
+       update(changed) {
+               const me = this;
+               const options = me.options;
+               const active = me._active;
+               let properties;
+
+               if (!active.length) {
+                       if (me.opacity !== 0) {
+                               properties = {
+                                       opacity: 0
+                               };
                        }
-
-                       // Determine colors for boxes
-                       helpers.each(tooltipItems, function(tooltipItem) {
-                               labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
-                               labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
-                       });
-
-
-                       // Build the Text Lines
-                       model.title = me.getTitle(tooltipItems, data);
-                       model.beforeBody = me.getBeforeBody(tooltipItems, data);
-                       model.body = me.getBody(tooltipItems, data);
-                       model.afterBody = me.getAfterBody(tooltipItems, data);
-                       model.footer = me.getFooter(tooltipItems, data);
-
-                       // Initial positioning and colors
-                       model.x = tooltipPosition.x;
-                       model.y = tooltipPosition.y;
-                       model.caretPadding = opts.caretPadding;
-                       model.labelColors = labelColors;
-                       model.labelTextColors = labelTextColors;
-
-                       // data points
-                       model.dataPoints = tooltipItems;
-
-                       // We need to determine alignment of the tooltip
-                       tooltipSize = getTooltipSize(this, model);
-                       alignment = determineAlignment(this, tooltipSize);
-                       // Final Size and Position
-                       backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
                } else {
-                       model.opacity = 0;
+                       const data = me._chart.data;
+                       const position = positioners[options.position].call(me, active, me._eventPosition);
+                       const tooltipItems = me._createItems();
+
+                       me.title = me.getTitle(tooltipItems, data);
+                       me.beforeBody = me.getBeforeBody(tooltipItems, data);
+                       me.body = me.getBody(tooltipItems, data);
+                       me.afterBody = me.getAfterBody(tooltipItems, data);
+                       me.footer = me.getFooter(tooltipItems, data);
+
+                       const size = me._size = getTooltipSize(me);
+                       const positionAndSize = helpers.extend({}, position, size);
+                       const alignment = determineAlignment(me._chart, options, positionAndSize);
+                       const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, me._chart);
+
+                       me.xAlign = alignment.xAlign;
+                       me.yAlign = alignment.yAlign;
+
+                       properties = {
+                               opacity: 1,
+                               x: backgroundPoint.x,
+                               y: backgroundPoint.y,
+                               width: size.width,
+                               height: size.height,
+                               caretX: position.x,
+                               caretY: position.y
+                       };
                }
 
-               model.xAlign = alignment.xAlign;
-               model.yAlign = alignment.yAlign;
-               model.x = backgroundPoint.x;
-               model.y = backgroundPoint.y;
-               model.width = tooltipSize.width;
-               model.height = tooltipSize.height;
-
-               // Point where the caret on the tooltip points to
-               model.caretX = tooltipPosition.x;
-               model.caretY = tooltipPosition.y;
-
-               me._model = model;
-
-               if (changed && opts.custom) {
-                       opts.custom.call(me, model);
+               if (properties) {
+                       me._resolveAnimations().update(me, properties);
                }
 
-               return me;
+               if (changed && options.custom) {
+                       options.custom.call(me);
+               }
        }
 
-       drawCaret(tooltipPoint, size) {
-               var ctx = this._chart.ctx;
-               var vm = this._view;
-               var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
+       drawCaret(tooltipPoint, ctx, size) {
+               var caretPosition = this.getCaretPosition(tooltipPoint, size);
 
                ctx.lineTo(caretPosition.x1, caretPosition.y1);
                ctx.lineTo(caretPosition.x2, caretPosition.y2);
                ctx.lineTo(caretPosition.x3, caretPosition.y3);
        }
 
-       getCaretPosition(tooltipPoint, size, vm) {
-               var x1, x2, x3, y1, y2, y3;
-               var caretSize = vm.caretSize;
-               var cornerRadius = vm.cornerRadius;
-               var xAlign = vm.xAlign;
-               var yAlign = vm.yAlign;
-               var ptX = tooltipPoint.x;
-               var ptY = tooltipPoint.y;
-               var width = size.width;
-               var height = size.height;
+       getCaretPosition(tooltipPoint, size) {
+               const {xAlign, yAlign, options} = this;
+               const {cornerRadius, caretSize} = options;
+               const {x: ptX, y: ptY} = tooltipPoint;
+               const {width, height} = size;
+               let x1, x2, x3, y1, y2, y3;
 
                if (yAlign === 'center') {
                        y2 = ptY + (height / 2);
@@ -719,117 +667,126 @@ class Tooltip extends Element {
                        if (xAlign === 'left') {
                                x1 = ptX;
                                x2 = x1 - caretSize;
-                               x3 = x1;
-
-                               y1 = y2 + caretSize;
-                               y3 = y2 - caretSize;
                        } else {
                                x1 = ptX + width;
                                x2 = x1 + caretSize;
-                               x3 = x1;
-
-                               y1 = y2 - caretSize;
-                               y3 = y2 + caretSize;
                        }
+                       x3 = x1;
+                       y1 = y2 + caretSize;
+                       y3 = y2 - caretSize;
                } else {
                        if (xAlign === 'left') {
                                x2 = ptX + cornerRadius + (caretSize);
-                               x1 = x2 - caretSize;
-                               x3 = x2 + caretSize;
                        } else if (xAlign === 'right') {
                                x2 = ptX + width - cornerRadius - caretSize;
-                               x1 = x2 - caretSize;
-                               x3 = x2 + caretSize;
                        } else {
-                               x2 = vm.caretX;
-                               x1 = x2 - caretSize;
-                               x3 = x2 + caretSize;
+                               x2 = this.caretX;
                        }
+                       x1 = x2 - caretSize;
+                       x3 = x2 + caretSize;
                        if (yAlign === 'top') {
                                y1 = ptY;
                                y2 = y1 - caretSize;
-                               y3 = y1;
                        } else {
                                y1 = ptY + height;
                                y2 = y1 + caretSize;
-                               y3 = y1;
-                               // invert drawing order
-                               var tmp = x3;
-                               x3 = x1;
-                               x1 = tmp;
                        }
+                       y3 = y1;
                }
-               return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
+               return {x1, x2, x3, y1, y2, y3};
        }
 
-       drawTitle(pt, vm, ctx) {
-               var title = vm.title;
+       drawTitle(pt, ctx) {
+               const me = this;
+               const options = me.options;
+               var title = me.title;
                var length = title.length;
                var titleFontSize, titleSpacing, i;
 
                if (length) {
-                       var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
+                       var rtlHelper = getRtlHelper(options.rtl, me.x, me.width);
 
-                       pt.x = getAlignedX(vm, vm._titleAlign);
+                       pt.x = getAlignedX(me, options.titleAlign);
 
-                       ctx.textAlign = rtlHelper.textAlign(vm._titleAlign);
+                       ctx.textAlign = rtlHelper.textAlign(options.titleAlign);
                        ctx.textBaseline = 'middle';
 
-                       titleFontSize = vm.titleFontSize;
-                       titleSpacing = vm.titleSpacing;
+                       titleFontSize = options.titleFontSize;
+                       titleSpacing = options.titleSpacing;
 
-                       ctx.fillStyle = vm.titleFontColor;
-                       ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
+                       ctx.fillStyle = options.titleFontColor;
+                       ctx.font = helpers.fontString(titleFontSize, options.titleFontStyle, options.titleFontFamily);
 
                        for (i = 0; i < length; ++i) {
                                ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2);
                                pt.y += titleFontSize + titleSpacing; // Line Height and spacing
 
                                if (i + 1 === length) {
-                                       pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
+                                       pt.y += options.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
                                }
                        }
                }
        }
 
-       drawBody(pt, vm, ctx) {
-               var bodyFontSize = vm.bodyFontSize;
-               var bodySpacing = vm.bodySpacing;
-               var bodyAlign = vm._bodyAlign;
-               var body = vm.body;
-               var drawColorBoxes = vm.displayColors;
+       _drawColorBox(ctx, pt, i, rtlHelper) {
+               const me = this;
+               const options = me.options;
+               const labelColors = me.labelColors[i];
+               const bodyFontSize = options.bodyFontSize;
+               const colorX = getAlignedX(me, 'left');
+               const rtlColorX = rtlHelper.x(colorX);
+
+               // Fill a white rect so that colours merge nicely if the opacity is < 1
+               ctx.fillStyle = options.multiKeyBackground;
+               ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
+
+               // Border
+               ctx.lineWidth = 1;
+               ctx.strokeStyle = labelColors.borderColor;
+               ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
+
+               // Inner square
+               ctx.fillStyle = labelColors.backgroundColor;
+               ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
+
+               // restore fillStyle
+               ctx.fillStyle = me.labelTextColors[i];
+       }
+
+       drawBody(pt, ctx) {
+               const me = this;
+               const {body, options} = me;
+               const {bodyFontSize, bodySpacing, bodyAlign, displayColors} = options;
                var xLinePadding = 0;
-               var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0;
 
-               var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
+               var rtlHelper = getRtlHelper(options.rtl, me.x, me.width);
 
                var fillLineOfText = function(line) {
                        ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2);
                        pt.y += bodyFontSize + bodySpacing;
                };
 
-               var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen;
                var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);
+               var bodyItem, textColor, lines, i, j, ilen, jlen;
 
                ctx.textAlign = bodyAlign;
                ctx.textBaseline = 'middle';
-               ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
+               ctx.font = helpers.fontString(bodyFontSize, options.bodyFontStyle, options.bodyFontFamily);
 
-               pt.x = getAlignedX(vm, bodyAlignForCalculation);
+               pt.x = getAlignedX(me, bodyAlignForCalculation);
 
                // Before body lines
-               ctx.fillStyle = vm.bodyFontColor;
-               helpers.each(vm.beforeBody, fillLineOfText);
+               ctx.fillStyle = options.bodyFontColor;
+               helpers.each(me.beforeBody, fillLineOfText);
 
-               xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right'
+               xLinePadding = displayColors && bodyAlignForCalculation !== 'right'
                        ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
                        : 0;
 
                // Draw body lines now
                for (i = 0, ilen = body.length; i < ilen; ++i) {
                        bodyItem = body[i];
-                       textColor = vm.labelTextColors[i];
-                       labelColors = vm.labelColors[i];
+                       textColor = me.labelTextColors[i];
 
                        ctx.fillStyle = textColor;
                        helpers.each(bodyItem.before, fillLineOfText);
@@ -837,22 +794,8 @@ class Tooltip extends Element {
                        lines = bodyItem.lines;
                        for (j = 0, jlen = lines.length; j < jlen; ++j) {
                                // Draw Legend-like boxes if needed
-                               if (drawColorBoxes) {
-                                       var rtlColorX = rtlHelper.x(colorX);
-
-                                       // Fill a white rect so that colours merge nicely if the opacity is < 1
-                                       ctx.fillStyle = vm.legendColorBackground;
-                                       ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
-
-                                       // Border
-                                       ctx.lineWidth = 1;
-                                       ctx.strokeStyle = labelColors.borderColor;
-                                       ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
-
-                                       // Inner square
-                                       ctx.fillStyle = labelColors.backgroundColor;
-                                       ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
-                                       ctx.fillStyle = textColor;
+                               if (displayColors) {
+                                       me._drawColorBox(ctx, pt, i, rtlHelper);
                                }
 
                                fillLineOfText(lines[j]);
@@ -865,67 +808,67 @@ class Tooltip extends Element {
                xLinePadding = 0;
 
                // After body lines
-               helpers.each(vm.afterBody, fillLineOfText);
+               helpers.each(me.afterBody, fillLineOfText);
                pt.y -= bodySpacing; // Remove last body spacing
        }
 
-       drawFooter(pt, vm, ctx) {
-               var footer = vm.footer;
+       drawFooter(pt, ctx) {
+               const me = this;
+               const options = me.options;
+               var footer = me.footer;
                var length = footer.length;
                var footerFontSize, i;
 
                if (length) {
-                       var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
+                       var rtlHelper = getRtlHelper(options.rtl, me.x, me.width);
 
-                       pt.x = getAlignedX(vm, vm._footerAlign);
-                       pt.y += vm.footerMarginTop;
+                       pt.x = getAlignedX(me, options.footerAlign);
+                       pt.y += options.footerMarginTop;
 
-                       ctx.textAlign = rtlHelper.textAlign(vm._footerAlign);
+                       ctx.textAlign = rtlHelper.textAlign(options.footerAlign);
                        ctx.textBaseline = 'middle';
 
-                       footerFontSize = vm.footerFontSize;
+                       footerFontSize = options.footerFontSize;
 
-                       ctx.fillStyle = vm.footerFontColor;
-                       ctx.font = helpers.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
+                       ctx.fillStyle = options.footerFontColor;
+                       ctx.font = helpers.fontString(footerFontSize, options.footerFontStyle, options.footerFontFamily);
 
                        for (i = 0; i < length; ++i) {
                                ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2);
-                               pt.y += footerFontSize + vm.footerSpacing;
+                               pt.y += footerFontSize + options.footerSpacing;
                        }
                }
        }
 
-       drawBackground(pt, vm, ctx, tooltipSize) {
-               ctx.fillStyle = vm.backgroundColor;
-               ctx.strokeStyle = vm.borderColor;
-               ctx.lineWidth = vm.borderWidth;
-               var xAlign = vm.xAlign;
-               var yAlign = vm.yAlign;
-               var x = pt.x;
-               var y = pt.y;
-               var width = tooltipSize.width;
-               var height = tooltipSize.height;
-               var radius = vm.cornerRadius;
+       drawBackground(pt, ctx, tooltipSize) {
+               const {xAlign, yAlign, options} = this;
+               const {x, y} = pt;
+               const {width, height} = tooltipSize;
+               const radius = options.cornerRadius;
+
+               ctx.fillStyle = options.backgroundColor;
+               ctx.strokeStyle = options.borderColor;
+               ctx.lineWidth = options.borderWidth;
 
                ctx.beginPath();
                ctx.moveTo(x + radius, y);
                if (yAlign === 'top') {
-                       this.drawCaret(pt, tooltipSize);
+                       this.drawCaret(pt, ctx, tooltipSize);
                }
                ctx.lineTo(x + width - radius, y);
                ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
                if (yAlign === 'center' && xAlign === 'right') {
-                       this.drawCaret(pt, tooltipSize);
+                       this.drawCaret(pt, ctx, tooltipSize);
                }
                ctx.lineTo(x + width, y + height - radius);
                ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
                if (yAlign === 'bottom') {
-                       this.drawCaret(pt, tooltipSize);
+                       this.drawCaret(pt, ctx, tooltipSize);
                }
                ctx.lineTo(x + radius, y + height);
                ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
                if (yAlign === 'center' && xAlign === 'left') {
-                       this.drawCaret(pt, tooltipSize);
+                       this.drawCaret(pt, ctx, tooltipSize);
                }
                ctx.lineTo(x, y + radius);
                ctx.quadraticCurveTo(x, y, x + radius, y);
@@ -933,56 +876,83 @@ class Tooltip extends Element {
 
                ctx.fill();
 
-               if (vm.borderWidth > 0) {
+               if (options.borderWidth > 0) {
                        ctx.stroke();
                }
        }
 
-       draw() {
-               var ctx = this._chart.ctx;
-               var vm = this._view;
+       /**
+        * Update x/y animation targets when _active elements are animating too
+        * @private
+        */
+       _updateAnimationTarget() {
+               const me = this;
+               const chart = me._chart;
+               const options = me.options;
+               const anims = me.$animations;
+               const animX = anims && anims.x;
+               const animY = anims && anims.y;
+               if (animX && animX.active() || animY && animY.active()) {
+                       const position = positioners[options.position].call(me, me._active, me._eventPosition);
+                       if (!position) {
+                               return;
+                       }
+                       const positionAndSize = helpers.extend({}, position, me._size);
+                       const alignment = determineAlignment(chart, options, positionAndSize);
+                       const point = getBackgroundPoint(options, positionAndSize, alignment, chart);
+                       if (animX._to !== point.x || animY._to !== point.y) {
+                               me._resolveAnimations().update(me, point);
+                       }
+               }
+       }
+
+       draw(ctx) {
+               const me = this;
+               const options = me.options;
+               let opacity = me.opacity;
 
-               if (vm.opacity === 0) {
+               if (!opacity) {
                        return;
                }
 
+               me._updateAnimationTarget();
+
                var tooltipSize = {
-                       width: vm.width,
-                       height: vm.height
+                       width: me.width,
+                       height: me.height
                };
                var pt = {
-                       x: vm.x,
-                       y: vm.y
+                       x: me.x,
+                       y: me.y
                };
 
                // IE11/Edge does not like very small opacities, so snap to 0
-               var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
+               opacity = Math.abs(opacity < 1e-3) ? 0 : opacity;
 
                // Truthy/falsey value for empty tooltip
-               var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
+               var hasTooltipContent = me.title.length || me.beforeBody.length || me.body.length || me.afterBody.length || me.footer.length;
 
-               if (this._options.enabled && hasTooltipContent) {
+               if (options.enabled && hasTooltipContent) {
                        ctx.save();
                        ctx.globalAlpha = opacity;
 
                        // Draw Background
-                       this.drawBackground(pt, vm, ctx, tooltipSize);
+                       me.drawBackground(pt, ctx, tooltipSize);
 
-                       // Draw Title, Body, and Footer
-                       pt.y += vm.yPadding;
+                       helpers.rtl.overrideTextDirection(ctx, options.textDirection);
 
-                       helpers.rtl.overrideTextDirection(ctx, vm.textDirection);
+                       pt.y += options.yPadding;
 
                        // Titles
-                       this.drawTitle(pt, vm, ctx);
+                       me.drawTitle(pt, ctx);
 
                        // Body
-                       this.drawBody(pt, vm, ctx);
+                       me.drawBody(pt, ctx);
 
                        // Footer
-                       this.drawFooter(pt, vm, ctx);
+                       me.drawFooter(pt, ctx);
 
-                       helpers.rtl.restoreTextDirection(ctx, vm.textDirection);
+                       helpers.rtl.restoreTextDirection(ctx, options.textDirection);
 
                        ctx.restore();
                }
@@ -996,7 +966,7 @@ class Tooltip extends Element {
         */
        handleEvent(e) {
                var me = this;
-               var options = me._options;
+               var options = me.options;
                var changed = false;
 
                me._lastActive = me._lastActive || [];
@@ -1004,12 +974,8 @@ class Tooltip extends Element {
                // Find Active Elements for tooltips
                if (e.type === 'mouseout') {
                        me._active = [];
-                       me._lastEvent = null;
                } else {
                        me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
-                       if (e.type !== 'click') {
-                               me._lastEvent = e.type === 'click' ? null : e;
-                       }
                        if (options.reverse) {
                                me._active.reverse();
                        }
@@ -1029,7 +995,7 @@ class Tooltip extends Element {
                                };
 
                                me.update(true);
-                               me.pivot();
+                               // me.pivot();
                        }
                }
 
index 000c98bc311c51a152ff3d1c7b6d55a9c4f8ce90..6d1ec447fcf8f1ed230c03adafce34b38b73382a 100644 (file)
@@ -66,13 +66,14 @@ function drawFullCircleBorders(ctx, vm, arc, inner) {
 }
 
 function drawBorder(ctx, vm, arc) {
-       var inner = vm.borderAlign === 'inner';
+       const options = vm.options;
+       var inner = options.borderAlign === 'inner';
 
        if (inner) {
-               ctx.lineWidth = vm.borderWidth * 2;
+               ctx.lineWidth = options.borderWidth * 2;
                ctx.lineJoin = 'round';
        } else {
-               ctx.lineWidth = vm.borderWidth;
+               ctx.lineWidth = options.borderWidth;
                ctx.lineJoin = 'bevel';
        }
 
@@ -98,75 +99,73 @@ class Arc extends Element {
        }
 
        inRange(chartX, chartY) {
-               var vm = this._view;
-
-               if (vm) {
-                       var pointRelativePosition = getAngleFromPoint(vm, {x: chartX, y: chartY});
-                       var angle = pointRelativePosition.angle;
-                       var distance = pointRelativePosition.distance;
-
-                       // Sanitise angle range
-                       var startAngle = vm.startAngle;
-                       var endAngle = vm.endAngle;
-                       while (endAngle < startAngle) {
-                               endAngle += TAU;
-                       }
-                       while (angle > endAngle) {
-                               angle -= TAU;
-                       }
-                       while (angle < startAngle) {
-                               angle += TAU;
-                       }
+               var me = this;
 
-                       // Check if within the range of the open/close angle
-                       var betweenAngles = (angle >= startAngle && angle <= endAngle);
-                       var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
+               var pointRelativePosition = getAngleFromPoint(me, {x: chartX, y: chartY});
+               var angle = pointRelativePosition.angle;
+               var distance = pointRelativePosition.distance;
 
-                       return (betweenAngles && withinRadius);
+               // Sanitise angle range
+               var startAngle = me.startAngle;
+               var endAngle = me.endAngle;
+               while (endAngle < startAngle) {
+                       endAngle += TAU;
+               }
+               while (angle > endAngle) {
+                       angle -= TAU;
                }
-               return false;
+               while (angle < startAngle) {
+                       angle += TAU;
+               }
+
+               // Check if within the range of the open/close angle
+               var betweenAngles = (angle >= startAngle && angle <= endAngle);
+               var withinRadius = (distance >= me.innerRadius && distance <= me.outerRadius);
+
+               return (betweenAngles && withinRadius);
        }
 
        getCenterPoint() {
-               var vm = this._view;
-               var halfAngle = (vm.startAngle + vm.endAngle) / 2;
-               var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
+               var me = this;
+               var halfAngle = (me.startAngle + me.endAngle) / 2;
+               var halfRadius = (me.innerRadius + me.outerRadius) / 2;
                return {
-                       x: vm.x + Math.cos(halfAngle) * halfRadius,
-                       y: vm.y + Math.sin(halfAngle) * halfRadius
+                       x: me.x + Math.cos(halfAngle) * halfRadius,
+                       y: me.y + Math.sin(halfAngle) * halfRadius
                };
        }
 
        tooltipPosition() {
-               var vm = this._view;
-               var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
-               var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
+               var me = this;
+               var centreAngle = me.startAngle + ((me.endAngle - me.startAngle) / 2);
+               var rangeFromCentre = (me.outerRadius - me.innerRadius) / 2 + me.innerRadius;
 
                return {
-                       x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
-                       y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
+                       x: me.x + (Math.cos(centreAngle) * rangeFromCentre),
+                       y: me.y + (Math.sin(centreAngle) * rangeFromCentre)
                };
        }
 
        draw(ctx) {
-               var vm = this._view;
-               var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
+               var me = this;
+               var options = me.options;
+               var pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
                var arc = {
-                       x: vm.x,
-                       y: vm.y,
-                       innerRadius: vm.innerRadius,
-                       outerRadius: Math.max(vm.outerRadius - pixelMargin, 0),
+                       x: me.x,
+                       y: me.y,
+                       innerRadius: me.innerRadius,
+                       outerRadius: Math.max(me.outerRadius - pixelMargin, 0),
                        pixelMargin: pixelMargin,
-                       startAngle: vm.startAngle,
-                       endAngle: vm.endAngle,
-                       fullCircles: Math.floor(vm.circumference / TAU)
+                       startAngle: me.startAngle,
+                       endAngle: me.endAngle,
+                       fullCircles: Math.floor(me.circumference / TAU)
                };
                var i;
 
                ctx.save();
 
-               ctx.fillStyle = vm.backgroundColor;
-               ctx.strokeStyle = vm.borderColor;
+               ctx.fillStyle = options.backgroundColor;
+               ctx.strokeStyle = options.borderColor;
 
                if (arc.fullCircles) {
                        arc.endAngle = arc.startAngle + TAU;
@@ -177,7 +176,7 @@ class Arc extends Element {
                        for (i = 0; i < arc.fullCircles; ++i) {
                                ctx.fill();
                        }
-                       arc.endAngle = arc.startAngle + vm.circumference % TAU;
+                       arc.endAngle = arc.startAngle + me.circumference % TAU;
                }
 
                ctx.beginPath();
@@ -186,8 +185,8 @@ class Arc extends Element {
                ctx.closePath();
                ctx.fill();
 
-               if (vm.borderWidth) {
-                       drawBorder(ctx, vm, arc);
+               if (options.borderWidth) {
+                       drawBorder(ctx, me, arc);
                }
 
                ctx.restore();
index 533b93fcaab488f525a1f112b0ccbf0a85c89b38..67bd08e1e3439b06220164d4d3f7a94857360b6f 100644 (file)
@@ -5,6 +5,7 @@ import Element from '../core/core.element';
 import helpers from '../helpers';
 
 const defaultColor = defaults.global.defaultColor;
+const isPointInArea = helpers.canvas._isPointInArea;
 
 defaults._set('global', {
        elements: {
@@ -18,48 +19,71 @@ defaults._set('global', {
                        borderDashOffset: 0.0,
                        borderJoinStyle: 'miter',
                        capBezierPoints: true,
-                       fill: true, // do we fill in the area between the line and its base axis
+                       fill: true
                }
        }
 });
 
 function startAtGap(points, spanGaps) {
        let closePath = true;
-       let previous = points.length && points[0]._view;
-       let index, view;
+       let previous = points.length && points[0];
+       let index, point;
 
        for (index = 1; index < points.length; ++index) {
                // If there is a gap in the (looping) line, start drawing from that gap
-               view = points[index]._view;
-               if (!view.skip && previous.skip) {
+               point = points[index];
+               if (!point.skip && previous.skip) {
                        points = points.slice(index).concat(points.slice(0, index));
                        closePath = spanGaps;
                        break;
                }
-               previous = view;
+               previous = point;
        }
 
        points.closePath = closePath;
        return points;
 }
 
-function setStyle(ctx, vm) {
-       ctx.lineCap = vm.borderCapStyle;
-       ctx.setLineDash(vm.borderDash);
-       ctx.lineDashOffset = vm.borderDashOffset;
-       ctx.lineJoin = vm.borderJoinStyle;
-       ctx.lineWidth = vm.borderWidth;
-       ctx.strokeStyle = vm.borderColor;
+function setStyle(ctx, options) {
+       ctx.lineCap = options.borderCapStyle;
+       ctx.setLineDash(options.borderDash);
+       ctx.lineDashOffset = options.borderDashOffset;
+       ctx.lineJoin = options.borderJoinStyle;
+       ctx.lineWidth = options.borderWidth;
+       ctx.strokeStyle = options.borderColor;
 }
 
-function normalPath(ctx, points, spanGaps, vm) {
-       const steppedLine = vm.steppedLine;
-       const lineMethod = steppedLine ? helpers.canvas._steppedLineTo : helpers.canvas._bezierCurveTo;
+function bezierCurveTo(ctx, previous, target, flip) {
+       ctx.bezierCurveTo(
+               flip ? previous.controlPointPreviousX : previous.controlPointNextX,
+               flip ? previous.controlPointPreviousY : previous.controlPointNextY,
+               flip ? target.controlPointNextX : target.controlPointPreviousX,
+               flip ? target.controlPointNextY : target.controlPointPreviousY,
+               target.x,
+               target.y);
+}
+
+function steppedLineTo(ctx, previous, target, flip, mode) {
+       if (mode === 'middle') {
+               const midpoint = (previous.x + target.x) / 2.0;
+               ctx.lineTo(midpoint, flip ? target.y : previous.y);
+               ctx.lineTo(midpoint, flip ? previous.y : target.y);
+       } else if ((mode === 'after' && !flip) || (mode !== 'after' && flip)) {
+               ctx.lineTo(previous.x, target.y);
+       } else {
+               ctx.lineTo(target.x, previous.y);
+       }
+       ctx.lineTo(target.x, target.y);
+}
+
+function normalPath(ctx, points, spanGaps, options) {
+       const steppedLine = options.steppedLine;
+       const lineMethod = steppedLine ? steppedLineTo : bezierCurveTo;
        let move = true;
        let index, currentVM, previousVM;
 
        for (index = 0; index < points.length; ++index) {
-               currentVM = points[index]._view;
+               currentVM = points[index];
 
                if (currentVM.skip) {
                        move = move || !spanGaps;
@@ -68,7 +92,7 @@ function normalPath(ctx, points, spanGaps, vm) {
                if (move) {
                        ctx.moveTo(currentVM.x, currentVM.y);
                        move = false;
-               } else if (vm.tension || steppedLine) {
+               } else if (options.tension || steppedLine) {
                        lineMethod(ctx, previousVM, currentVM, false, steppedLine);
                } else {
                        ctx.lineTo(currentVM.x, currentVM.y);
@@ -91,7 +115,7 @@ function fastPath(ctx, points, spanGaps) {
        let index, vm, truncX, x, y, prevX, minY, maxY, lastY;
 
        for (index = 0; index < points.length; ++index) {
-               vm = points[index]._view;
+               vm = points[index];
 
                // If point is skipped, we either move to next (not skipped) point
                // or line to it if spanGaps is true. `move` can already be true.
@@ -135,8 +159,64 @@ function fastPath(ctx, points, spanGaps) {
        }
 }
 
-function useFastPath(vm) {
-       return vm.tension === 0 && !vm.steppedLine && !vm.fill && !vm.borderDash.length;
+function useFastPath(options) {
+       return options.tension === 0 && !options.steppedLine && !options.fill && !options.borderDash.length;
+}
+
+function capControlPoint(pt, min, max) {
+       return Math.max(Math.min(pt, max), min);
+}
+
+function capBezierPoints(points, area) {
+       var i, ilen, model;
+       for (i = 0, ilen = points.length; i < ilen; ++i) {
+               model = points[i];
+               if (isPointInArea(model, area)) {
+                       if (i > 0 && isPointInArea(points[i - 1], area)) {
+                               model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
+                               model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
+                       }
+                       if (i < points.length - 1 && isPointInArea(points[i + 1], area)) {
+                               model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
+                               model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
+                       }
+               }
+       }
+}
+
+function updateBezierControlPoints(points, options, area, loop) {
+       var i, ilen, point, controlPoints;
+
+       // Only consider points that are drawn in case the spanGaps option is used
+       if (options.spanGaps) {
+               points = points.filter(function(pt) {
+                       return !pt.skip;
+               });
+       }
+
+       if (options.cubicInterpolationMode === 'monotone') {
+               helpers.curve.splineCurveMonotone(points);
+       } else {
+               let prev = loop ? points[points.length - 1] : points[0];
+               for (i = 0, ilen = points.length; i < ilen; ++i) {
+                       point = points[i];
+                       controlPoints = helpers.curve.splineCurve(
+                               prev,
+                               point,
+                               points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen],
+                               options.tension
+                       );
+                       point.controlPointPreviousX = controlPoints.previous.x;
+                       point.controlPointPreviousY = controlPoints.previous.y;
+                       point.controlPointNextX = controlPoints.next.x;
+                       point.controlPointNextY = controlPoints.next.y;
+                       prev = point;
+               }
+       }
+
+       if (options.capBezierPoints) {
+               capBezierPoints(points, area);
+       }
 }
 
 class Line extends Element {
@@ -145,10 +225,21 @@ class Line extends Element {
                super(props);
        }
 
-       draw(ctx) {
+       updateControlPoints(chartArea) {
                const me = this;
-               const vm = me._view;
-               const spanGaps = vm.spanGaps;
+               if (me._controlPointsUpdated) {
+                       return;
+               }
+               const options = me.options;
+               if (options.tension && !options.steppedLine) {
+                       updateBezierControlPoints(me._children, options, chartArea, me._loop);
+               }
+       }
+
+       drawPath(ctx, area) {
+               const me = this;
+               const options = me.options;
+               const spanGaps = options.spanGaps;
                let closePath = me._loop;
                let points = me._children;
 
@@ -161,19 +252,30 @@ class Line extends Element {
                        closePath = points.closePath;
                }
 
-               ctx.save();
+               if (useFastPath(options)) {
+                       fastPath(ctx, points, spanGaps);
+               } else {
+                       me.updateControlPoints(area);
+                       normalPath(ctx, points, spanGaps, options);
+               }
 
-               setStyle(ctx, vm);
+               return closePath;
+       }
 
-               ctx.beginPath();
+       draw(ctx, area) {
+               const me = this;
 
-               if (useFastPath(vm)) {
-                       fastPath(ctx, points, spanGaps);
-               } else {
-                       normalPath(ctx, points, spanGaps, vm);
+               if (!me._children.length) {
+                       return;
                }
 
-               if (closePath) {
+               ctx.save();
+
+               setStyle(ctx, me.options);
+
+               ctx.beginPath();
+
+               if (me.drawPath(ctx, area)) {
                        ctx.closePath();
                }
 
index 8de39312187ddd1a9b9c8b53b7cba0232556f35c..be92d51fbd1e1021076eaab03842abc69be5a417 100644 (file)
@@ -29,62 +29,55 @@ class Point extends Element {
        }
 
        inRange(mouseX, mouseY) {
-               const vm = this._view;
-               return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
+               const options = this.options;
+               return ((Math.pow(mouseX - this.x, 2) + Math.pow(mouseY - this.y, 2)) < Math.pow(options.hitRadius + options.radius, 2));
        }
 
        inXRange(mouseX) {
-               const vm = this._view;
-               return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
+               const options = this.options;
+               return (Math.abs(mouseX - this.x) < options.radius + options.hitRadius);
        }
 
        inYRange(mouseY) {
-               const vm = this._view;
-               return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
+               const options = this.options;
+               return (Math.abs(mouseY - this.y) < options.radius + options.hitRadius);
        }
 
        getCenterPoint() {
-               const vm = this._view;
-               return {
-                       x: vm.x,
-                       y: vm.y
-               };
+               return {x: this.x, y: this.y};
        }
 
        size() {
-               const vm = this._view;
-               const radius = vm.radius || 0;
-               const borderWidth = vm.borderWidth || 0;
+               const options = this.options || {};
+               const radius = options.radius || 0;
+               const borderWidth = radius && options.borderWidth || 0;
                return (radius + borderWidth) * 2;
        }
 
        tooltipPosition() {
-               const vm = this._view;
+               const options = this.options;
                return {
-                       x: vm.x,
-                       y: vm.y,
-                       padding: vm.radius + vm.borderWidth
+                       x: this.x,
+                       y: this.y,
+                       padding: options.radius + options.borderWidth
                };
        }
 
        draw(ctx, chartArea) {
-               const vm = this._view;
-               const pointStyle = vm.pointStyle;
-               const rotation = vm.rotation;
-               const radius = vm.radius;
-               const x = vm.x;
-               const y = vm.y;
-
-               if (vm.skip || radius <= 0) {
+               const me = this;
+               const options = me.options;
+               const radius = options.radius;
+
+               if (me.skip || radius <= 0) {
                        return;
                }
 
                // Clipping for Points.
-               if (chartArea === undefined || helpers.canvas._isPointInArea(vm, chartArea)) {
-                       ctx.strokeStyle = vm.borderColor;
-                       ctx.lineWidth = vm.borderWidth;
-                       ctx.fillStyle = vm.backgroundColor;
-                       helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
+               if (chartArea === undefined || helpers.canvas._isPointInArea(me, chartArea)) {
+                       ctx.strokeStyle = options.borderColor;
+                       ctx.lineWidth = options.borderWidth;
+                       ctx.fillStyle = options.backgroundColor;
+                       helpers.canvas.drawPoint(ctx, options.pointStyle, radius, me.x, me.y, options.rotation);
                }
        }
 }
index f77512e3af2acf290cd2d006d94525cf82e80293..29cf411d252f9fd29415e68a37e49bd6ffdecd40 100644 (file)
@@ -17,31 +17,27 @@ defaults._set('global', {
        }
 });
 
-function isVertical(vm) {
-       return vm && vm.width !== undefined;
-}
-
 /**
  * Helper function to get the bounds of the bar regardless of the orientation
  * @param bar {Chart.Element.Rectangle} the bar
  * @return {Bounds} bounds of the bar
  * @private
  */
-function getBarBounds(vm) {
+function getBarBounds(bar) {
        var x1, x2, y1, y2, half;
 
-       if (isVertical(vm)) {
-               half = vm.width / 2;
-               x1 = vm.x - half;
-               x2 = vm.x + half;
-               y1 = Math.min(vm.y, vm.base);
-               y2 = Math.max(vm.y, vm.base);
+       if (bar.horizontal) {
+               half = bar.height / 2;
+               x1 = Math.min(bar.x, bar.base);
+               x2 = Math.max(bar.x, bar.base);
+               y1 = bar.y - half;
+               y2 = bar.y + half;
        } else {
-               half = vm.height / 2;
-               x1 = Math.min(vm.x, vm.base);
-               x2 = Math.max(vm.x, vm.base);
-               y1 = vm.y - half;
-               y2 = vm.y + half;
+               half = bar.width / 2;
+               x1 = bar.x - half;
+               x2 = bar.x + half;
+               y1 = Math.min(bar.y, bar.base);
+               y2 = Math.max(bar.y, bar.base);
        }
 
        return {
@@ -56,19 +52,19 @@ function swap(orig, v1, v2) {
        return orig === v1 ? v2 : orig === v2 ? v1 : orig;
 }
 
-function parseBorderSkipped(vm) {
-       var edge = vm.borderSkipped;
+function parseBorderSkipped(bar) {
+       var edge = bar.options.borderSkipped;
        var res = {};
 
        if (!edge) {
                return res;
        }
 
-       if (vm.horizontal) {
-               if (vm.base > vm.x) {
+       if (bar.horizontal) {
+               if (bar.base > bar.x) {
                        edge = swap(edge, 'left', 'right');
                }
-       } else if (vm.base < vm.y) {
+       } else if (bar.base < bar.y) {
                edge = swap(edge, 'bottom', 'top');
        }
 
@@ -76,9 +72,9 @@ function parseBorderSkipped(vm) {
        return res;
 }
 
-function parseBorderWidth(vm, maxW, maxH) {
-       var value = vm.borderWidth;
-       var skip = parseBorderSkipped(vm);
+function parseBorderWidth(bar, maxW, maxH) {
+       var value = bar.options.borderWidth;
+       var skip = parseBorderSkipped(bar);
        var t, r, b, l;
 
        if (helpers.isObject(value)) {
@@ -98,11 +94,11 @@ function parseBorderWidth(vm, maxW, maxH) {
        };
 }
 
-function boundingRects(vm) {
-       var bounds = getBarBounds(vm);
+function boundingRects(bar) {
+       var bounds = getBarBounds(bar);
        var width = bounds.right - bounds.left;
        var height = bounds.bottom - bounds.top;
-       var border = parseBorderWidth(vm, width / 2, height / 2);
+       var border = parseBorderWidth(bar, width / 2, height / 2);
 
        return {
                outer: {
@@ -120,10 +116,10 @@ function boundingRects(vm) {
        };
 }
 
-function inRange(vm, x, y) {
+function inRange(bar, x, y) {
        var skipX = x === null;
        var skipY = y === null;
-       var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm);
+       var bounds = !bar || (skipX && skipY) ? false : getBarBounds(bar);
 
        return bounds
                && (skipX || x >= bounds.left && x <= bounds.right)
@@ -137,12 +133,12 @@ class Rectangle extends Element {
        }
 
        draw(ctx) {
-               var vm = this._view;
-               var rects = boundingRects(vm);
+               var options = this.options;
+               var rects = boundingRects(this);
                var outer = rects.outer;
                var inner = rects.inner;
 
-               ctx.fillStyle = vm.backgroundColor;
+               ctx.fillStyle = options.backgroundColor;
                ctx.fillRect(outer.x, outer.y, outer.w, outer.h);
 
                if (outer.w === inner.w && outer.h === inner.h) {
@@ -153,43 +149,36 @@ class Rectangle extends Element {
                ctx.beginPath();
                ctx.rect(outer.x, outer.y, outer.w, outer.h);
                ctx.clip();
-               ctx.fillStyle = vm.borderColor;
+               ctx.fillStyle = options.borderColor;
                ctx.rect(inner.x, inner.y, inner.w, inner.h);
                ctx.fill('evenodd');
                ctx.restore();
        }
 
        inRange(mouseX, mouseY) {
-               return inRange(this._view, mouseX, mouseY);
+               return inRange(this, mouseX, mouseY);
        }
 
        inXRange(mouseX) {
-               return inRange(this._view, mouseX, null);
+               return inRange(this, mouseX, null);
        }
 
        inYRange(mouseY) {
-               return inRange(this._view, null, mouseY);
+               return inRange(this, null, mouseY);
        }
 
        getCenterPoint() {
-               var vm = this._view;
-               var x, y;
-               if (isVertical(vm)) {
-                       x = vm.x;
-                       y = (vm.y + vm.base) / 2;
-               } else {
-                       x = (vm.x + vm.base) / 2;
-                       y = vm.y;
-               }
-
-               return {x: x, y: y};
+               const {x, y, base, horizontal} = this;
+               return {
+                       x: horizontal ? (x + base) / 2 : x,
+                       y: horizontal ? y : (y + base) / 2
+               };
        }
 
        tooltipPosition() {
-               var vm = this._view;
                return {
-                       x: vm.x,
-                       y: vm.y
+                       x: this.x,
+                       y: this.y
                };
        }
 }
index 292e27740e65c42b3132baadcabd53b322db4349..d4bf75823bc6d811592b5edd11d889104ce150fa 100644 (file)
@@ -45,7 +45,7 @@ export function splineCurveMonotone(points) {
 
        var pointsWithTangents = (points || []).map(function(point) {
                return {
-                       model: point._model,
+                       model: point,
                        deltaK: 0,
                        mK: 0
                };
index 92ed94caa2650f8b794265038c7b5bceb72f4e48..a3101d0f8095c27e76e575674c581d2eddf4a8d3 100644 (file)
@@ -6,6 +6,7 @@ var Chart = require('./core/core.controller');
 Chart.helpers = require('./helpers/index');
 Chart._adapters = require('./core/core.adapters');
 Chart.Animation = require('./core/core.animation');
+Chart.Animator = require('./core/core.animator');
 Chart.animationService = require('./core/core.animations');
 Chart.controllers = require('./controllers/index');
 Chart.DatasetController = require('./core/core.datasetController');
index b2487e35b01faaa5d6284d9b65a35ce88e682a04..2ddda44de7ba9ed51157f1191886bfea81c3d095 100644 (file)
@@ -28,7 +28,7 @@ var mappers = {
                var length = points.length || 0;
 
                return !length ? null : function(point, i) {
-                       return (i < length && points[i]._view) || null;
+                       return (i < length && points[i]) || null;
                };
        },
 
@@ -55,7 +55,7 @@ var mappers = {
 
 // @todo if (fill[0] === '#')
 function decodeFill(el, index, count) {
-       var model = el._model || {};
+       var model = el.options || {};
        var fillOption = model.fill;
        var fill = fillOption && typeof fillOption.target !== 'undefined' ? fillOption.target : fillOption;
        var target;
@@ -105,7 +105,7 @@ function decodeFill(el, index, count) {
 }
 
 function computeLinearBoundary(source) {
-       var model = source.el._model || {};
+       var model = source.el || {};
        var scale = source.scale || {};
        var fill = source.fill;
        var target = null;
@@ -352,11 +352,11 @@ function clipAndFill(ctx, clippingPointsSets, fillingPointsSets, color, stepped,
 
 function doFill(ctx, points, mapper, colors, el, area) {
        const count = points.length;
-       const view = el._view;
+       const options = el.options;
        const loop = el._loop;
-       const span = view.spanGaps;
-       const stepped = view.steppedLine;
-       const tension = view.tension;
+       const span = options.spanGaps;
+       const stepped = options.steppedLine;
+       const tension = options.tension;
        let curve0 = [];
        let curve1 = [];
        let len0 = 0;
@@ -369,8 +369,8 @@ function doFill(ctx, points, mapper, colors, el, area) {
 
        for (i = 0, ilen = count; i < ilen; ++i) {
                index = i % count;
-               p0 = points[index]._view;
-               p1 = mapper(p0, index, view);
+               p0 = points[index];
+               p1 = mapper(p0, index);
                d0 = isDrawable(p0);
                d1 = isDrawable(p1);
 
@@ -423,7 +423,7 @@ module.exports = {
                        el = meta.dataset;
                        source = null;
 
-                       if (el && el._model && el instanceof elements.Line) {
+                       if (el && el.options && el instanceof elements.Line) {
                                source = {
                                        visible: chart.isDatasetVisible(i),
                                        fill: decodeFill(el, i, count),
@@ -450,9 +450,19 @@ module.exports = {
        },
 
        beforeDatasetsDraw: function(chart) {
-               var metasets = chart._getSortedVisibleDatasetMetas();
-               var ctx = chart.ctx;
-               var meta, i, el, view, points, mapper, color, colors, fillOption;
+               const metasets = chart._getSortedVisibleDatasetMetas();
+               const area = chart.chartArea;
+               const ctx = chart.ctx;
+               var meta, i, el, options, points, mapper, color, colors, fillOption;
+
+               for (i = metasets.length - 1; i >= 0; --i) {
+                       meta = metasets[i].$filler;
+
+                       if (!meta || !meta.visible) {
+                               continue;
+                       }
+                       meta.el.updateControlPoints(area);
+               }
 
                for (i = metasets.length - 1; i >= 0; --i) {
                        meta = metasets[i].$filler;
@@ -462,11 +472,11 @@ module.exports = {
                        }
 
                        el = meta.el;
-                       view = el._view;
+                       options = el.options;
                        points = el._children || [];
                        mapper = meta.mapper;
-                       fillOption = meta.el._model.fill;
-                       color = view.backgroundColor || defaults.global.defaultColor;
+                       fillOption = options.fill;
+                       color = options.backgroundColor || defaults.global.defaultColor;
 
                        colors = {above: color, below: color};
                        if (fillOption && typeof fillOption === 'object') {
@@ -474,8 +484,8 @@ module.exports = {
                                colors.below = fillOption.below || color;
                        }
                        if (mapper && points.length) {
-                               helpers.canvas.clipArea(ctx, chart.chartArea);
-                               doFill(ctx, points, mapper, colors, el, chart.chartArea);
+                               helpers.canvas.clipArea(ctx, area);
+                               doFill(ctx, points, mapper, colors, el, area);
                                helpers.canvas.unclipArea(ctx);
                        }
                }
index 5913809e3c6e89ca7e3b09e0bf5516f2930e8775..12f71afd9338edcb7119497a2101181061dfa1d6 100644 (file)
@@ -58,7 +58,7 @@ defaults._set('global', {
                                        return {
                                                text: datasets[meta.index].label,
                                                fillStyle: style.backgroundColor,
-                                               hidden: !chart.isDatasetVisible(meta.index),
+                                               hidden: !meta.visible,
                                                lineCap: style.borderCapStyle,
                                                lineDash: style.borderDash,
                                                lineDashOffset: style.borderDashOffset,
index 651e1a7d3c913fe501852cbb602fa78834e2d358..d5cbf2bfe11bb0b7d0bcedcb0f1becc84beb9879 100644 (file)
Binary files a/test/fixtures/controller.line/clip/default-y-max.png and b/test/fixtures/controller.line/clip/default-y-max.png differ
index 923cf75aa8731fede7eb4634742f047d5db075f5..bd5d96c5eaac8120a1d7f19b074bf9679abb1c89 100644 (file)
Binary files a/test/fixtures/controller.line/clip/default-y.png and b/test/fixtures/controller.line/clip/default-y.png differ
index fc63d3740aebf71d0fe880feb0fecef133f841cb..12652cc90df370adb6a92c299db6dd70387bfd3f 100644 (file)
@@ -82,14 +82,13 @@ module.exports = {
                                                event = {
                                                        type: 'mousemove',
                                                        target: canvas,
-                                                       clientX: rect.left + point._model.x,
-                                                       clientY: rect.top + point._model.y
+                                                       clientX: rect.left + point.x,
+                                                       clientY: rect.top + point.y
                                                };
                                                chart.handleEvent(event);
                                                chart.tooltip.handleEvent(event);
-                                               chart.tooltip.transition(1);
-                                               chart.tooltip._view.opacity = j / 10;
-                                               chart.tooltip.draw();
+                                               chart.tooltip.opacity = j / 10;
+                                               chart.tooltip.draw(chart.ctx);
                                        }
                                }
                        }
index e6d199452f5e53012e025cca57a08401f8617628..0ec56a7a9db497bc15283138391007aacbf6e7d0 100644 (file)
@@ -728,11 +728,11 @@ describe('Chart.controllers.bar', function() {
                        {x: 89, y: 512},
                        {x: 217, y: 0}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
-                       expect(meta.data[i]._model.base).toBeCloseToPixel(1024);
-                       expect(meta.data[i]._model.width).toBeCloseToPixel(46);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(expected.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(expected.y);
+                       expect(meta.data[i].base).toBeCloseToPixel(1024);
+                       expect(meta.data[i].width).toBeCloseToPixel(46);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'red',
                                borderSkipped: 'top',
                                borderColor: 'blue',
@@ -785,10 +785,10 @@ describe('Chart.controllers.bar', function() {
                var bar1 = meta.data[0];
                var bar2 = meta.data[1];
 
-               expect(bar1._model.x).toBeCloseToPixel(179);
-               expect(bar1._model.y).toBeCloseToPixel(114);
-               expect(bar2._model.x).toBeCloseToPixel(435);
-               expect(bar2._model.y).toBeCloseToPixel(0);
+               expect(bar1.x).toBeCloseToPixel(179);
+               expect(bar1.y).toBeCloseToPixel(114);
+               expect(bar2.x).toBeCloseToPixel(435);
+               expect(bar2.y).toBeCloseToPixel(0);
        });
 
        it('should update elements when the scales are stacked', function() {
@@ -829,10 +829,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 293, w: 92 / 2, x: 295, y: 146},
                        {b: 293, w: 92 / 2, x: 422, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta0.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta0.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -843,10 +843,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 146, w: 92 / 2, x: 345, y: 146},
                        {b: 439, w: 92 / 2, x: 473, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta1.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta1.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -890,10 +890,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 1024, w: 92 / 2, x: 294, y: 922},
                        {b: 1024, w: 92 / 2, x: 422.5, y: 0}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta0.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta0.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -904,10 +904,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 922, w: 92 / 2, x: 345, y: 0},
                        {b: 0, w: 92 / 2, x: 473.5, y: 0}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta1.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta1.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -949,10 +949,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 293, w: 92, x: 320, y: 146},
                        {b: 293, w: 92, x: 448, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta0.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta0.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -963,10 +963,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 293, w: 92, x: 320, y: 293},
                        {b: 293, w: 92, x: 448, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta1.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta1.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -1008,10 +1008,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 293, w: 92 / 2, x: 295, y: 146},
                        {b: 293, w: 92 / 2, x: 422, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta0.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta0.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -1022,10 +1022,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 146, w: 92 / 2, x: 345, y: 146},
                        {b: 439, w: 92 / 2, x: 473, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta1.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta1.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -1069,10 +1069,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 293, w: 92, x: 320, y: 146},
                        {b: 293, w: 92, x: 448, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta0.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta0.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta = chart.getDatasetMeta(1);
@@ -1083,10 +1083,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 146, w: 92, x: 320, y: 146},
                        {b: 439, w: 92, x: 448, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta.data[i]._model.width).toBeCloseToPixel(values.w);
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta.data[i].width).toBeCloseToPixel(values.w);
+                       expect(meta.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -1126,10 +1126,10 @@ describe('Chart.controllers.bar', function() {
                        {x: 89, y: 256},
                        {x: 217, y: 0}
                ].forEach(function(values, i) {
-                       expect(meta.data[i]._model.base).toBeCloseToPixel(512);
-                       expect(meta.data[i]._model.width).toBeCloseToPixel(46);
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta.data[i].base).toBeCloseToPixel(512);
+                       expect(meta.data[i].width).toBeCloseToPixel(46);
+                       expect(meta.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -1172,10 +1172,10 @@ describe('Chart.controllers.bar', function() {
                        {b: 384, x: 89, y: 256},
                        {b: 256, x: 217, y: 0}
                ].forEach(function(values, i) {
-                       expect(meta.data[i]._model.base).toBeCloseToPixel(values.b);
-                       expect(meta.data[i]._model.width).toBeCloseToPixel(46);
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta.data[i].base).toBeCloseToPixel(values.b);
+                       expect(meta.data[i].width).toBeCloseToPixel(46);
+                       expect(meta.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(values.y);
                });
        });
 
@@ -1235,29 +1235,31 @@ describe('Chart.controllers.bar', function() {
                var bar = meta.data[0];
 
                meta.controller.setHoverStyle(bar, 1, 0);
-               expect(bar._model.backgroundColor).toBe('rgb(230, 0, 0)');
-               expect(bar._model.borderColor).toBe('rgb(0, 0, 230)');
-               expect(bar._model.borderWidth).toBe(2);
+               expect(bar.options.backgroundColor).toBe('rgb(230, 0, 0)');
+               expect(bar.options.borderColor).toBe('rgb(0, 0, 230)');
+               expect(bar.options.borderWidth).toBe(2);
 
                // Set a dataset style
                chart.data.datasets[1].hoverBackgroundColor = 'rgb(128, 128, 128)';
                chart.data.datasets[1].hoverBorderColor = 'rgb(0, 0, 0)';
                chart.data.datasets[1].hoverBorderWidth = 5;
+               chart.update();
 
                meta.controller.setHoverStyle(bar, 1, 0);
-               expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
-               expect(bar._model.borderColor).toBe('rgb(0, 0, 0)');
-               expect(bar._model.borderWidth).toBe(5);
+               expect(bar.options.backgroundColor).toBe('rgb(128, 128, 128)');
+               expect(bar.options.borderColor).toBe('rgb(0, 0, 0)');
+               expect(bar.options.borderWidth).toBe(5);
 
                // Should work with array styles so that we can set per bar
                chart.data.datasets[1].hoverBackgroundColor = ['rgb(255, 255, 255)', 'rgb(128, 128, 128)'];
                chart.data.datasets[1].hoverBorderColor = ['rgb(9, 9, 9)', 'rgb(0, 0, 0)'];
                chart.data.datasets[1].hoverBorderWidth = [2.5, 5];
+               chart.update();
 
                meta.controller.setHoverStyle(bar, 1, 0);
-               expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
-               expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
-               expect(bar._model.borderWidth).toBe(2.5);
+               expect(bar.options.backgroundColor).toBe('rgb(255, 255, 255)');
+               expect(bar.options.borderColor).toBe('rgb(9, 9, 9)');
+               expect(bar.options.borderWidth).toBe(2.5);
        });
 
        it('should remove a hover style from a bar', function() {
@@ -1293,17 +1295,17 @@ describe('Chart.controllers.bar', function() {
                chart.options.elements.rectangle.borderWidth = 3.14;
 
                chart.update();
-               expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
-               expect(bar._model.borderColor).toBe('rgb(15, 15, 15)');
-               expect(bar._model.borderWidth).toBe(3.14);
+               expect(bar.options.backgroundColor).toBe('rgb(128, 128, 128)');
+               expect(bar.options.borderColor).toBe('rgb(15, 15, 15)');
+               expect(bar.options.borderWidth).toBe(3.14);
                meta.controller.setHoverStyle(bar, 1, 0);
-               expect(bar._model.backgroundColor).toBe(helpers.getHoverColor('rgb(128, 128, 128)'));
-               expect(bar._model.borderColor).toBe(helpers.getHoverColor('rgb(15, 15, 15)'));
-               expect(bar._model.borderWidth).toBe(3.14);
+               expect(bar.options.backgroundColor).toBe(helpers.getHoverColor('rgb(128, 128, 128)'));
+               expect(bar.options.borderColor).toBe(helpers.getHoverColor('rgb(15, 15, 15)'));
+               expect(bar.options.borderWidth).toBe(3.14);
                meta.controller.removeHoverStyle(bar);
-               expect(bar._model.backgroundColor).toBe('rgb(128, 128, 128)');
-               expect(bar._model.borderColor).toBe('rgb(15, 15, 15)');
-               expect(bar._model.borderWidth).toBe(3.14);
+               expect(bar.options.backgroundColor).toBe('rgb(128, 128, 128)');
+               expect(bar.options.borderColor).toBe('rgb(15, 15, 15)');
+               expect(bar.options.borderWidth).toBe(3.14);
 
                // Should work with array styles so that we can set per bar
                chart.data.datasets[1].backgroundColor = ['rgb(255, 255, 255)', 'rgb(128, 128, 128)'];
@@ -1311,17 +1313,17 @@ describe('Chart.controllers.bar', function() {
                chart.data.datasets[1].borderWidth = [2.5, 5];
 
                chart.update();
-               expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
-               expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
-               expect(bar._model.borderWidth).toBe(2.5);
+               expect(bar.options.backgroundColor).toBe('rgb(255, 255, 255)');
+               expect(bar.options.borderColor).toBe('rgb(9, 9, 9)');
+               expect(bar.options.borderWidth).toBe(2.5);
                meta.controller.setHoverStyle(bar, 1, 0);
-               expect(bar._model.backgroundColor).toBe(helpers.getHoverColor('rgb(255, 255, 255)'));
-               expect(bar._model.borderColor).toBe(helpers.getHoverColor('rgb(9, 9, 9)'));
-               expect(bar._model.borderWidth).toBe(2.5);
+               expect(bar.options.backgroundColor).toBe(helpers.getHoverColor('rgb(255, 255, 255)'));
+               expect(bar.options.borderColor).toBe(helpers.getHoverColor('rgb(9, 9, 9)'));
+               expect(bar.options.borderWidth).toBe(2.5);
                meta.controller.removeHoverStyle(bar);
-               expect(bar._model.backgroundColor).toBe('rgb(255, 255, 255)');
-               expect(bar._model.borderColor).toBe('rgb(9, 9, 9)');
-               expect(bar._model.borderWidth).toBe(2.5);
+               expect(bar.options.backgroundColor).toBe('rgb(255, 255, 255)');
+               expect(bar.options.borderColor).toBe('rgb(9, 9, 9)');
+               expect(bar.options.borderWidth).toBe(2.5);
        });
 
        describe('Bar width', function() {
@@ -1351,7 +1353,7 @@ describe('Chart.controllers.bar', function() {
                        for (var i = 0; i < chart.data.datasets.length; i++) {
                                var bars = chart.getDatasetMeta(i).data;
                                for (var j = xScale.min; j <= xScale.max; j++) {
-                                       totalBarWidth += bars[j]._model.width;
+                                       totalBarWidth += bars[j].width;
                                }
                                if (stacked) {
                                        break;
@@ -1425,7 +1427,7 @@ describe('Chart.controllers.bar', function() {
                        for (var i = 0; i < chart.data.datasets.length; i++) {
                                var bars = chart.getDatasetMeta(i).data;
                                for (var j = yScale.min; j <= yScale.max; j++) {
-                                       totalBarHeight += bars[j]._model.height;
+                                       totalBarHeight += bars[j].height;
                                }
                                if (stacked) {
                                        break;
@@ -1525,8 +1527,8 @@ describe('Chart.controllers.bar', function() {
 
                                        for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
                                                meta = chart.getDatasetMeta(i);
-                                               expect(meta.data[0]._model.width).toBeCloseToPixel(expected);
-                                               expect(meta.data[1]._model.width).toBeCloseToPixel(expected);
+                                               expect(meta.data[0].width).toBeCloseToPixel(expected);
+                                               expect(meta.data[1].width).toBeCloseToPixel(expected);
                                        }
                                });
 
@@ -1540,8 +1542,8 @@ describe('Chart.controllers.bar', function() {
 
                                        for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
                                                meta = chart.getDatasetMeta(i);
-                                               expect(meta.data[0]._model.width).toBeCloseToPixel(10);
-                                               expect(meta.data[1]._model.width).toBeCloseToPixel(10);
+                                               expect(meta.data[0].width).toBeCloseToPixel(10);
+                                               expect(meta.data[1].width).toBeCloseToPixel(10);
                                        }
                                });
                        });
@@ -1562,8 +1564,8 @@ describe('Chart.controllers.bar', function() {
 
                var data = chart.getDatasetMeta(0).data;
 
-               expect(data[0]._model.base - minBarLength).toEqual(data[0]._model.y);
-               expect(data[1]._model.base + minBarLength).toEqual(data[1]._model.y);
+               expect(data[0].base - minBarLength).toEqual(data[0].y);
+               expect(data[1].base + minBarLength).toEqual(data[1].y);
        });
 
        it('minBarLength settings should be used on X axis on horizontalBar chart', function() {
@@ -1580,7 +1582,7 @@ describe('Chart.controllers.bar', function() {
 
                var data = chart.getDatasetMeta(0).data;
 
-               expect(data[0]._model.base + minBarLength).toEqual(data[0]._model.x);
-               expect(data[1]._model.base - minBarLength).toEqual(data[1]._model.x);
+               expect(data[0].base + minBarLength).toEqual(data[0].x);
+               expect(data[1].base - minBarLength).toEqual(data[1].x);
        });
 });
index 9de15b1b812e24baa6fcb4411c90935216095cd8..8f4211423e357d43962d42a4ff0845cbe5996682 100644 (file)
@@ -138,15 +138,14 @@ describe('Chart.controllers.bubble', function() {
                        {r: 2, x: 341, y: 486},
                        {r: 1, x: 512, y: 0}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.radius).toBe(expected.r);
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(expected.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(expected.y);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: Chart.defaults.global.defaultColor,
                                borderColor: Chart.defaults.global.defaultColor,
                                borderWidth: 1,
                                hitRadius: 1,
-                               skip: false
+                               radius: expected.r
                        }));
                });
 
@@ -162,12 +161,11 @@ describe('Chart.controllers.bubble', function() {
                chart.update();
 
                for (var i = 0; i < 4; ++i) {
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'rgb(98, 98, 98)',
                                borderColor: 'rgb(8, 8, 8)',
                                borderWidth: 0.55,
-                               hitRadius: 3.3,
-                               skip: false
+                               hitRadius: 3.3
                        }));
                }
        });
@@ -295,16 +293,16 @@ describe('Chart.controllers.bubble', function() {
                        var point = chart.getDatasetMeta(0).data[0];
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(49, 135, 221)');
-                       expect(point._model.borderColor).toBe('rgb(22, 89, 156)');
-                       expect(point._model.borderWidth).toBe(1);
-                       expect(point._model.radius).toBe(20 + 4);
+                       expect(point.options.backgroundColor).toBe('rgb(49, 135, 221)');
+                       expect(point.options.borderColor).toBe('rgb(22, 89, 156)');
+                       expect(point.options.borderWidth).toBe(1);
+                       expect(point.options.radius).toBe(20 + 4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(20);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(20);
                });
 
                it ('should handle hover styles defined via dataset properties', function() {
@@ -321,16 +319,16 @@ describe('Chart.controllers.bubble', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(point._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(point._model.borderWidth).toBe(8.4);
-                       expect(point._model.radius).toBe(20 + 4.2);
+                       expect(point.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(point.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(point.options.borderWidth).toBe(8.4);
+                       expect(point.options.radius).toBe(20 + 4.2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(20);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(20);
                });
 
                it ('should handle hover styles defined via element options', function() {
@@ -347,16 +345,16 @@ describe('Chart.controllers.bubble', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(point._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(point._model.borderWidth).toBe(8.4);
-                       expect(point._model.radius).toBe(20 + 4.2);
+                       expect(point.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(point.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(point.options.borderWidth).toBe(8.4);
+                       expect(point.options.radius).toBe(20 + 4.2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(20);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(20);
                });
        });
 });
index 328d5dab85efbacaf2385d938adc2fb7167d8c3c..a1eaf71c8596ef50b9aa7ba66e9d981c5ca404a9 100644 (file)
@@ -78,6 +78,7 @@ describe('Chart.controllers.doughnut', function() {
                                legend: false,
                                title: false,
                                animation: {
+                                       duration: 0,
                                        animateRotate: true,
                                        animateScale: false
                                },
@@ -106,14 +107,14 @@ describe('Chart.controllers.doughnut', function() {
                        {c: 0},
                        {c: 0}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(192);
-                       expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
-                               startAngle: Math.PI * -0.5,
-                               endAngle: Math.PI * -0.5,
+                       expect(meta.data[i].x).toBeCloseToPixel(256);
+                       expect(meta.data[i].y).toBeCloseToPixel(256);
+                       expect(meta.data[i].outerRadius).toBeCloseToPixel(256);
+                       expect(meta.data[i].innerRadius).toBeCloseToPixel(192);
+                       expect(meta.data[i].circumference).toBeCloseTo(expected.c, 8);
+                       expect(meta.data[i].startAngle).toBeCloseToPixel(Math.PI * -0.5);
+                       expect(meta.data[i].endAngle).toBeCloseToPixel(Math.PI * -0.5);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'rgb(255, 0, 0)',
                                borderColor: 'rgb(0, 0, 255)',
                                borderWidth: 2
@@ -128,14 +129,14 @@ describe('Chart.controllers.doughnut', function() {
                        {c: 0, s: 2.2689280275, e: 2.2689280275},
                        {c: 2.4434609527, s: 2.2689280275, e: 4.7123889803}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(192);
-                       expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
-                       expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
-                       expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(256);
+                       expect(meta.data[i].y).toBeCloseToPixel(256);
+                       expect(meta.data[i].outerRadius).toBeCloseToPixel(256);
+                       expect(meta.data[i].innerRadius).toBeCloseToPixel(192);
+                       expect(meta.data[i].circumference).toBeCloseTo(expected.c, 8);
+                       expect(meta.data[i].startAngle).toBeCloseTo(expected.s, 8);
+                       expect(meta.data[i].endAngle).toBeCloseTo(expected.e, 8);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'rgb(255, 0, 0)',
                                borderColor: 'rgb(0, 0, 255)',
                                borderWidth: 2
@@ -200,13 +201,13 @@ describe('Chart.controllers.doughnut', function() {
                        {c: Math.PI / 8, s: Math.PI, e: Math.PI + Math.PI / 8},
                        {c: 3 * Math.PI / 8, s: Math.PI + Math.PI / 8, e: Math.PI + Math.PI / 2}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(512);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(512);
-                       expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(512);
-                       expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(384);
-                       expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
-                       expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
-                       expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
+                       expect(meta.data[i].x).toBeCloseToPixel(512);
+                       expect(meta.data[i].y).toBeCloseToPixel(512);
+                       expect(meta.data[i].outerRadius).toBeCloseToPixel(512);
+                       expect(meta.data[i].innerRadius).toBeCloseToPixel(384);
+                       expect(meta.data[i].circumference).toBeCloseTo(expected.c, 8);
+                       expect(meta.data[i].startAngle).toBeCloseTo(expected.s, 8);
+                       expect(meta.data[i].endAngle).toBeCloseTo(expected.e, 8);
                });
        });
 
@@ -244,9 +245,9 @@ describe('Chart.controllers.doughnut', function() {
                        {c: Math.PI / 8, s: Math.PI, e: Math.PI + Math.PI / 8},
                        {c: 3 * Math.PI / 8, s: Math.PI + Math.PI / 8, e: Math.PI + Math.PI / 2}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.circumference).toBeCloseTo(expected.c, 8);
-                       expect(meta.data[i]._model.startAngle).toBeCloseTo(expected.s, 8);
-                       expect(meta.data[i]._model.endAngle).toBeCloseTo(expected.e, 8);
+                       expect(meta.data[i].circumference).toBeCloseTo(expected.c, 8);
+                       expect(meta.data[i].startAngle).toBeCloseTo(expected.s, 8);
+                       expect(meta.data[i].endAngle).toBeCloseTo(expected.e, 8);
                });
        });
 
@@ -351,14 +352,14 @@ describe('Chart.controllers.doughnut', function() {
                        var arc = chart.getDatasetMeta(0).data[0];
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(49, 135, 221)');
-                       expect(arc._model.borderColor).toBe('rgb(22, 89, 156)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(49, 135, 221)');
+                       expect(arc.options.borderColor).toBe('rgb(22, 89, 156)');
+                       expect(arc.options.borderWidth).toBe(2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(arc._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(arc.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(arc.options.borderWidth).toBe(2);
                });
 
                it ('should handle hover styles defined via dataset properties', function() {
@@ -374,14 +375,14 @@ describe('Chart.controllers.doughnut', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(arc._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(arc._model.borderWidth).toBe(8.4);
+                       expect(arc.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(arc.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(arc.options.borderWidth).toBe(8.4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(arc._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(arc.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(arc.options.borderWidth).toBe(2);
                });
 
                it ('should handle hover styles defined via element options', function() {
@@ -397,14 +398,14 @@ describe('Chart.controllers.doughnut', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(arc._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(arc._model.borderWidth).toBe(8.4);
+                       expect(arc.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(arc.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(arc.options.borderWidth).toBe(8.4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(arc._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(arc.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(arc.options.borderWidth).toBe(2);
                });
        });
 });
index 61248398dd9aa1cf9040cbfe3908d5bb8f7eb3c6..4b0e87851a0194669a0baad300c7f0dc02d68f61 100644 (file)
@@ -203,9 +203,9 @@ describe('Chart.controllers.line', function() {
                        {x: 0, y: 512},
                        {x: 171, y: 0}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(expected.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(expected.y);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'red',
                                borderColor: 'blue',
                        }));
@@ -248,7 +248,7 @@ describe('Chart.controllers.line', function() {
                var meta = chart.getDatasetMeta(0);
                // 1 point
                var point = meta.data[0];
-               expect(point._model.x).toBeCloseToPixel(0);
+               expect(point.x).toBeCloseToPixel(0);
 
                // 2 points
                chart.data.labels = ['One', 'Two'];
@@ -257,8 +257,8 @@ describe('Chart.controllers.line', function() {
 
                var points = meta.data;
 
-               expect(points[0]._model.x).toBeCloseToPixel(0);
-               expect(points[1]._model.x).toBeCloseToPixel(512);
+               expect(points[0].x).toBeCloseToPixel(0);
+               expect(points[1].x).toBeCloseToPixel(512);
 
                // 3 points
                chart.data.labels = ['One', 'Two', 'Three'];
@@ -267,9 +267,9 @@ describe('Chart.controllers.line', function() {
 
                points = meta.data;
 
-               expect(points[0]._model.x).toBeCloseToPixel(0);
-               expect(points[1]._model.x).toBeCloseToPixel(256);
-               expect(points[2]._model.x).toBeCloseToPixel(512);
+               expect(points[0].x).toBeCloseToPixel(0);
+               expect(points[1].x).toBeCloseToPixel(256);
+               expect(points[2].x).toBeCloseToPixel(512);
 
                // 4 points
                chart.data.labels = ['One', 'Two', 'Three', 'Four'];
@@ -278,10 +278,10 @@ describe('Chart.controllers.line', function() {
 
                points = meta.data;
 
-               expect(points[0]._model.x).toBeCloseToPixel(0);
-               expect(points[1]._model.x).toBeCloseToPixel(171);
-               expect(points[2]._model.x).toBeCloseToPixel(340);
-               expect(points[3]._model.x).toBeCloseToPixel(512);
+               expect(points[0].x).toBeCloseToPixel(0);
+               expect(points[1].x).toBeCloseToPixel(171);
+               expect(points[2].x).toBeCloseToPixel(340);
+               expect(points[3].x).toBeCloseToPixel(512);
        });
 
        it('should update elements when the y scale is stacked', function() {
@@ -320,8 +320,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -332,8 +332,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
 
        });
@@ -383,8 +383,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -395,8 +395,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
 
        });
@@ -461,8 +461,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -473,8 +473,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
 
        });
@@ -515,8 +515,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 439}
                ].forEach(function(values, i) {
-                       expect(meta0.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta0.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta0.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta0.data[i].y).toBeCloseToPixel(values.y);
                });
 
                var meta1 = chart.getDatasetMeta(1);
@@ -527,8 +527,8 @@ describe('Chart.controllers.line', function() {
                        {x: 341, y: 146},
                        {x: 512, y: 497}
                ].forEach(function(values, i) {
-                       expect(meta1.data[i]._model.x).toBeCloseToPixel(values.x);
-                       expect(meta1.data[i]._model.y).toBeCloseToPixel(values.y);
+                       expect(meta1.data[i].x).toBeCloseToPixel(values.x);
+                       expect(meta1.data[i].y).toBeCloseToPixel(values.y);
                });
 
        });
@@ -552,9 +552,9 @@ describe('Chart.controllers.line', function() {
 
                var meta = chart.getDatasetMeta(0);
 
-               expect(meta.dataset._model.backgroundColor).toBe('rgb(98, 98, 98)');
-               expect(meta.dataset._model.borderColor).toBe('rgb(8, 8, 8)');
-               expect(meta.dataset._model.borderWidth).toBe(0.55);
+               expect(meta.dataset.options.backgroundColor).toBe('rgb(98, 98, 98)');
+               expect(meta.dataset.options.borderColor).toBe('rgb(8, 8, 8)');
+               expect(meta.dataset.options.borderWidth).toBe(0.55);
        });
 
        describe('dataset global defaults', function() {
@@ -595,19 +595,19 @@ describe('Chart.controllers.line', function() {
                                }
                        });
 
-                       var model = chart.getDatasetMeta(0).dataset._model;
+                       var options = chart.getDatasetMeta(0).dataset.options;
 
-                       expect(model.spanGaps).toBe(true);
-                       expect(model.tension).toBe(0.231);
-                       expect(model.backgroundColor).toBe('#add');
-                       expect(model.borderWidth).toBe('#daa');
-                       expect(model.borderColor).toBe('#dad');
-                       expect(model.borderCapStyle).toBe('round');
-                       expect(model.borderDash).toEqual([0]);
-                       expect(model.borderDashOffset).toBe(0.871);
-                       expect(model.borderJoinStyle).toBe('miter');
-                       expect(model.fill).toBe('start');
-                       expect(model.cubicInterpolationMode).toBe('monotone');
+                       expect(options.spanGaps).toBe(true);
+                       expect(options.tension).toBe(0.231);
+                       expect(options.backgroundColor).toBe('#add');
+                       expect(options.borderWidth).toBe('#daa');
+                       expect(options.borderColor).toBe('#dad');
+                       expect(options.borderCapStyle).toBe('round');
+                       expect(options.borderDash).toEqual([0]);
+                       expect(options.borderDashOffset).toBe(0.871);
+                       expect(options.borderJoinStyle).toBe('miter');
+                       expect(options.fill).toBe('start');
+                       expect(options.cubicInterpolationMode).toBe('monotone');
                });
 
                it('should be overriden by user-supplied values', function() {
@@ -639,14 +639,14 @@ describe('Chart.controllers.line', function() {
                                }
                        });
 
-                       var model = chart.getDatasetMeta(0).dataset._model;
+                       var options = chart.getDatasetMeta(0).dataset.options;
 
                        // dataset-level option overrides global default
-                       expect(model.spanGaps).toBe(true);
+                       expect(options.spanGaps).toBe(true);
                        // chart-level default overrides global default
-                       expect(model.tension).toBe(0.345);
+                       expect(options.tension).toBe(0.345);
                        // dataset-level option overrides chart-level default
-                       expect(model.backgroundColor).toBe('#dad');
+                       expect(options.backgroundColor).toBe('#dad');
                });
        });
 
@@ -679,19 +679,19 @@ describe('Chart.controllers.line', function() {
                        }
                });
 
-               var model = chart.getDatasetMeta(0).dataset._model;
+               var options = chart.getDatasetMeta(0).dataset.options;
 
-               expect(model.spanGaps).toBe(true);
-               expect(model.tension).toBe(0.231);
-               expect(model.backgroundColor).toBe('#add');
-               expect(model.borderWidth).toBe('#daa');
-               expect(model.borderColor).toBe('#dad');
-               expect(model.borderCapStyle).toBe('round');
-               expect(model.borderDash).toEqual([0]);
-               expect(model.borderDashOffset).toBe(0.871);
-               expect(model.borderJoinStyle).toBe('miter');
-               expect(model.fill).toBe('start');
-               expect(model.cubicInterpolationMode).toBe('monotone');
+               expect(options.spanGaps).toBe(true);
+               expect(options.tension).toBe(0.231);
+               expect(options.backgroundColor).toBe('#add');
+               expect(options.borderWidth).toBe('#daa');
+               expect(options.borderColor).toBe('#dad');
+               expect(options.borderCapStyle).toBe('round');
+               expect(options.borderDash).toEqual([0]);
+               expect(options.borderDashOffset).toBe(0.871);
+               expect(options.borderJoinStyle).toBe('miter');
+               expect(options.fill).toBe('start');
+               expect(options.cubicInterpolationMode).toBe('monotone');
        });
 
        it('should obey the dataset options', function() {
@@ -717,19 +717,19 @@ describe('Chart.controllers.line', function() {
                        }
                });
 
-               var model = chart.getDatasetMeta(0).dataset._model;
+               var options = chart.getDatasetMeta(0).dataset.options;
 
-               expect(model.spanGaps).toBe(true);
-               expect(model.tension).toBe(0.231);
-               expect(model.backgroundColor).toBe('#add');
-               expect(model.borderWidth).toBe('#daa');
-               expect(model.borderColor).toBe('#dad');
-               expect(model.borderCapStyle).toBe('round');
-               expect(model.borderDash).toEqual([0]);
-               expect(model.borderDashOffset).toBe(0.871);
-               expect(model.borderJoinStyle).toBe('miter');
-               expect(model.fill).toBe('start');
-               expect(model.cubicInterpolationMode).toBe('monotone');
+               expect(options.spanGaps).toBe(true);
+               expect(options.tension).toBe(0.231);
+               expect(options.backgroundColor).toBe('#add');
+               expect(options.borderWidth).toBe('#daa');
+               expect(options.borderColor).toBe('#dad');
+               expect(options.borderCapStyle).toBe('round');
+               expect(options.borderDash).toEqual([0]);
+               expect(options.borderDashOffset).toBe(0.871);
+               expect(options.borderJoinStyle).toBe('miter');
+               expect(options.fill).toBe('start');
+               expect(options.cubicInterpolationMode).toBe('monotone');
        });
 
        it('should handle number of data point changes in update', function() {
@@ -790,16 +790,16 @@ describe('Chart.controllers.line', function() {
                        var point = chart.getDatasetMeta(0).data[0];
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(49, 135, 221)');
-                       expect(point._model.borderColor).toBe('rgb(22, 89, 156)');
-                       expect(point._model.borderWidth).toBe(1);
-                       expect(point._model.radius).toBe(4);
+                       expect(point.options.backgroundColor).toBe('rgb(49, 135, 221)');
+                       expect(point.options.borderColor).toBe('rgb(22, 89, 156)');
+                       expect(point.options.borderWidth).toBe(1);
+                       expect(point.options.radius).toBe(4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(3);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(3);
                });
 
                it ('should handle hover styles defined via dataset properties', function() {
@@ -816,16 +816,16 @@ describe('Chart.controllers.line', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(point._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(point._model.borderWidth).toBe(8.4);
-                       expect(point._model.radius).toBe(4.2);
+                       expect(point.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(point.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(point.options.borderWidth).toBe(8.4);
+                       expect(point.options.radius).toBe(4.2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(3);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(3);
                });
 
                it ('should handle hover styles defined via element options', function() {
@@ -842,16 +842,16 @@ describe('Chart.controllers.line', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(point._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(point._model.borderWidth).toBe(8.4);
-                       expect(point._model.radius).toBe(4.2);
+                       expect(point.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(point.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(point.options.borderWidth).toBe(8.4);
+                       expect(point.options.radius).toBe(4.2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(3);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(3);
                });
 
                it ('should handle dataset hover styles defined via dataset properties', function() {
@@ -872,14 +872,14 @@ describe('Chart.controllers.line', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(dataset._model.backgroundColor).toBe('#000');
-                       expect(dataset._model.borderColor).toBe('#111');
-                       expect(dataset._model.borderWidth).toBe(12);
+                       expect(dataset.options.backgroundColor).toBe('#000');
+                       expect(dataset.options.borderColor).toBe('#111');
+                       expect(dataset.options.borderWidth).toBe(12);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(dataset._model.backgroundColor).toBe('#AAA');
-                       expect(dataset._model.borderColor).toBe('#BBB');
-                       expect(dataset._model.borderWidth).toBe(6);
+                       expect(dataset.options.backgroundColor).toBe('#AAA');
+                       expect(dataset.options.borderColor).toBe('#BBB');
+                       expect(dataset.options.borderWidth).toBe(6);
                });
        });
 
@@ -899,7 +899,7 @@ describe('Chart.controllers.line', function() {
                var meta = chart.getDatasetMeta(0);
                var point = meta.data[0];
 
-               expect(point._model.borderWidth).toBe(0);
+               expect(point.options.borderWidth).toBe(0);
        });
 
        it('should allow an array as the point border width setting', function() {
@@ -916,9 +916,9 @@ describe('Chart.controllers.line', function() {
                });
 
                var meta = chart.getDatasetMeta(0);
-               expect(meta.data[0]._model.borderWidth).toBe(1);
-               expect(meta.data[1]._model.borderWidth).toBe(2);
-               expect(meta.data[2]._model.borderWidth).toBe(3);
-               expect(meta.data[3]._model.borderWidth).toBe(4);
+               expect(meta.data[0].options.borderWidth).toBe(1);
+               expect(meta.data[1].options.borderWidth).toBe(2);
+               expect(meta.data[2].options.borderWidth).toBe(3);
+               expect(meta.data[3].options.borderWidth).toBe(4);
        });
 });
index f4b4107568995652f920fe89d14ef548975f56b0..f7ea6e6a5bf9fe0f1742e5328219432736fb6a28 100644 (file)
@@ -108,13 +108,13 @@ describe('Chart.controllers.polarArea', function() {
                        {o: 51, s: 0.5 * Math.PI, e: Math.PI},
                        {o: 0, s: Math.PI, e: 1.5 * Math.PI}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(259);
-                       expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(0);
-                       expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(expected.o);
-                       expect(meta.data[i]._model.startAngle).toBe(expected.s);
-                       expect(meta.data[i]._model.endAngle).toBe(expected.e);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(256);
+                       expect(meta.data[i].y).toBeCloseToPixel(259);
+                       expect(meta.data[i].innerRadius).toBeCloseToPixel(0);
+                       expect(meta.data[i].outerRadius).toBeCloseToPixel(expected.o);
+                       expect(meta.data[i].startAngle).toBe(expected.s);
+                       expect(meta.data[i].endAngle).toBe(expected.e);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'rgb(255, 0, 0)',
                                borderColor: 'rgb(0, 255, 0)',
                                borderWidth: 1.2
@@ -129,17 +129,17 @@ describe('Chart.controllers.polarArea', function() {
                chart.update();
 
                for (var i = 0; i < 4; ++i) {
-                       expect(meta.data[i]._model.backgroundColor).toBe('rgb(128, 129, 130)');
-                       expect(meta.data[i]._model.borderColor).toBe('rgb(56, 57, 58)');
-                       expect(meta.data[i]._model.borderWidth).toBe(1.123);
+                       expect(meta.data[i].options.backgroundColor).toBe('rgb(128, 129, 130)');
+                       expect(meta.data[i].options.borderColor).toBe('rgb(56, 57, 58)');
+                       expect(meta.data[i].options.borderWidth).toBe(1.123);
                }
 
                chart.update();
 
-               expect(meta.data[0]._model.x).toBeCloseToPixel(256);
-               expect(meta.data[0]._model.y).toBeCloseToPixel(259);
-               expect(meta.data[0]._model.innerRadius).toBeCloseToPixel(0);
-               expect(meta.data[0]._model.outerRadius).toBeCloseToPixel(177);
+               expect(meta.data[0].x).toBeCloseToPixel(256);
+               expect(meta.data[0].y).toBeCloseToPixel(259);
+               expect(meta.data[0].innerRadius).toBeCloseToPixel(0);
+               expect(meta.data[0].outerRadius).toBeCloseToPixel(177);
        });
 
        it('should update elements with start angle from options', function() {
@@ -176,13 +176,13 @@ describe('Chart.controllers.polarArea', function() {
                        {o: 51, s: Math.PI, e: 1.5 * Math.PI},
                        {o: 0, s: 1.5 * Math.PI, e: 2.0 * Math.PI}
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(256);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(259);
-                       expect(meta.data[i]._model.innerRadius).toBeCloseToPixel(0);
-                       expect(meta.data[i]._model.outerRadius).toBeCloseToPixel(expected.o);
-                       expect(meta.data[i]._model.startAngle).toBe(expected.s);
-                       expect(meta.data[i]._model.endAngle).toBe(expected.e);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(256);
+                       expect(meta.data[i].y).toBeCloseToPixel(259);
+                       expect(meta.data[i].innerRadius).toBeCloseToPixel(0);
+                       expect(meta.data[i].outerRadius).toBeCloseToPixel(expected.o);
+                       expect(meta.data[i].startAngle).toBe(expected.s);
+                       expect(meta.data[i].endAngle).toBe(expected.e);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'rgb(255, 0, 0)',
                                borderColor: 'rgb(0, 255, 0)',
                                borderWidth: 1.2
@@ -265,14 +265,14 @@ describe('Chart.controllers.polarArea', function() {
                        var arc = chart.getDatasetMeta(0).data[0];
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(49, 135, 221)');
-                       expect(arc._model.borderColor).toBe('rgb(22, 89, 156)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(49, 135, 221)');
+                       expect(arc.options.borderColor).toBe('rgb(22, 89, 156)');
+                       expect(arc.options.borderWidth).toBe(2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(arc._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(arc.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(arc.options.borderWidth).toBe(2);
                });
 
                it ('should handle hover styles defined via dataset properties', function() {
@@ -288,14 +288,14 @@ describe('Chart.controllers.polarArea', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(arc._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(arc._model.borderWidth).toBe(8.4);
+                       expect(arc.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(arc.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(arc.options.borderWidth).toBe(8.4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(arc._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(arc.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(arc.options.borderWidth).toBe(2);
                });
 
                it ('should handle hover styles defined via element options', function() {
@@ -311,14 +311,14 @@ describe('Chart.controllers.polarArea', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(arc._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(arc._model.borderWidth).toBe(8.4);
+                       expect(arc.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(arc.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(arc.options.borderWidth).toBe(8.4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', arc);
-                       expect(arc._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(arc._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(arc._model.borderWidth).toBe(2);
+                       expect(arc.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(arc.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(arc.options.borderWidth).toBe(2);
                });
        });
 });
index c93bc1b3501d455ed5a293d101053c95eb1c700a..c3031cefab1c388413dfabe1bf567493577b81e0 100644 (file)
@@ -118,7 +118,7 @@ describe('Chart.controllers.radar', function() {
                meta.controller.reset(); // reset first
 
                // Line element
-               expect(meta.dataset._model).toEqual(jasmine.objectContaining({
+               expect(meta.dataset.options).toEqual(jasmine.objectContaining({
                        backgroundColor: 'rgb(255, 0, 0)',
                        borderCapStyle: 'round',
                        borderColor: 'rgb(0, 255, 0)',
@@ -136,20 +136,19 @@ describe('Chart.controllers.radar', function() {
                        {x: 256, y: 260, cppx: 256, cppy: 260, cpnx: 256, cpny: 260},
                        {x: 256, y: 260, cppx: 256, cppy: 260, cpnx: 256, cpny: 260},
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
-                       expect(meta.data[i]._model.controlPointPreviousX).toBeCloseToPixel(expected.cppx);
-                       expect(meta.data[i]._model.controlPointPreviousY).toBeCloseToPixel(expected.cppy);
-                       expect(meta.data[i]._model.controlPointNextX).toBeCloseToPixel(expected.cpnx);
-                       expect(meta.data[i]._model.controlPointNextY).toBeCloseToPixel(expected.cpny);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(expected.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(expected.y);
+                       expect(meta.data[i].controlPointPreviousX).toBeCloseToPixel(expected.cppx);
+                       expect(meta.data[i].controlPointPreviousY).toBeCloseToPixel(expected.cppy);
+                       expect(meta.data[i].controlPointNextX).toBeCloseToPixel(expected.cpnx);
+                       expect(meta.data[i].controlPointNextY).toBeCloseToPixel(expected.cpny);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: Chart.defaults.global.defaultColor,
                                borderWidth: 1,
                                borderColor: Chart.defaults.global.defaultColor,
                                hitRadius: 1,
                                radius: 3,
                                pointStyle: 'circle',
-                               skip: false,
                        }));
                });
 
@@ -162,20 +161,19 @@ describe('Chart.controllers.radar', function() {
                        {x: 256, y: 260, cppx: 277, cppy: 260, cpnx: 250, cpny: 260},
                        {x: 200, y: 260, cppx: 200, cppy: 264, cpnx: 200, cpny: 250},
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
-                       expect(meta.data[i]._model.controlPointPreviousX).toBeCloseToPixel(expected.cppx);
-                       expect(meta.data[i]._model.controlPointPreviousY).toBeCloseToPixel(expected.cppy);
-                       expect(meta.data[i]._model.controlPointNextX).toBeCloseToPixel(expected.cpnx);
-                       expect(meta.data[i]._model.controlPointNextY).toBeCloseToPixel(expected.cpny);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(expected.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(expected.y);
+                       expect(meta.data[i].controlPointPreviousX).toBeCloseToPixel(expected.cppx);
+                       expect(meta.data[i].controlPointPreviousY).toBeCloseToPixel(expected.cppy);
+                       expect(meta.data[i].controlPointNextX).toBeCloseToPixel(expected.cpnx);
+                       expect(meta.data[i].controlPointNextY).toBeCloseToPixel(expected.cpny);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: Chart.defaults.global.defaultColor,
                                borderWidth: 1,
                                borderColor: Chart.defaults.global.defaultColor,
                                hitRadius: 1,
                                radius: 3,
                                pointStyle: 'circle',
-                               skip: false,
                        }));
                });
 
@@ -199,7 +197,7 @@ describe('Chart.controllers.radar', function() {
 
                meta.controller._update();
 
-               expect(meta.dataset._model).toEqual(jasmine.objectContaining({
+               expect(meta.dataset.options).toEqual(jasmine.objectContaining({
                        backgroundColor: 'rgb(98, 98, 98)',
                        borderCapStyle: 'butt',
                        borderColor: 'rgb(8, 8, 8)',
@@ -218,16 +216,15 @@ describe('Chart.controllers.radar', function() {
                        {x: 256, y: 260},
                        {x: 200, y: 260},
                ].forEach(function(expected, i) {
-                       expect(meta.data[i]._model.x).toBeCloseToPixel(expected.x);
-                       expect(meta.data[i]._model.y).toBeCloseToPixel(expected.y);
-                       expect(meta.data[i]._model).toEqual(jasmine.objectContaining({
+                       expect(meta.data[i].x).toBeCloseToPixel(expected.x);
+                       expect(meta.data[i].y).toBeCloseToPixel(expected.y);
+                       expect(meta.data[i].options).toEqual(jasmine.objectContaining({
                                backgroundColor: 'rgb(128, 129, 130)',
                                borderWidth: 1.123,
                                borderColor: 'rgb(56, 57, 58)',
                                hitRadius: 3.3,
                                radius: 22,
-                               pointStyle: 'circle',
-                               skip: false,
+                               pointStyle: 'circle'
                        }));
                });
        });
@@ -260,16 +257,16 @@ describe('Chart.controllers.radar', function() {
                        var point = chart.getDatasetMeta(0).data[0];
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(49, 135, 221)');
-                       expect(point._model.borderColor).toBe('rgb(22, 89, 156)');
-                       expect(point._model.borderWidth).toBe(1);
-                       expect(point._model.radius).toBe(4);
+                       expect(point.options.backgroundColor).toBe('rgb(49, 135, 221)');
+                       expect(point.options.borderColor).toBe('rgb(22, 89, 156)');
+                       expect(point.options.borderWidth).toBe(1);
+                       expect(point.options.radius).toBe(4);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(3);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(3);
                });
 
                it ('should handle hover styles defined via dataset properties', function() {
@@ -286,16 +283,16 @@ describe('Chart.controllers.radar', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(point._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(point._model.borderWidth).toBe(8.4);
-                       expect(point._model.radius).toBe(4.2);
+                       expect(point.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(point.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(point.options.borderWidth).toBe(8.4);
+                       expect(point.options.radius).toBe(4.2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(3);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(3);
                });
 
                it ('should handle hover styles defined via element options', function() {
@@ -312,16 +309,16 @@ describe('Chart.controllers.radar', function() {
                        chart.update();
 
                        jasmine.triggerMouseEvent(chart, 'mousemove', point);
-                       expect(point._model.backgroundColor).toBe('rgb(200, 100, 150)');
-                       expect(point._model.borderColor).toBe('rgb(150, 50, 100)');
-                       expect(point._model.borderWidth).toBe(8.4);
-                       expect(point._model.radius).toBe(4.2);
+                       expect(point.options.backgroundColor).toBe('rgb(200, 100, 150)');
+                       expect(point.options.borderColor).toBe('rgb(150, 50, 100)');
+                       expect(point.options.borderWidth).toBe(8.4);
+                       expect(point.options.radius).toBe(4.2);
 
                        jasmine.triggerMouseEvent(chart, 'mouseout', point);
-                       expect(point._model.backgroundColor).toBe('rgb(100, 150, 200)');
-                       expect(point._model.borderColor).toBe('rgb(50, 100, 150)');
-                       expect(point._model.borderWidth).toBe(2);
-                       expect(point._model.radius).toBe(3);
+                       expect(point.options.backgroundColor).toBe('rgb(100, 150, 200)');
+                       expect(point.options.borderColor).toBe('rgb(50, 100, 150)');
+                       expect(point.options.borderWidth).toBe(2);
+                       expect(point.options.radius).toBe(3);
                });
        });
 
@@ -339,7 +336,7 @@ describe('Chart.controllers.radar', function() {
 
                var meta = chart.getDatasetMeta(0);
                var point = meta.data[0];
-               expect(point._model.borderWidth).toBe(0);
+               expect(point.options.borderWidth).toBe(0);
        });
 
        it('should use the pointRadius setting over the radius setting', function() {
@@ -360,8 +357,8 @@ describe('Chart.controllers.radar', function() {
 
                var meta0 = chart.getDatasetMeta(0);
                var meta1 = chart.getDatasetMeta(1);
-               expect(meta0.data[0]._model.radius).toBe(10);
-               expect(meta1.data[0]._model.radius).toBe(20);
+               expect(meta0.data[0].options.radius).toBe(10);
+               expect(meta1.data[0].options.radius).toBe(20);
        });
 
        it('should return id for value scale', function() {
similarity index 92%
rename from test/specs/controller.scatter.test.js
rename to test/specs/controller.scatter.tests.js
index a2744b9e4372aafbb0d0437c8bb9085b2f125ea2..45f4bf08c2f6639b5ffa6fce7de1665fdf759916 100644 (file)
@@ -21,8 +21,8 @@ describe('Chart.controllers.scatter', function() {
                jasmine.triggerMouseEvent(chart, 'mousemove', point);
 
                // Title should be empty
-               expect(chart.tooltip._view.title.length).toBe(0);
-               expect(chart.tooltip._view.body[0].lines).toEqual(['(10, 15)']);
+               expect(chart.tooltip.title.length).toBe(0);
+               expect(chart.tooltip.body[0].lines).toEqual(['(10, 15)']);
        });
 
        describe('showLines option', function() {
index c0528ccf985683480d5194179a0e4155ea7ebbee..cc707f26bd33c3f224241be00e5008c288183406 100644 (file)
@@ -66,7 +66,6 @@ describe('Chart', function() {
                        var callback = function() {};
                        var defaults = Chart.defaults;
 
-                       defaults.global.responsiveAnimationDuration = 42;
                        defaults.global.hover.onHover = callback;
                        defaults.line.spanGaps = true;
                        defaults.line.hover.mode = 'x-axis';
@@ -79,11 +78,9 @@ describe('Chart', function() {
                        expect(options.defaultFontSize).toBe(defaults.global.defaultFontSize);
                        expect(options.showLines).toBe(defaults.line.showLines);
                        expect(options.spanGaps).toBe(true);
-                       expect(options.responsiveAnimationDuration).toBe(42);
                        expect(options.hover.onHover).toBe(callback);
                        expect(options.hover.mode).toBe('x-axis');
 
-                       defaults.global.responsiveAnimationDuration = 0;
                        defaults.global.hover.onHover = null;
                        defaults.line.spanGaps = false;
                        defaults.line.hover.mode = 'index';
@@ -93,7 +90,6 @@ describe('Chart', function() {
                        var callback = function() {};
                        var defaults = Chart.defaults;
 
-                       defaults.global.responsiveAnimationDuration = 42;
                        defaults.global.hover.onHover = callback;
                        defaults.line.hover.mode = 'x-axis';
                        defaults.line.spanGaps = true;
@@ -101,7 +97,6 @@ describe('Chart', function() {
                        var chart = acquireChart({
                                type: 'line',
                                options: {
-                                       responsiveAnimationDuration: 4242,
                                        spanGaps: false,
                                        hover: {
                                                mode: 'dataset',
@@ -113,13 +108,11 @@ describe('Chart', function() {
                        });
 
                        var options = chart.options;
-                       expect(options.responsiveAnimationDuration).toBe(4242);
                        expect(options.showLines).toBe(defaults.global.showLines);
                        expect(options.spanGaps).toBe(false);
                        expect(options.hover.mode).toBe('dataset');
                        expect(options.title.position).toBe('bottom');
 
-                       defaults.global.responsiveAnimationDuration = 0;
                        defaults.global.hover.onHover = null;
                        defaults.line.hover.mode = 'index';
                        defaults.line.spanGaps = false;
@@ -950,18 +943,18 @@ describe('Chart', function() {
 
                        // Verify that points are at their initial correct location,
                        // then we will reset and see that they moved
-                       expect(meta.data[0]._model.y).toBeCloseToPixel(333);
-                       expect(meta.data[1]._model.y).toBeCloseToPixel(183);
-                       expect(meta.data[2]._model.y).toBeCloseToPixel(32);
-                       expect(meta.data[3]._model.y).toBeCloseToPixel(482);
+                       expect(meta.data[0].y).toBeCloseToPixel(333);
+                       expect(meta.data[1].y).toBeCloseToPixel(183);
+                       expect(meta.data[2].y).toBeCloseToPixel(32);
+                       expect(meta.data[3].y).toBeCloseToPixel(482);
 
                        chart.reset();
 
                        // For a line chart, the animation state is the bottom
-                       expect(meta.data[0]._model.y).toBeCloseToPixel(482);
-                       expect(meta.data[1]._model.y).toBeCloseToPixel(482);
-                       expect(meta.data[2]._model.y).toBeCloseToPixel(482);
-                       expect(meta.data[3]._model.y).toBeCloseToPixel(482);
+                       expect(meta.data[0].y).toBeCloseToPixel(482);
+                       expect(meta.data[1].y).toBeCloseToPixel(482);
+                       expect(meta.data[2].y).toBeCloseToPixel(482);
+                       expect(meta.data[3].y).toBeCloseToPixel(482);
                });
        });
 
@@ -1106,7 +1099,7 @@ describe('Chart', function() {
                        chart.options.tooltips = newTooltipConfig;
 
                        chart.update();
-                       expect(chart.tooltip._options).toEqual(jasmine.objectContaining(newTooltipConfig));
+                       expect(chart.tooltip.options).toEqual(jasmine.objectContaining(newTooltipConfig));
                });
 
                it ('should update the tooltip on update', function() {
@@ -1283,46 +1276,4 @@ describe('Chart', function() {
                        ]);
                });
        });
-
-       describe('controller.update', function() {
-               beforeEach(function() {
-                       this.chart = acquireChart({
-                               type: 'doughnut',
-                               options: {
-                                       animation: {
-                                               easing: 'linear',
-                                               duration: 500
-                                       }
-                               }
-                       });
-
-                       this.addAnimationSpy = spyOn(Chart.animationService, 'addAnimation');
-               });
-
-               it('should add an animation with the default options', function() {
-                       this.chart.update();
-
-                       expect(this.addAnimationSpy).toHaveBeenCalledWith(
-                               this.chart,
-                               jasmine.objectContaining({easing: 'linear'}),
-                               500,
-                               undefined
-                       );
-               });
-
-               it('should add an animation with the provided options', function() {
-                       this.chart.update({
-                               duration: 800,
-                               easing: 'easeOutBounce',
-                               lazy: false,
-                       });
-
-                       expect(this.addAnimationSpy).toHaveBeenCalledWith(
-                               this.chart,
-                               jasmine.objectContaining({easing: 'easeOutBounce'}),
-                               800,
-                               false
-                       );
-               });
-       });
 });
diff --git a/test/specs/core.element.tests.js b/test/specs/core.element.tests.js
deleted file mode 100644 (file)
index 8b72a0c..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-// Test the core element functionality
-describe('Core element tests', function() {
-       it ('should transition model properties', function() {
-               var element = new Chart.Element({
-                       _model: {
-                               numberProp: 0,
-                               numberProp2: 100,
-                               _underscoreProp: 0,
-                               stringProp: 'abc',
-                               objectProp: {
-                                       myObject: true
-                               },
-                               colorProp: 'rgb(0, 0, 0)'
-                       }
-               });
-
-               // First transition clones model into view
-               element.transition(0.25);
-
-               expect(element._view).toEqual(element._model);
-               expect(element._view).not.toBe(element._model);
-               expect(element._view.objectProp).toBe(element._model.objectProp); // not cloned
-
-               element._model.numberProp = 100;
-               element._model.numberProp2 = 250;
-               element._model._underscoreProp = 200;
-               element._model.stringProp = 'def';
-               element._model.newStringProp = 'newString';
-               element._model.colorProp = 'rgb(255, 255, 0)';
-
-               element.transition(0.25);
-
-               expect(element._view).toEqual({
-                       numberProp: 25,
-                       numberProp2: 137.5,
-                       _underscoreProp: 0, // underscore props are not transition to a new value
-                       stringProp: 'def',
-                       newStringProp: 'newString',
-                       objectProp: {
-                               myObject: true
-                       },
-                       colorProp: 'rgb(64, 64, 0)',
-               });
-
-               // Final transition clones model into view
-               element.transition(1);
-
-               expect(element._view).toEqual(element._model);
-               expect(element._view).not.toBe(element._model);
-       });
-});
index 86eabf44e858fc629b2e044e85b835a8b695bcf1..8eae2e9d7802d01a90149815ef2487f38807fac9 100644 (file)
@@ -33,8 +33,8 @@ describe('Core.Interaction', function() {
                                type: 'click',
                                chart: chart,
                                native: true, // needed otherwise things its a DOM event
-                               x: point._model.x,
-                               y: point._model.y,
+                               x: point.x,
+                               y: point.y,
                        };
 
                        var elements = Chart.Interaction.modes.point(chart, evt, {}).map(item => item.element);
@@ -88,8 +88,8 @@ describe('Core.Interaction', function() {
                                        type: 'click',
                                        chart: chart,
                                        native: true, // needed otherwise things its a DOM event
-                                       x: point._model.x,
-                                       y: point._model.y,
+                                       x: point.x,
+                                       y: point.y,
                                };
 
                                var elements = Chart.Interaction.modes.index(chart, evt, {intersect: true}).map(item => item.element);
@@ -223,8 +223,8 @@ describe('Core.Interaction', function() {
                                        type: 'click',
                                        chart: chart,
                                        native: true, // needed otherwise things its a DOM event
-                                       x: point._model.x,
-                                       y: point._model.y
+                                       x: point.x,
+                                       y: point.y
                                };
 
                                var elements = Chart.Interaction.modes.dataset(chart, evt, {intersect: true});
@@ -365,8 +365,8 @@ describe('Core.Interaction', function() {
 
                                        // Halfway between 2 mid points
                                        var pt = {
-                                               x: meta0.data[1]._view.x,
-                                               y: (meta0.data[1]._view.y + meta1.data[1]._view.y) / 2
+                                               x: meta0.data[1].x,
+                                               y: (meta0.data[1].y + meta1.data[1].y) / 2
                                        };
 
                                        var evt = {
@@ -391,8 +391,8 @@ describe('Core.Interaction', function() {
 
                                        // At 'Point 2', 10
                                        var pt = {
-                                               x: meta0.data[1]._view.x,
-                                               y: meta0.data[0]._view.y
+                                               x: meta0.data[1].x,
+                                               y: meta0.data[0].y
                                        };
 
                                        var evt = {
@@ -415,8 +415,8 @@ describe('Core.Interaction', function() {
 
                                        // Haflway between 'Point 1' and 'Point 2', y=10
                                        var pt = {
-                                               x: (meta0.data[0]._view.x + meta0.data[1]._view.x) / 2,
-                                               y: meta0.data[0]._view.y
+                                               x: (meta0.data[0].x + meta0.data[1].x) / 2,
+                                               y: meta0.data[0].y
                                        };
 
                                        var evt = {
@@ -440,8 +440,8 @@ describe('Core.Interaction', function() {
 
                                        // 'Point 1', y = 30
                                        var pt = {
-                                               x: meta0.data[0]._view.x,
-                                               y: meta0.data[2]._view.y
+                                               x: meta0.data[0].x,
+                                               y: meta0.data[2].y
                                        };
 
                                        var evt = {
@@ -464,8 +464,8 @@ describe('Core.Interaction', function() {
 
                                        // 'Point 1', y = 40
                                        var pt = {
-                                               x: meta0.data[0]._view.x,
-                                               y: meta0.data[1]._view.y
+                                               x: meta0.data[0].x,
+                                               y: meta0.data[1].y
                                        };
 
                                        var evt = {
@@ -514,8 +514,8 @@ describe('Core.Interaction', function() {
                                                type: 'click',
                                                chart: chart,
                                                native: true, // needed otherwise things its a DOM event
-                                               x: point._view.x + 15,
-                                               y: point._view.y
+                                               x: point.x + 15,
+                                               y: point.y
                                        };
 
                                        // Nothing intersects so find nothing
@@ -526,8 +526,8 @@ describe('Core.Interaction', function() {
                                                type: 'click',
                                                chart: chart,
                                                native: true,
-                                               x: point._view.x,
-                                               y: point._view.y
+                                               x: point.x,
+                                               y: point.y
                                        };
                                        elements = Chart.Interaction.modes.nearest(chart, evt, {intersect: true}).map(item => item.element);
                                        expect(elements).toEqual([point]);
@@ -547,8 +547,8 @@ describe('Core.Interaction', function() {
 
                                        // Halfway between 2 mid points
                                        var pt = {
-                                               x: meta0.data[1]._view.x,
-                                               y: meta0.data[1]._view.y
+                                               x: meta0.data[1].x,
+                                               y: meta0.data[1].y
                                        };
 
                                        var evt = {
@@ -577,8 +577,8 @@ describe('Core.Interaction', function() {
 
                                        // Halfway between 2 mid points
                                        var pt = {
-                                               x: meta0.data[1]._view.x,
-                                               y: meta0.data[1]._view.y
+                                               x: meta0.data[1].x,
+                                               y: meta0.data[1].y
                                        };
 
                                        var evt = {
@@ -626,8 +626,8 @@ describe('Core.Interaction', function() {
 
                        // Halfway between 2 mid points
                        var pt = {
-                               x: meta0.data[1]._view.x,
-                               y: meta0.data[1]._view.y
+                               x: meta0.data[1].x,
+                               y: meta0.data[1].y
                        };
 
                        var evt = {
@@ -660,8 +660,8 @@ describe('Core.Interaction', function() {
 
                        // Halfway between 2 mid points
                        var pt = {
-                               x: meta0.data[1]._view.x,
-                               y: meta0.data[1]._view.y
+                               x: meta0.data[1].x,
+                               y: meta0.data[1].y
                        };
 
                        var evt = {
@@ -718,8 +718,8 @@ describe('Core.Interaction', function() {
 
                        // Halfway between 2 mid points
                        var pt = {
-                               x: meta0.data[1]._view.x,
-                               y: meta0.data[1]._view.y
+                               x: meta0.data[1].x,
+                               y: meta0.data[1].y
                        };
 
                        var evt = {
@@ -752,8 +752,8 @@ describe('Core.Interaction', function() {
 
                        // Halfway between 2 mid points
                        var pt = {
-                               x: meta0.data[1]._view.x,
-                               y: meta0.data[1]._view.y
+                               x: meta0.data[1].x,
+                               y: meta0.data[1].y
                        };
 
                        var evt = {
index f0ad7441e0332317bec946732055dfe60831ee1b..4b8da46e454231af774d5a7ac9847510653e43ac 100644 (file)
@@ -69,7 +69,7 @@ describe('Core.Tooltip', function() {
                                view: window,
                                bubbles: true,
                                cancelable: true,
-                               clientX: rect.left + point._model.x,
+                               clientX: rect.left + point.x,
                                clientY: 0
                        });
 
@@ -80,46 +80,55 @@ describe('Core.Tooltip', function() {
                        var tooltip = chart.tooltip;
                        var globalDefaults = Chart.defaults.global;
 
-                       expect(tooltip._view).toEqual(jasmine.objectContaining({
-                               // Positioning
-                               xPadding: 6,
-                               yPadding: 6,
-                               xAlign: 'left',
-                               yAlign: 'center',
+                       expect(tooltip.options.xPadding).toEqual(6);
+                       expect(tooltip.options.yPadding).toEqual(6);
+                       expect(tooltip.xAlign).toEqual('left');
+                       expect(tooltip.yAlign).toEqual('center');
 
+                       expect(tooltip.options).toEqual(jasmine.objectContaining({
                                // Body
                                bodyFontColor: '#fff',
-                               _bodyFontFamily: globalDefaults.defaultFontFamily,
-                               _bodyFontStyle: globalDefaults.defaultFontStyle,
-                               _bodyAlign: 'left',
+                               bodyFontFamily: globalDefaults.defaultFontFamily,
+                               bodyFontStyle: globalDefaults.defaultFontStyle,
+                               bodyAlign: 'left',
                                bodyFontSize: globalDefaults.defaultFontSize,
                                bodySpacing: 2,
+                       }));
 
+                       expect(tooltip.options).toEqual(jasmine.objectContaining({
                                // Title
                                titleFontColor: '#fff',
-                               _titleFontFamily: globalDefaults.defaultFontFamily,
-                               _titleFontStyle: 'bold',
+                               titleFontFamily: globalDefaults.defaultFontFamily,
+                               titleFontStyle: 'bold',
                                titleFontSize: globalDefaults.defaultFontSize,
-                               _titleAlign: 'left',
+                               titleAlign: 'left',
                                titleSpacing: 2,
                                titleMarginBottom: 6,
+                       }));
 
+                       expect(tooltip.options).toEqual(jasmine.objectContaining({
                                // Footer
                                footerFontColor: '#fff',
-                               _footerFontFamily: globalDefaults.defaultFontFamily,
-                               _footerFontStyle: 'bold',
+                               footerFontFamily: globalDefaults.defaultFontFamily,
+                               footerFontStyle: 'bold',
                                footerFontSize: globalDefaults.defaultFontSize,
-                               _footerAlign: 'left',
+                               footerAlign: 'left',
                                footerSpacing: 2,
                                footerMarginTop: 6,
+                       }));
 
+                       expect(tooltip.options).toEqual(jasmine.objectContaining({
                                // Appearance
                                caretSize: 5,
+                               caretPadding: 2,
                                cornerRadius: 6,
                                backgroundColor: 'rgba(0,0,0,0.8)',
+                               multiKeyBackground: '#fff',
+                               displayColors: true
+                       }));
+
+                       expect(tooltip).toEqual(jasmine.objectContaining({
                                opacity: 1,
-                               legendColorBackground: '#fff',
-                               displayColors: true,
 
                                // Text
                                title: ['Point 2'],
@@ -135,7 +144,6 @@ describe('Core.Tooltip', function() {
                                }],
                                afterBody: [],
                                footer: [],
-                               caretPadding: 2,
                                labelColors: [{
                                        borderColor: globalDefaults.defaultColor,
                                        backgroundColor: globalDefaults.defaultColor
@@ -145,8 +153,8 @@ describe('Core.Tooltip', function() {
                                }]
                        }));
 
-                       expect(tooltip._view.x).toBeCloseToPixel(267);
-                       expect(tooltip._view.y).toBeCloseToPixel(155);
+                       expect(tooltip.x).toBeCloseToPixel(267);
+                       expect(tooltip.y).toBeCloseToPixel(155);
                });
 
                it('Should only display if intersecting if intersect is set', function() {
@@ -185,7 +193,7 @@ describe('Core.Tooltip', function() {
                                view: window,
                                bubbles: true,
                                cancelable: true,
-                               clientX: rect.left + point._model.x,
+                               clientX: rect.left + point.x,
                                clientY: 0
                        });
 
@@ -194,46 +202,9 @@ describe('Core.Tooltip', function() {
 
                        // Check and see if tooltip was displayed
                        var tooltip = chart.tooltip;
-                       var globalDefaults = Chart.defaults.global;
-
-                       expect(tooltip._view).toEqual(jasmine.objectContaining({
-                               // Positioning
-                               xPadding: 6,
-                               yPadding: 6,
-
-                               // Body
-                               bodyFontColor: '#fff',
-                               _bodyFontFamily: globalDefaults.defaultFontFamily,
-                               _bodyFontStyle: globalDefaults.defaultFontStyle,
-                               _bodyAlign: 'left',
-                               bodyFontSize: globalDefaults.defaultFontSize,
-                               bodySpacing: 2,
-
-                               // Title
-                               titleFontColor: '#fff',
-                               _titleFontFamily: globalDefaults.defaultFontFamily,
-                               _titleFontStyle: 'bold',
-                               titleFontSize: globalDefaults.defaultFontSize,
-                               _titleAlign: 'left',
-                               titleSpacing: 2,
-                               titleMarginBottom: 6,
-
-                               // Footer
-                               footerFontColor: '#fff',
-                               _footerFontFamily: globalDefaults.defaultFontFamily,
-                               _footerFontStyle: 'bold',
-                               footerFontSize: globalDefaults.defaultFontSize,
-                               _footerAlign: 'left',
-                               footerSpacing: 2,
-                               footerMarginTop: 6,
 
-                               // Appearance
-                               caretSize: 5,
-                               cornerRadius: 6,
-                               backgroundColor: 'rgba(0,0,0,0.8)',
+                       expect(tooltip).toEqual(jasmine.objectContaining({
                                opacity: 0,
-                               legendColorBackground: '#fff',
-                               displayColors: true,
                        }));
                });
        });
@@ -274,8 +245,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point._model.x,
-                       clientY: rect.top + point._model.y
+                       clientX: rect.left + point.x,
+                       clientY: rect.top + point.y
                });
 
                // Manually trigger rather than having an async test
@@ -285,46 +256,55 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
-                       // Positioning
-                       xPadding: 6,
-                       yPadding: 6,
-                       xAlign: 'left',
-                       yAlign: 'center',
+               expect(tooltip.options.xPadding).toEqual(6);
+               expect(tooltip.options.yPadding).toEqual(6);
+               expect(tooltip.xAlign).toEqual('left');
+               expect(tooltip.yAlign).toEqual('center');
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Body
                        bodyFontColor: '#fff',
-                       _bodyFontFamily: globalDefaults.defaultFontFamily,
-                       _bodyFontStyle: globalDefaults.defaultFontStyle,
-                       _bodyAlign: 'left',
+                       bodyFontFamily: globalDefaults.defaultFontFamily,
+                       bodyFontStyle: globalDefaults.defaultFontStyle,
+                       bodyAlign: 'left',
                        bodyFontSize: globalDefaults.defaultFontSize,
                        bodySpacing: 2,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Title
                        titleFontColor: '#fff',
-                       _titleFontFamily: globalDefaults.defaultFontFamily,
-                       _titleFontStyle: 'bold',
+                       titleFontFamily: globalDefaults.defaultFontFamily,
+                       titleFontStyle: 'bold',
                        titleFontSize: globalDefaults.defaultFontSize,
-                       _titleAlign: 'left',
+                       titleAlign: 'left',
                        titleSpacing: 2,
                        titleMarginBottom: 6,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Footer
                        footerFontColor: '#fff',
-                       _footerFontFamily: globalDefaults.defaultFontFamily,
-                       _footerFontStyle: 'bold',
+                       footerFontFamily: globalDefaults.defaultFontFamily,
+                       footerFontStyle: 'bold',
                        footerFontSize: globalDefaults.defaultFontSize,
-                       _footerAlign: 'left',
+                       footerAlign: 'left',
                        footerSpacing: 2,
                        footerMarginTop: 6,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Appearance
                        caretSize: 5,
+                       caretPadding: 2,
                        cornerRadius: 6,
                        backgroundColor: 'rgba(0,0,0,0.8)',
+                       multiKeyBackground: '#fff',
+                       displayColors: true
+               }));
+
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        opacity: 1,
-                       legendColorBackground: '#fff',
-                       displayColors: true,
 
                        // Text
                        title: ['Point 2'],
@@ -336,7 +316,6 @@ describe('Core.Tooltip', function() {
                        }],
                        afterBody: [],
                        footer: [],
-                       caretPadding: 2,
                        labelTextColors: ['#fff'],
                        labelColors: [{
                                borderColor: globalDefaults.defaultColor,
@@ -344,8 +323,8 @@ describe('Core.Tooltip', function() {
                        }]
                }));
 
-               expect(tooltip._view.x).toBeCloseToPixel(267);
-               expect(tooltip._view.y).toBeCloseToPixel(312);
+               expect(tooltip.x).toBeCloseToPixel(267);
+               expect(tooltip.y).toBeCloseToPixel(312);
        });
 
        it('Should display information from user callbacks', function() {
@@ -421,8 +400,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point._model.x,
-                       clientY: rect.top + point._model.y
+                       clientX: rect.left + point.x,
+                       clientY: rect.top + point.y
                });
 
                // Manually trigger rather than having an async test
@@ -432,45 +411,54 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
-                       // Positioning
-                       xPadding: 6,
-                       yPadding: 6,
-                       xAlign: 'center',
-                       yAlign: 'top',
+               expect(tooltip.options.xPadding).toEqual(6);
+               expect(tooltip.options.yPadding).toEqual(6);
+               expect(tooltip.xAlign).toEqual('center');
+               expect(tooltip.yAlign).toEqual('top');
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Body
                        bodyFontColor: '#fff',
-                       _bodyFontFamily: globalDefaults.defaultFontFamily,
-                       _bodyFontStyle: globalDefaults.defaultFontStyle,
-                       _bodyAlign: 'left',
+                       bodyFontFamily: globalDefaults.defaultFontFamily,
+                       bodyFontStyle: globalDefaults.defaultFontStyle,
+                       bodyAlign: 'left',
                        bodyFontSize: globalDefaults.defaultFontSize,
                        bodySpacing: 2,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Title
                        titleFontColor: '#fff',
-                       _titleFontFamily: globalDefaults.defaultFontFamily,
-                       _titleFontStyle: 'bold',
+                       titleFontFamily: globalDefaults.defaultFontFamily,
+                       titleFontStyle: 'bold',
                        titleFontSize: globalDefaults.defaultFontSize,
-                       _titleAlign: 'left',
+                       titleAlign: 'left',
                        titleSpacing: 2,
                        titleMarginBottom: 6,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Footer
                        footerFontColor: '#fff',
-                       _footerFontFamily: globalDefaults.defaultFontFamily,
-                       _footerFontStyle: 'bold',
+                       footerFontFamily: globalDefaults.defaultFontFamily,
+                       footerFontStyle: 'bold',
                        footerFontSize: globalDefaults.defaultFontSize,
-                       _footerAlign: 'left',
+                       footerAlign: 'left',
                        footerSpacing: 2,
                        footerMarginTop: 6,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Appearance
                        caretSize: 5,
+                       caretPadding: 2,
                        cornerRadius: 6,
                        backgroundColor: 'rgba(0,0,0,0.8)',
+                       multiKeyBackground: '#fff',
+               }));
+
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        opacity: 1,
-                       legendColorBackground: '#fff',
 
                        // Text
                        title: ['beforeTitle', 'title', 'afterTitle'],
@@ -486,7 +474,6 @@ describe('Core.Tooltip', function() {
                        }],
                        afterBody: ['afterBody'],
                        footer: ['beforeFooter', 'footer', 'afterFooter'],
-                       caretPadding: 2,
                        labelTextColors: ['labelTextColor', 'labelTextColor'],
                        labelColors: [{
                                borderColor: globalDefaults.defaultColor,
@@ -497,8 +484,8 @@ describe('Core.Tooltip', function() {
                        }]
                }));
 
-               expect(tooltip._view.x).toBeCloseToPixel(214);
-               expect(tooltip._view.y).toBeCloseToPixel(190);
+               expect(tooltip.x).toBeCloseToPixel(214);
+               expect(tooltip.y).toBeCloseToPixel(190);
        });
 
        it('Should allow sorting items', function() {
@@ -539,8 +526,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point0._model.x,
-                       clientY: rect.top + point0._model.y
+                       clientX: rect.left + point0.x,
+                       clientY: rect.top + point0.y
                });
 
                // Manually trigger rather than having an async test
@@ -550,7 +537,7 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        // Positioning
                        xAlign: 'left',
                        yAlign: 'center',
@@ -578,8 +565,8 @@ describe('Core.Tooltip', function() {
                        }]
                }));
 
-               expect(tooltip._view.x).toBeCloseToPixel(267);
-               expect(tooltip._view.y).toBeCloseToPixel(155);
+               expect(tooltip.x).toBeCloseToPixel(267);
+               expect(tooltip.y).toBeCloseToPixel(155);
        });
 
        it('Should allow reversing items', function() {
@@ -618,8 +605,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point0._model.x,
-                       clientY: rect.top + point0._model.y
+                       clientX: rect.left + point0.x,
+                       clientY: rect.top + point0.y
                });
 
                // Manually trigger rather than having an async test
@@ -629,7 +616,7 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        // Positioning
                        xAlign: 'left',
                        yAlign: 'center',
@@ -657,8 +644,8 @@ describe('Core.Tooltip', function() {
                        }]
                }));
 
-               expect(tooltip._view.x).toBeCloseToPixel(267);
-               expect(tooltip._view.y).toBeCloseToPixel(155);
+               expect(tooltip.x).toBeCloseToPixel(267);
+               expect(tooltip.y).toBeCloseToPixel(155);
        });
 
        it('Should follow dataset order', function() {
@@ -698,8 +685,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point0._model.x,
-                       clientY: rect.top + point0._model.y
+                       clientX: rect.left + point0.x,
+                       clientY: rect.top + point0.y
                });
 
                // Manually trigger rather than having an async test
@@ -709,7 +696,7 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        // Positioning
                        xAlign: 'left',
                        yAlign: 'center',
@@ -737,8 +724,8 @@ describe('Core.Tooltip', function() {
                        }]
                }));
 
-               expect(tooltip._view.x).toBeCloseToPixel(267);
-               expect(tooltip._view.y).toBeCloseToPixel(155);
+               expect(tooltip.x).toBeCloseToPixel(267);
+               expect(tooltip.y).toBeCloseToPixel(155);
        });
 
        it('should filter items from the tooltip using the callback', function() {
@@ -781,8 +768,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point0._model.x,
-                       clientY: rect.top + point0._model.y
+                       clientX: rect.left + point0.x,
+                       clientY: rect.top + point0.y
                });
 
                // Manually trigger rather than having an async test
@@ -792,7 +779,7 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        // Positioning
                        xAlign: 'left',
                        yAlign: 'center',
@@ -850,8 +837,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point0._model.x,
-                       clientY: rect.top + point0._model.y
+                       clientX: rect.left + point0.x,
+                       clientY: rect.top + point0.y
                });
 
                // Manually trigger rather than having an async test
@@ -860,7 +847,7 @@ describe('Core.Tooltip', function() {
                // Check and see if tooltip was displayed
                var tooltip = chart.tooltip;
 
-               expect(tooltip._model).toEqual(jasmine.objectContaining({
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Positioning
                        caretPadding: 10,
                }));
@@ -901,11 +888,11 @@ describe('Core.Tooltip', function() {
                        // Check and see if tooltip was displayed
                        var tooltip = chart.tooltip;
 
-                       expect(tooltip._view instanceof Object).toBe(true);
-                       expect(tooltip._view.dataPoints instanceof Array).toBe(true);
-                       expect(tooltip._view.dataPoints.length).toBe(1);
+                       expect(tooltip instanceof Object).toBe(true);
+                       expect(tooltip.dataPoints instanceof Array).toBe(true);
+                       expect(tooltip.dataPoints.length).toBe(1);
 
-                       var tooltipItem = tooltip._view.dataPoints[0];
+                       var tooltipItem = tooltip.dataPoints[0];
 
                        expect(tooltipItem.index).toBe(pointIndex);
                        expect(tooltipItem.datasetIndex).toBe(datasetIndex);
@@ -957,8 +944,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: false,
                        cancelable: true,
-                       clientX: rect.left + firstPoint._model.x,
-                       clientY: rect.top + firstPoint._model.y
+                       clientX: rect.left + firstPoint.x,
+                       clientY: rect.top + firstPoint.y
                });
 
                var tooltip = chart.tooltip;
@@ -1022,8 +1009,8 @@ describe('Core.Tooltip', function() {
                                view: window,
                                bubbles: true,
                                cancelable: true,
-                               clientX: rect.left + point._model.x,
-                               clientY: rect.top + point._model.y
+                               clientX: rect.left + point.x,
+                               clientY: rect.top + point.y
                        });
 
                        // Manually trigger rather than having an async test
@@ -1063,6 +1050,9 @@ describe('Core.Tooltip', function() {
                                animation: {
                                        // without this slice center point is calculated wrong
                                        animateRotate: false
+                               },
+                               tooltips: {
+                                       animation: false
                                }
                        }
                });
@@ -1091,14 +1081,15 @@ describe('Core.Tooltip', function() {
                                chart.update();
                                node.dispatchEvent(mouseOutEvent);
                                node.dispatchEvent(mouseMoveEvent);
-                               var model = chart.tooltip._model;
-                               expect(model.x).toBeGreaterThanOrEqual(0);
-                               if (model.width <= chart.width) {
-                                       expect(model.x + model.width).toBeLessThanOrEqual(chart.width);
+                               var tooltip = chart.tooltip;
+                               expect(tooltip.dataPoints.length).toBe(1);
+                               expect(tooltip.x).toBeGreaterThanOrEqual(0);
+                               if (tooltip.width <= chart.width) {
+                                       expect(tooltip.x + tooltip.width).toBeLessThanOrEqual(chart.width);
                                }
-                               expect(model.caretX).toBeCloseToPixel(tooltipPosition.x);
+                               expect(tooltip.caretX).toBeCloseToPixel(tooltipPosition.x);
                                // if tooltip is longer than chart area then all tests done
-                               if (model.width > chart.width) {
+                               if (tooltip.width > chart.width) {
                                        break;
                                }
                        }
@@ -1176,8 +1167,8 @@ describe('Core.Tooltip', function() {
                        view: window,
                        bubbles: true,
                        cancelable: true,
-                       clientX: rect.left + point._model.x,
-                       clientY: rect.top + point._model.y
+                       clientX: rect.left + point.x,
+                       clientY: rect.top + point.y
                });
 
                // Manually trigger rather than having an async test
@@ -1187,45 +1178,54 @@ describe('Core.Tooltip', function() {
                var tooltip = chart.tooltip;
                var globalDefaults = Chart.defaults.global;
 
-               expect(tooltip._view).toEqual(jasmine.objectContaining({
-                       // Positioning
-                       xPadding: 6,
-                       yPadding: 6,
-                       xAlign: 'center',
-                       yAlign: 'top',
+               expect(tooltip.options.xPadding).toEqual(6);
+               expect(tooltip.options.yPadding).toEqual(6);
+               expect(tooltip.xAlign).toEqual('center');
+               expect(tooltip.yAlign).toEqual('top');
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Body
                        bodyFontColor: '#fff',
-                       _bodyFontFamily: globalDefaults.defaultFontFamily,
-                       _bodyFontStyle: globalDefaults.defaultFontStyle,
-                       _bodyAlign: 'left',
+                       bodyFontFamily: globalDefaults.defaultFontFamily,
+                       bodyFontStyle: globalDefaults.defaultFontStyle,
+                       bodyAlign: 'left',
                        bodyFontSize: globalDefaults.defaultFontSize,
                        bodySpacing: 2,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Title
                        titleFontColor: '#fff',
-                       _titleFontFamily: globalDefaults.defaultFontFamily,
-                       _titleFontStyle: 'bold',
+                       titleFontFamily: globalDefaults.defaultFontFamily,
+                       titleFontStyle: 'bold',
                        titleFontSize: globalDefaults.defaultFontSize,
-                       _titleAlign: 'left',
+                       titleAlign: 'left',
                        titleSpacing: 2,
                        titleMarginBottom: 6,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Footer
                        footerFontColor: '#fff',
-                       _footerFontFamily: globalDefaults.defaultFontFamily,
-                       _footerFontStyle: 'bold',
+                       footerFontFamily: globalDefaults.defaultFontFamily,
+                       footerFontStyle: 'bold',
                        footerFontSize: globalDefaults.defaultFontSize,
-                       _footerAlign: 'left',
+                       footerAlign: 'left',
                        footerSpacing: 2,
                        footerMarginTop: 6,
+               }));
 
+               expect(tooltip.options).toEqual(jasmine.objectContaining({
                        // Appearance
                        caretSize: 5,
+                       caretPadding: 2,
                        cornerRadius: 6,
                        backgroundColor: 'rgba(0,0,0,0.8)',
+                       multiKeyBackground: '#fff',
+               }));
+
+               expect(tooltip).toEqual(jasmine.objectContaining({
                        opacity: 1,
-                       legendColorBackground: '#fff',
 
                        // Text
                        title: ['beforeTitle', 'newline', 'title', 'newline', 'afterTitle', 'newline'],
@@ -1241,7 +1241,6 @@ describe('Core.Tooltip', function() {
                        }],
                        afterBody: ['afterBody', 'newline'],
                        footer: ['beforeFooter', 'newline', 'footer', 'newline', 'afterFooter', 'newline'],
-                       caretPadding: 2,
                        labelTextColors: ['labelTextColor', 'labelTextColor'],
                        labelColors: [{
                                borderColor: globalDefaults.defaultColor,
@@ -1262,45 +1261,51 @@ describe('Core.Tooltip', function() {
                                y: 100,
                                width: 100,
                                height: 100,
-                               xPadding: 5,
-                               yPadding: 5,
                                xAlign: 'left',
                                yAlign: 'top',
 
-                               // Body
-                               bodyFontColor: '#fff',
-                               _bodyFontFamily: globalDefaults.defaultFontFamily,
-                               _bodyFontStyle: globalDefaults.defaultFontStyle,
-                               _bodyAlign: body,
-                               bodyFontSize: globalDefaults.defaultFontSize,
-                               bodySpacing: 2,
-
-                               // Title
-                               titleFontColor: '#fff',
-                               _titleFontFamily: globalDefaults.defaultFontFamily,
-                               _titleFontStyle: 'bold',
-                               titleFontSize: globalDefaults.defaultFontSize,
-                               _titleAlign: title,
-                               titleSpacing: 2,
-                               titleMarginBottom: 6,
-
-                               // Footer
-                               footerFontColor: '#fff',
-                               _footerFontFamily: globalDefaults.defaultFontFamily,
-                               _footerFontStyle: 'bold',
-                               footerFontSize: globalDefaults.defaultFontSize,
-                               _footerAlign: footer,
-                               footerSpacing: 2,
-                               footerMarginTop: 6,
+                               options: {
+                                       xPadding: 5,
+                                       yPadding: 5,
+
+                                       // Body
+                                       bodyFontColor: '#fff',
+                                       bodyFontFamily: globalDefaults.defaultFontFamily,
+                                       bodyFontStyle: globalDefaults.defaultFontStyle,
+                                       bodyAlign: body,
+                                       bodyFontSize: globalDefaults.defaultFontSize,
+                                       bodySpacing: 2,
+
+                                       // Title
+                                       titleFontColor: '#fff',
+                                       titleFontFamily: globalDefaults.defaultFontFamily,
+                                       titleFontStyle: 'bold',
+                                       titleFontSize: globalDefaults.defaultFontSize,
+                                       titleAlign: title,
+                                       titleSpacing: 2,
+                                       titleMarginBottom: 6,
+
+                                       // Footer
+                                       footerFontColor: '#fff',
+                                       footerFontFamily: globalDefaults.defaultFontFamily,
+                                       footerFontStyle: 'bold',
+                                       footerFontSize: globalDefaults.defaultFontSize,
+                                       footerAlign: footer,
+                                       footerSpacing: 2,
+                                       footerMarginTop: 6,
+
+                                       // Appearance
+                                       caretSize: 5,
+                                       cornerRadius: 6,
+                                       caretPadding: 2,
+                                       borderColor: '#aaa',
+                                       borderWidth: 1,
+                                       backgroundColor: 'rgba(0,0,0,0.8)',
+                                       multiKeyBackground: '#fff',
+                                       displayColors: false
 
-                               // Appearance
-                               caretSize: 5,
-                               cornerRadius: 6,
-                               borderColor: '#aaa',
-                               borderWidth: 1,
-                               backgroundColor: 'rgba(0,0,0,0.8)',
+                               },
                                opacity: 1,
-                               legendColorBackground: '#fff',
 
                                // Text
                                title: ['title'],
@@ -1312,7 +1317,6 @@ describe('Core.Tooltip', function() {
                                }],
                                afterBody: [],
                                footer: ['footer'],
-                               caretPadding: 2,
                                labelTextColors: ['#fff'],
                                labelColors: [{
                                        borderColor: 'rgb(255, 0, 0)',
@@ -1348,16 +1352,19 @@ describe('Core.Tooltip', function() {
 
                var mockContext = window.createMockContext();
                var tooltip = new Chart.Tooltip({
-                       _options: globalDefaults.tooltips,
                        _chart: {
-                               ctx: mockContext,
+                               options: {
+                                       tooltips: {
+                                               animation: false,
+                                       }
+                               }
                        }
                });
 
                it('Should go left', function() {
                        mockContext.resetCalls();
-                       tooltip._view = makeView('left', 'left', 'left');
-                       tooltip.draw();
+                       Chart.helpers.merge(tooltip, makeView('left', 'left', 'left'));
+                       tooltip.draw(mockContext);
 
                        expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
                                {name: 'setTextAlign', args: ['left']},
@@ -1376,8 +1383,8 @@ describe('Core.Tooltip', function() {
 
                it('Should go right', function() {
                        mockContext.resetCalls();
-                       tooltip._view = makeView('right', 'right', 'right');
-                       tooltip.draw();
+                       Chart.helpers.merge(tooltip, makeView('right', 'right', 'right'));
+                       tooltip.draw(mockContext);
 
                        expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
                                {name: 'setTextAlign', args: ['right']},
@@ -1396,8 +1403,8 @@ describe('Core.Tooltip', function() {
 
                it('Should center', function() {
                        mockContext.resetCalls();
-                       tooltip._view = makeView('center', 'center', 'center');
-                       tooltip.draw();
+                       Chart.helpers.merge(tooltip, makeView('center', 'center', 'center'));
+                       tooltip.draw(mockContext);
 
                        expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
                                {name: 'setTextAlign', args: ['center']},
@@ -1416,8 +1423,8 @@ describe('Core.Tooltip', function() {
 
                it('Should allow mixed', function() {
                        mockContext.resetCalls();
-                       tooltip._view = makeView('right', 'center', 'left');
-                       tooltip.draw();
+                       Chart.helpers.merge(tooltip, makeView('right', 'center', 'left'));
+                       tooltip.draw(mockContext);
 
                        expect(mockContext.getCalls()).toEqual(Array.prototype.concat(drawBody, [
                                {name: 'setTextAlign', args: ['right']},
index 41c720d9b876b4cc13500dbf14b64be872246e9f..bc47a2752e38bdec670192e9a67a424a827a182c 100644 (file)
@@ -13,20 +13,15 @@ describe('Arc element tests', function() {
        });
 
        it ('should determine if in range', function() {
+               // Mock out the arc as if the controller put it there
                var arc = new Chart.elements.Arc({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Mock out the view as if the controller put it there
-               arc._view = {
                        startAngle: 0,
                        endAngle: Math.PI / 2,
                        x: 0,
                        y: 0,
                        innerRadius: 5,
                        outerRadius: 10,
-               };
+               });
 
                expect(arc.inRange(2, 2)).toBe(false);
                expect(arc.inRange(7, 0)).toBe(true);
@@ -36,20 +31,15 @@ describe('Arc element tests', function() {
        });
 
        it ('should get the tooltip position', function() {
+               // Mock out the arc as if the controller put it there
                var arc = new Chart.elements.Arc({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Mock out the view as if the controller put it there
-               arc._view = {
                        startAngle: 0,
                        endAngle: Math.PI / 2,
                        x: 0,
                        y: 0,
                        innerRadius: 0,
                        outerRadius: Math.sqrt(2),
-               };
+               });
 
                var pos = arc.tooltipPosition();
                expect(pos.x).toBeCloseTo(0.5);
@@ -57,20 +47,15 @@ describe('Arc element tests', function() {
        });
 
        it ('should get the center', function() {
+               // Mock out the arc as if the controller put it there
                var arc = new Chart.elements.Arc({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Mock out the view as if the controller put it there
-               arc._view = {
                        startAngle: 0,
                        endAngle: Math.PI / 2,
                        x: 0,
                        y: 0,
                        innerRadius: 0,
                        outerRadius: Math.sqrt(2),
-               };
+               });
 
                var center = arc.getCenterPoint();
                expect(center.x).toBeCloseTo(0.5, 6);
index 199998dd039cc42939a16224c20a775b96ba5cd2..6d14fe8ce9bc8b203609a99e8f75dc98405de45e 100644 (file)
@@ -13,21 +13,15 @@ describe('Chart.elements.Point', function() {
        });
 
        it ('Should correctly identify as in range', function() {
+               // Mock out the point as if we were made by the controller
                var point = new Chart.elements.Point({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Safely handles if these are called before the viewmodel is instantiated
-               expect(point.inRange(5)).toBe(false);
-
-               // Attach a view object as if we were the controller
-               point._view = {
-                       radius: 2,
-                       hitRadius: 3,
+                       options: {
+                               radius: 2,
+                               hitRadius: 3,
+                       },
                        x: 10,
                        y: 15
-               };
+               });
 
                expect(point.inRange(10, 15)).toBe(true);
                expect(point.inRange(10, 10)).toBe(false);
@@ -36,18 +30,15 @@ describe('Chart.elements.Point', function() {
        });
 
        it ('should get the correct tooltip position', function() {
+               // Mock out the point as if we were made by the controller
                var point = new Chart.elements.Point({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Attach a view object as if we were the controller
-               point._view = {
-                       radius: 2,
-                       borderWidth: 6,
+                       options: {
+                               radius: 2,
+                               borderWidth: 6,
+                       },
                        x: 10,
                        y: 15
-               };
+               });
 
                expect(point.tooltipPosition()).toEqual({
                        x: 10,
@@ -57,34 +48,31 @@ describe('Chart.elements.Point', function() {
        });
 
        it('should get the correct center point', function() {
+               // Mock out the point as if we were made by the controller
                var point = new Chart.elements.Point({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Attach a view object as if we were the controller
-               point._view = {
-                       radius: 2,
+                       options: {
+                               radius: 2,
+                       },
                        x: 10,
                        y: 10
-               };
+               });
 
                expect(point.getCenterPoint()).toEqual({x: 10, y: 10});
        });
 
        it ('should not draw if skipped', function() {
                var mockContext = window.createMockContext();
-               var point = new Chart.elements.Point();
 
-               // Attach a view object as if we were the controller
-               point._view = {
-                       radius: 2,
-                       hitRadius: 3,
+               // Mock out the point as if we were made by the controller
+               var point = new Chart.elements.Point({
+                       options: {
+                               radius: 2,
+                               hitRadius: 3,
+                       },
                        x: 10,
                        y: 15,
-                       ctx: mockContext,
                        skip: true
-               };
+               });
 
                point.draw(mockContext);
 
index 686eea9324bdff977daf4e15d461702b30a75015..e85d7f2027c247507050d54ba054b354ff85b2ce 100644 (file)
@@ -14,20 +14,11 @@ describe('Rectangle element tests', function() {
 
        it('Should correctly identify as in range', function() {
                var rectangle = new Chart.elements.Rectangle({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Safely handles if these are called before the viewmodel is instantiated
-               expect(rectangle.inRange(5)).toBe(false);
-
-               // Attach a view object as if we were the controller
-               rectangle._view = {
                        base: 0,
                        width: 4,
                        x: 10,
                        y: 15
-               };
+               });
 
                expect(rectangle.inRange(10, 15)).toBe(true);
                expect(rectangle.inRange(10, 10)).toBe(true);
@@ -36,17 +27,11 @@ describe('Rectangle element tests', function() {
 
                // Test when the y is below the base (negative bar)
                var negativeRectangle = new Chart.elements.Rectangle({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Attach a view object as if we were the controller
-               negativeRectangle._view = {
                        base: 0,
                        width: 4,
                        x: 10,
                        y: -15
-               };
+               });
 
                expect(negativeRectangle.inRange(10, -16)).toBe(false);
                expect(negativeRectangle.inRange(10, 1)).toBe(false);
@@ -55,17 +40,11 @@ describe('Rectangle element tests', function() {
 
        it('should get the correct tooltip position', function() {
                var rectangle = new Chart.elements.Rectangle({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Attach a view object as if we were the controller
-               rectangle._view = {
                        base: 0,
                        width: 4,
                        x: 10,
                        y: 15
-               };
+               });
 
                expect(rectangle.tooltipPosition()).toEqual({
                        x: 10,
@@ -74,17 +53,11 @@ describe('Rectangle element tests', function() {
 
                // Test when the y is below the base (negative bar)
                var negativeRectangle = new Chart.elements.Rectangle({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Attach a view object as if we were the controller
-               negativeRectangle._view = {
                        base: -10,
                        width: 4,
                        x: 10,
                        y: -15
-               };
+               });
 
                expect(negativeRectangle.tooltipPosition()).toEqual({
                        x: 10,
@@ -94,17 +67,11 @@ describe('Rectangle element tests', function() {
 
        it('should get the center', function() {
                var rectangle = new Chart.elements.Rectangle({
-                       _datasetIndex: 2,
-                       _index: 1
-               });
-
-               // Attach a view object as if we were the controller
-               rectangle._view = {
                        base: 0,
                        width: 4,
                        x: 10,
                        y: 15
-               };
+               });
 
                expect(rectangle.getCenterPoint()).toEqual({x: 10, y: 7.5});
        });
index e1188458a91cdc85c0071a2ca92db2248c51fd61..4da3d4ae18b5769d98d3eb19d9968de3e3be19f2 100644 (file)
@@ -22,8 +22,8 @@ describe('Default Configs', function() {
                        chart.tooltip.update();
 
                        // Title is always blank
-                       expect(chart.tooltip._model.title).toEqual([]);
-                       expect(chart.tooltip._model.body).toEqual([{
+                       expect(chart.tooltip.title).toEqual([]);
+                       expect(chart.tooltip.body).toEqual([{
                                before: [],
                                lines: ['My dataset: (10, 12, 5)'],
                                after: []
@@ -50,8 +50,8 @@ describe('Default Configs', function() {
                        chart.tooltip.update();
 
                        // Title is always blank
-                       expect(chart.tooltip._model.title).toEqual([]);
-                       expect(chart.tooltip._model.body).toEqual([{
+                       expect(chart.tooltip.title).toEqual([]);
+                       expect(chart.tooltip.body).toEqual([{
                                before: [],
                                lines: ['label2: 20'],
                                after: []
@@ -76,8 +76,8 @@ describe('Default Configs', function() {
                        chart.tooltip.update();
 
                        // Title is always blank
-                       expect(chart.tooltip._model.title).toEqual([]);
-                       expect(chart.tooltip._model.body).toEqual([{
+                       expect(chart.tooltip.title).toEqual([]);
+                       expect(chart.tooltip.body).toEqual([{
                                before: [],
                                lines: [
                                        'row1: 20',
@@ -196,8 +196,8 @@ describe('Default Configs', function() {
                        chart.tooltip.update();
 
                        // Title is always blank
-                       expect(chart.tooltip._model.title).toEqual([]);
-                       expect(chart.tooltip._model.body).toEqual([{
+                       expect(chart.tooltip.title).toEqual([]);
+                       expect(chart.tooltip.body).toEqual([{
                                before: [],
                                lines: ['label2: 20'],
                                after: []
index fea8adcccecfd7ee0cbea8f1675140a7ba2bd991..d0f4e7115f6270b4e0b1043c78fce70f28b2eb17 100644 (file)
@@ -49,163 +49,135 @@ describe('Curve helper tests', function() {
 
        it('should spline curves with monotone cubic interpolation', function() {
                var dataPoints = [
-                       {_model: {x: 0, y: 0, skip: false}},
-                       {_model: {x: 3, y: 6, skip: false}},
-                       {_model: {x: 9, y: 6, skip: false}},
-                       {_model: {x: 12, y: 60, skip: false}},
-                       {_model: {x: 15, y: 60, skip: false}},
-                       {_model: {x: 18, y: 120, skip: false}},
-                       {_model: {x: null, y: null, skip: true}},
-                       {_model: {x: 21, y: 180, skip: false}},
-                       {_model: {x: 24, y: 120, skip: false}},
-                       {_model: {x: 27, y: 125, skip: false}},
-                       {_model: {x: 30, y: 105, skip: false}},
-                       {_model: {x: 33, y: 110, skip: false}},
-                       {_model: {x: 33, y: 110, skip: false}},
-                       {_model: {x: 36, y: 170, skip: false}}
+                       {x: 0, y: 0, skip: false},
+                       {x: 3, y: 6, skip: false},
+                       {x: 9, y: 6, skip: false},
+                       {x: 12, y: 60, skip: false},
+                       {x: 15, y: 60, skip: false},
+                       {x: 18, y: 120, skip: false},
+                       {x: null, y: null, skip: true},
+                       {x: 21, y: 180, skip: false},
+                       {x: 24, y: 120, skip: false},
+                       {x: 27, y: 125, skip: false},
+                       {x: 30, y: 105, skip: false},
+                       {x: 33, y: 110, skip: false},
+                       {x: 33, y: 110, skip: false},
+                       {x: 36, y: 170, skip: false}
                ];
                helpers.splineCurveMonotone(dataPoints);
                expect(dataPoints).toEqual([{
-                       _model: {
-                               x: 0,
-                               y: 0,
-                               skip: false,
-                               controlPointNextX: 1,
-                               controlPointNextY: 2
-                       }
+                       x: 0,
+                       y: 0,
+                       skip: false,
+                       controlPointNextX: 1,
+                       controlPointNextY: 2
                },
                {
-                       _model: {
-                               x: 3,
-                               y: 6,
-                               skip: false,
-                               controlPointPreviousX: 2,
-                               controlPointPreviousY: 6,
-                               controlPointNextX: 5,
-                               controlPointNextY: 6
-                       }
+                       x: 3,
+                       y: 6,
+                       skip: false,
+                       controlPointPreviousX: 2,
+                       controlPointPreviousY: 6,
+                       controlPointNextX: 5,
+                       controlPointNextY: 6
                },
                {
-                       _model: {
-                               x: 9,
-                               y: 6,
-                               skip: false,
-                               controlPointPreviousX: 7,
-                               controlPointPreviousY: 6,
-                               controlPointNextX: 10,
-                               controlPointNextY: 6
-                       }
+                       x: 9,
+                       y: 6,
+                       skip: false,
+                       controlPointPreviousX: 7,
+                       controlPointPreviousY: 6,
+                       controlPointNextX: 10,
+                       controlPointNextY: 6
                },
                {
-                       _model: {
-                               x: 12,
-                               y: 60,
-                               skip: false,
-                               controlPointPreviousX: 11,
-                               controlPointPreviousY: 60,
-                               controlPointNextX: 13,
-                               controlPointNextY: 60
-                       }
+                       x: 12,
+                       y: 60,
+                       skip: false,
+                       controlPointPreviousX: 11,
+                       controlPointPreviousY: 60,
+                       controlPointNextX: 13,
+                       controlPointNextY: 60
                },
                {
-                       _model: {
-                               x: 15,
-                               y: 60,
-                               skip: false,
-                               controlPointPreviousX: 14,
-                               controlPointPreviousY: 60,
-                               controlPointNextX: 16,
-                               controlPointNextY: 60
-                       }
+                       x: 15,
+                       y: 60,
+                       skip: false,
+                       controlPointPreviousX: 14,
+                       controlPointPreviousY: 60,
+                       controlPointNextX: 16,
+                       controlPointNextY: 60
                },
                {
-                       _model: {
-                               x: 18,
-                               y: 120,
-                               skip: false,
-                               controlPointPreviousX: 17,
-                               controlPointPreviousY: 100
-                       }
+                       x: 18,
+                       y: 120,
+                       skip: false,
+                       controlPointPreviousX: 17,
+                       controlPointPreviousY: 100
                },
                {
-                       _model: {
-                               x: null,
-                               y: null,
-                               skip: true
-                       }
+                       x: null,
+                       y: null,
+                       skip: true
                },
                {
-                       _model: {
-                               x: 21,
-                               y: 180,
-                               skip: false,
-                               controlPointNextX: 22,
-                               controlPointNextY: 160
-                       }
+                       x: 21,
+                       y: 180,
+                       skip: false,
+                       controlPointNextX: 22,
+                       controlPointNextY: 160
                },
                {
-                       _model: {
-                               x: 24,
-                               y: 120,
-                               skip: false,
-                               controlPointPreviousX: 23,
-                               controlPointPreviousY: 120,
-                               controlPointNextX: 25,
-                               controlPointNextY: 120
-                       }
+                       x: 24,
+                       y: 120,
+                       skip: false,
+                       controlPointPreviousX: 23,
+                       controlPointPreviousY: 120,
+                       controlPointNextX: 25,
+                       controlPointNextY: 120
                },
                {
-                       _model: {
-                               x: 27,
-                               y: 125,
-                               skip: false,
-                               controlPointPreviousX: 26,
-                               controlPointPreviousY: 125,
-                               controlPointNextX: 28,
-                               controlPointNextY: 125
-                       }
+                       x: 27,
+                       y: 125,
+                       skip: false,
+                       controlPointPreviousX: 26,
+                       controlPointPreviousY: 125,
+                       controlPointNextX: 28,
+                       controlPointNextY: 125
                },
                {
-                       _model: {
-                               x: 30,
-                               y: 105,
-                               skip: false,
-                               controlPointPreviousX: 29,
-                               controlPointPreviousY: 105,
-                               controlPointNextX: 31,
-                               controlPointNextY: 105
-                       }
+                       x: 30,
+                       y: 105,
+                       skip: false,
+                       controlPointPreviousX: 29,
+                       controlPointPreviousY: 105,
+                       controlPointNextX: 31,
+                       controlPointNextY: 105
                },
                {
-                       _model: {
-                               x: 33,
-                               y: 110,
-                               skip: false,
-                               controlPointPreviousX: 32,
-                               controlPointPreviousY: 110,
-                               controlPointNextX: 33,
-                               controlPointNextY: 110
-                       }
+                       x: 33,
+                       y: 110,
+                       skip: false,
+                       controlPointPreviousX: 32,
+                       controlPointPreviousY: 110,
+                       controlPointNextX: 33,
+                       controlPointNextY: 110
                },
                {
-                       _model: {
-                               x: 33,
-                               y: 110,
-                               skip: false,
-                               controlPointPreviousX: 33,
-                               controlPointPreviousY: 110,
-                               controlPointNextX: 34,
-                               controlPointNextY: 110
-                       }
+                       x: 33,
+                       y: 110,
+                       skip: false,
+                       controlPointPreviousX: 33,
+                       controlPointPreviousY: 110,
+                       controlPointNextX: 34,
+                       controlPointNextY: 110
                },
                {
-                       _model: {
-                               x: 36,
-                               y: 170,
-                               skip: false,
-                               controlPointPreviousX: 35,
-                               controlPointPreviousY: 150
-                       }
+                       x: 36,
+                       y: 170,
+                       skip: false,
+                       controlPointPreviousX: 35,
+                       controlPointPreviousY: 150
                }]);
        });
 });
index 880a72c6532d799c02447c49c14171612167d3be..671e3af2c4814c46f816ec9ecdffc9810d04e3ca 100644 (file)
@@ -149,7 +149,7 @@ describe('Legend block tests', function() {
                        datasetIndex: 1
                }, {
                        text: 'dataset3',
-                       fillStyle: 'green',
+                       fillStyle: 'rgba(0,0,0,0.1)',
                        hidden: false,
                        lineCap: 'butt',
                        lineDash: [],
@@ -198,7 +198,7 @@ describe('Legend block tests', function() {
 
                expect(chart.legend.legendItems).toEqual([{
                        text: 'dataset3',
-                       fillStyle: 'green',
+                       fillStyle: 'rgba(0,0,0,0.1)',
                        hidden: false,
                        lineCap: 'butt',
                        lineDash: [],
index b082a2f03ca127358cb3b0bc8f63cb2a02635552..e2d3e7a28773756d330efea6aeb350839662223e 100644 (file)
@@ -113,8 +113,6 @@ function _resolveElementPoint(el) {
                        point = el.getCenterPoint();
                } else if (el.x !== undefined && el.y !== undefined) {
                        point = el;
-               } else if (el._model && el._model.x !== undefined && el._model.y !== undefined) {
-                       point = el._model;
                }
        }
        return point;