]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Move some of the radial linear scale functions into the radialLinear scale
authorEvert Timberg <evert.timberg@gmail.com>
Sat, 13 Jun 2015 13:51:28 +0000 (09:51 -0400)
committerEvert Timberg <evert.timberg@gmail.com>
Sat, 13 Jun 2015 13:51:28 +0000 (09:51 -0400)
src/charts/chart.polarArea.js
src/charts/chart.radar.js
src/scales/scale.radialLinear.js

index 4c627748855d6ef4a5cee18f9313ecf9e45df4c1..72f4b8f76682c7caf49c8cfd53c4f8153c8419f7 100644 (file)
 (function() {
-    "use strict";
-
-    var root = this,
-        Chart = root.Chart,
-        //Cache a local reference to Chart.helpers
-        helpers = Chart.helpers;
-
-    var defaultConfig = {
-
-        scale: {
-            type: "radialLinear",
-            display: true,
-
-            //Boolean - Whether to animate scaling the chart from the centre
-            animate: false,
-
-            lineArc: true,
-
-            // grid line settings
-            gridLines: {
-                show: true,
-                color: "rgba(0, 0, 0, 0.05)",
-                lineWidth: 1,
-            },
-
-            // scale numbers
-            beginAtZero: true,
-
-            // label settings
-            labels: {
-                show: true,
-                template: "<%=value.toLocaleString()%>",
-                fontSize: 12,
-                fontStyle: "normal",
-                fontColor: "#666",
-                fontFamily: "Helvetica Neue",
-
-                //Boolean - Show a backdrop to the scale label
-                showLabelBackdrop: true,
-
-                //String - The colour of the label backdrop
-                backdropColor: "rgba(255,255,255,0.75)",
-
-                //Number - The backdrop padding above & below the label in pixels
-                backdropPaddingY: 2,
-
-                //Number - The backdrop padding to the side of the label in pixels
-                backdropPaddingX: 2,
-            }
-        },
-
-        //Boolean - Whether to animate the rotation of the chart
-        animateRotate: true,
-    };
-
-
-    Chart.Type.extend({
-        //Passing in a name registers this chart in the Chart namespace
-        name: "PolarArea",
-        //Providing a defaults will also register the deafults in the chart namespace
-        defaults: defaultConfig,
-        //Initialize is fired when the chart is initialized - Data is passed in as a parameter
-        //Config is automatically merged by the core of Chart.js, and is available at this.options
-        initialize: function() {
-
-            // Scale setup
-            var self = this;
-            var ScaleClass = Chart.scales.getScaleConstructor(this.options.scale.type);
-            this.scale = new ScaleClass({
-                options: this.options.scale,
-                lineArc: true,
-                width: this.chart.width,
-                height: this.chart.height,
-                xCenter: this.chart.width / 2,
-                yCenter: this.chart.height / 2,
-                ctx: this.chart.ctx,
-                valuesCount: this.data.length,
-                calculateRange: function() {
-                    this.min = null;
-                    this.max = null;
-
-                    helpers.each(self.data.datasets[0].data, function(value) {
-                        if (this.min === null) {
-                            this.min = value;
-                        } else if (value < this.min) {
-                            this.min = value;
-                        }
-
-                        if (this.max === null) {
-                            this.max = value;
-                        } else if (value > this.max) {
-                            this.max = value;
-                        }
-                    }, this);
-                }
-            });
-
-            helpers.bindEvents(this, this.options.events, this.events);
-
-            //Set up tooltip events on the chart
-            helpers.bindEvents(this, this.options.events, this.events);
-
-            //Create a new bar for each piece of data
-            helpers.each(this.data.datasets, function(dataset, datasetIndex) {
-                dataset.metaData = [];
-                helpers.each(dataset.data, function(dataPoint, index) {
-                    dataset.metaData.push(new Chart.Arc({
-                        _chart: this.chart,
-                        _datasetIndex: datasetIndex,
-                        _index: index,
-                        _model: {}
-                    }));
-                }, this);
-            }, this);
-
-            // Create tooltip instance exclusively for this chart with some defaults.
-            this.tooltip = new Chart.Tooltip({
-                _chart: this.chart,
-                _data: this.data,
-                _options: this.options,
-            }, this);
-
-            // Fit the scale before we animate
-            this.updateScaleRange();
-            this.scale.calculateRange();
-            Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
-            // so that we animate nicely
-            this.resetElements();
-
-            // Update the chart with the latest data.
-            this.update();
-
-        },
-        updateScaleRange: function() {
-            helpers.extend(this.scale, {
-                size: helpers.min([this.chart.width, this.chart.height]),
-                xCenter: this.chart.width / 2,
-                yCenter: this.chart.height / 2
-            });
-        },
-        resetElements: function() {
-            var circumference = 1 / this.data.datasets[0].data.length * 2;
-
-            // Map new data to data points
-            helpers.each(this.data.datasets[0].metaData, function(slice, index) {
-
-                var value = this.data.datasets[0].data[index];
-
-                helpers.extend(slice, {
-                    _index: index,
-                    _model: {
-                        x: this.chart.width / 2,
-                        y: this.chart.height / 2,
-                        innerRadius: 0,
-                        outerRadius: 0,
-                        startAngle: Math.PI * -0.5,
-                        endAngle: Math.PI * -0.5,
-
-                        backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.arc.backgroundColor),
-                        hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
-                        borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.arc.borderWidth),
-                        borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.arc.borderColor),
-
-                        label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
-                    },
-                });
-
-                slice.pivot();
-            }, this);
-        },
-        update: function(animationDuration) {
-
-            this.updateScaleRange();
-            this.scale.calculateRange();
-            this.scale.generateTicks();
-            this.scale.buildYLabels();
-
-            Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
-            var circumference = 1 / this.data.datasets[0].data.length * 2;
-
-            // Map new data to data points
-            helpers.each(this.data.datasets[0].metaData, function(slice, index) {
-
-                var value = this.data.datasets[0].data[index];
-
-                var startAngle = (-0.5 * Math.PI) + (Math.PI * circumference) * index;
-                var endAngle = startAngle + (circumference * Math.PI);
-
-                helpers.extend(slice, {
-                    _index: index,
-                    _model: {
-                        x: this.chart.width / 2,
-                        y: this.chart.height / 2,
-                        innerRadius: 0,
-                        outerRadius: this.scale.calculateCenterOffset(value),
-                        startAngle: startAngle,
-                        endAngle: endAngle,
-
-                        backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.arc.backgroundColor),
-                        hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
-                        borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.arc.borderWidth),
-                        borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.arc.borderColor),
-
-                        label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
-                    },
-                });
-                slice.pivot();
-
-                console.log(slice);
-
-            }, this);
-
-            this.render(animationDuration);
-        },
-        draw: function(ease) {
-            var easingDecimal = ease || 1;
-
-            this.clear();
-
-            helpers.each(this.data.datasets[0].metaData, function(slice, index) {
-                slice.transition(easingDecimal).draw();
-            }, this);
-
-            this.scale.draw();
-
-            this.tooltip.transition(easingDecimal).draw();
-        },
-        events: function(e) {
-
-            // If exiting chart
-            if (e.type == 'mouseout') {
-                return this;
-            }
-
-            this.lastActive = this.lastActive || [];
-
-            // Find Active Elements
-            this.active = function() {
-                switch (this.options.hover.mode) {
-                    case 'single':
-                        return this.getSliceAtEvent(e);
-                    case 'label':
-                        return this.getSlicesAtEvent(e);
-                    case 'dataset':
-                        return this.getDatasetAtEvent(e);
-                    default:
-                        return e;
-                }
-            }.call(this);
-
-            // On Hover hook
-            if (this.options.hover.onHover) {
-                this.options.hover.onHover.call(this, this.active);
-            }
-
-            if (e.type == 'mouseup' || e.type == 'click') {
-                if (this.options.onClick) {
-                    this.options.onClick.call(this, e, this.active);
-                }
-            }
-
-            var dataset;
-            var index;
-            // Remove styling for last active (even if it may still be active)
-            if (this.lastActive.length) {
-                switch (this.options.hover.mode) {
-                    case 'single':
-                        dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
-                        index = this.lastActive[0]._index;
-
-                        this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
-                        this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
-                        this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
-                        break;
-                    case 'label':
-                        for (var i = 0; i < this.lastActive.length; i++) {
-                            dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
-                            index = this.lastActive[i]._index;
-
-                            this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
-                            this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
-                            this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
-                        }
-                        break;
-                    case 'dataset':
-                        break;
-                    default:
-                        // Don't change anything
-                }
-            }
-
-            // Built in hover styling
-            if (this.active.length && this.options.hover.mode) {
-                switch (this.options.hover.mode) {
-                    case 'single':
-                        dataset = this.data.datasets[this.active[0]._datasetIndex];
-                        index = this.active[0]._index;
-
-                        this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 1);
-                        this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
-                        this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
-                        this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth);
-                        break;
-                    case 'label':
-                        for (var i = 0; i < this.active.length; i++) {
-                            dataset = this.data.datasets[this.active[i]._datasetIndex];
-                            index = this.active[i]._index;
-
-                            this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 1);
-                            this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
-                            this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
-                            this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth);
-                        }
-                        break;
-                    case 'dataset':
-                        break;
-                    default:
-                        // Don't change anything
-                }
-            }
-
-
-            // Built in Tooltips
-            if (this.options.tooltips.enabled) {
-
-                // The usual updates
-                this.tooltip.initialize();
-
-                // Active
-                if (this.active.length) {
-                    this.tooltip._model.opacity = 1;
-
-                    helpers.extend(this.tooltip, {
-                        _active: this.active,
-                    });
-
-                    this.tooltip.update();
-                } else {
-                    // Inactive
-                    this.tooltip._model.opacity = 0;
-                }
-            }
-
-
-            // Hover animations
-            this.tooltip.pivot();
-
-            if (!this.animating) {
-                var changed;
-
-                helpers.each(this.active, function(element, index) {
-                    if (element !== this.lastActive[index]) {
-                        changed = true;
-                    }
-                }, this);
-
-                // If entering, leaving, or changing elements, animate the change via pivot
-                if ((!this.lastActive.length && this.active.length) ||
-                    (this.lastActive.length && !this.active.length) ||
-                    (this.lastActive.length && this.active.length && changed)) {
-
-                    this.stop();
-                    this.render(this.options.hover.animationDuration);
-                }
-            }
-
-            // Remember Last Active
-            this.lastActive = this.active;
-            return this;
-        },
-        getSliceAtEvent: function(e) {
-            var elements = [];
-
-            var location = helpers.getRelativePosition(e);
-
-            this.eachElement(function(slice, index) {
-                if (slice.inRange(location.x, location.y)) {
-                    elements.push(slice);
-                }
-            }, this);
-            return elements;
-        },
-        /*getSlicesAtEvent: function(e) {
-            var elements = [];
-
-            var location = helpers.getRelativePosition(e);
-
-            this.eachElement(function(slice, index) {
-                if (slice.inGroupRange(location.x, location.y)) {
-                    elements.push(slice);
-                }
-            }, this);
-            return elements;
-        },*/
-    });
+       "use strict";
+
+       var root = this,
+               Chart = root.Chart,
+               //Cache a local reference to Chart.helpers
+               helpers = Chart.helpers;
+
+       var defaultConfig = {
+
+               scale: {
+                       type: "radialLinear",
+                       display: true,
+
+                       //Boolean - Whether to animate scaling the chart from the centre
+                       animate: false,
+
+                       lineArc: true,
+
+                       // grid line settings
+                       gridLines: {
+                               show: true,
+                               color: "rgba(0, 0, 0, 0.05)",
+                               lineWidth: 1,
+                       },
+
+                       // scale numbers
+                       beginAtZero: true,
+
+                       // label settings
+                       labels: {
+                               show: true,
+                               template: "<%=value.toLocaleString()%>",
+                               fontSize: 12,
+                               fontStyle: "normal",
+                               fontColor: "#666",
+                               fontFamily: "Helvetica Neue",
+
+                               //Boolean - Show a backdrop to the scale label
+                               showLabelBackdrop: true,
+
+                               //String - The colour of the label backdrop
+                               backdropColor: "rgba(255,255,255,0.75)",
+
+                               //Number - The backdrop padding above & below the label in pixels
+                               backdropPaddingY: 2,
+
+                               //Number - The backdrop padding to the side of the label in pixels
+                               backdropPaddingX: 2,
+                       }
+               },
+
+               //Boolean - Whether to animate the rotation of the chart
+               animateRotate: true,
+       };
+
+
+       Chart.Type.extend({
+               //Passing in a name registers this chart in the Chart namespace
+               name: "PolarArea",
+               //Providing a defaults will also register the deafults in the chart namespace
+               defaults: defaultConfig,
+               //Initialize is fired when the chart is initialized - Data is passed in as a parameter
+               //Config is automatically merged by the core of Chart.js, and is available at this.options
+               initialize: function() {
+
+                       // Scale setup
+                       var self = this;
+                       var ScaleClass = Chart.scales.getScaleConstructor(this.options.scale.type);
+                       this.scale = new ScaleClass({
+                               options: this.options.scale,
+                               lineArc: true,
+                               width: this.chart.width,
+                               height: this.chart.height,
+                               xCenter: this.chart.width / 2,
+                               yCenter: this.chart.height / 2,
+                               ctx: this.chart.ctx,
+                               valuesCount: this.data.length,
+                               data: this.data
+                       });
+
+                       helpers.bindEvents(this, this.options.events, this.events);
+
+                       //Set up tooltip events on the chart
+                       helpers.bindEvents(this, this.options.events, this.events);
+
+                       //Create a new bar for each piece of data
+                       helpers.each(this.data.datasets, function(dataset, datasetIndex) {
+                               dataset.metaData = [];
+                               helpers.each(dataset.data, function(dataPoint, index) {
+                                       dataset.metaData.push(new Chart.Arc({
+                                               _chart: this.chart,
+                                               _datasetIndex: datasetIndex,
+                                               _index: index,
+                                               _model: {}
+                                       }));
+                               }, this);
+                       }, this);
+
+                       // Create tooltip instance exclusively for this chart with some defaults.
+                       this.tooltip = new Chart.Tooltip({
+                               _chart: this.chart,
+                               _data: this.data,
+                               _options: this.options,
+                       }, this);
+
+                       // Fit the scale before we animate
+                       this.updateScaleRange();
+                       this.scale.calculateRange();
+                       Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
+
+                       // so that we animate nicely
+                       this.resetElements();
+
+                       // Update the chart with the latest data.
+                       this.update();
+
+               },
+               updateScaleRange: function() {
+                       helpers.extend(this.scale, {
+                               size: helpers.min([this.chart.width, this.chart.height]),
+                               xCenter: this.chart.width / 2,
+                               yCenter: this.chart.height / 2
+                       });
+               },
+               resetElements: function() {
+                       var circumference = 1 / this.data.datasets[0].data.length * 2;
+
+                       // Map new data to data points
+                       helpers.each(this.data.datasets[0].metaData, function(slice, index) {
+
+                               var value = this.data.datasets[0].data[index];
+
+                               helpers.extend(slice, {
+                                       _index: index,
+                                       _model: {
+                                               x: this.chart.width / 2,
+                                               y: this.chart.height / 2,
+                                               innerRadius: 0,
+                                               outerRadius: 0,
+                                               startAngle: Math.PI * -0.5,
+                                               endAngle: Math.PI * -0.5,
+
+                                               backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.arc.backgroundColor),
+                                               hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
+                                               borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.arc.borderWidth),
+                                               borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.arc.borderColor),
+
+                                               label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
+                                       },
+                               });
+
+                               slice.pivot();
+                       }, this);
+               },
+               update: function(animationDuration) {
+
+                       this.updateScaleRange();
+                       this.scale.calculateRange();
+                       this.scale.generateTicks();
+                       this.scale.buildYLabels();
+
+                       Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
+
+                       var circumference = 1 / this.data.datasets[0].data.length * 2;
+
+                       // Map new data to data points
+                       helpers.each(this.data.datasets[0].metaData, function(slice, index) {
+
+                               var value = this.data.datasets[0].data[index];
+
+                               var startAngle = (-0.5 * Math.PI) + (Math.PI * circumference) * index;
+                               var endAngle = startAngle + (circumference * Math.PI);
+
+                               helpers.extend(slice, {
+                                       _index: index,
+                                       _model: {
+                                               x: this.chart.width / 2,
+                                               y: this.chart.height / 2,
+                                               innerRadius: 0,
+                                               outerRadius: this.scale.calculateCenterOffset(value),
+                                               startAngle: startAngle,
+                                               endAngle: endAngle,
+
+                                               backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].backgroundColor, index, this.options.elements.arc.backgroundColor),
+                                               hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
+                                               borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderWidth, index, this.options.elements.arc.borderWidth),
+                                               borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[0].borderColor, index, this.options.elements.arc.borderColor),
+
+                                               label: helpers.getValueAtIndexOrDefault(this.data.datasets[0].labels, index, this.data.datasets[0].labels[index])
+                                       },
+                               });
+                               slice.pivot();
+
+                               console.log(slice);
+
+                       }, this);
+
+                       this.render(animationDuration);
+               },
+               draw: function(ease) {
+                       var easingDecimal = ease || 1;
+
+                       this.clear();
+
+                       helpers.each(this.data.datasets[0].metaData, function(slice, index) {
+                               slice.transition(easingDecimal).draw();
+                       }, this);
+
+                       this.scale.draw();
+
+                       this.tooltip.transition(easingDecimal).draw();
+               },
+               events: function(e) {
+
+                       // If exiting chart
+                       if (e.type == 'mouseout') {
+                               return this;
+                       }
+
+                       this.lastActive = this.lastActive || [];
+
+                       // Find Active Elements
+                       this.active = function() {
+                               switch (this.options.hover.mode) {
+                                       case 'single':
+                                               return this.getSliceAtEvent(e);
+                                       case 'label':
+                                               return this.getSlicesAtEvent(e);
+                                       case 'dataset':
+                                               return this.getDatasetAtEvent(e);
+                                       default:
+                                               return e;
+                               }
+                       }.call(this);
+
+                       // On Hover hook
+                       if (this.options.hover.onHover) {
+                               this.options.hover.onHover.call(this, this.active);
+                       }
+
+                       if (e.type == 'mouseup' || e.type == 'click') {
+                               if (this.options.onClick) {
+                                       this.options.onClick.call(this, e, this.active);
+                               }
+                       }
+
+                       var dataset;
+                       var index;
+                       // Remove styling for last active (even if it may still be active)
+                       if (this.lastActive.length) {
+                               switch (this.options.hover.mode) {
+                                       case 'single':
+                                               dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
+                                               index = this.lastActive[0]._index;
+
+                                               this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
+                                               this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
+                                               this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
+                                               break;
+                                       case 'label':
+                                               for (var i = 0; i < this.lastActive.length; i++) {
+                                                       dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
+                                                       index = this.lastActive[i]._index;
+
+                                                       this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor);
+                                                       this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor);
+                                                       this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth);
+                                               }
+                                               break;
+                                       case 'dataset':
+                                               break;
+                                       default:
+                                               // Don't change anything
+                               }
+                       }
+
+                       // Built in hover styling
+                       if (this.active.length && this.options.hover.mode) {
+                               switch (this.options.hover.mode) {
+                                       case 'single':
+                                               dataset = this.data.datasets[this.active[0]._datasetIndex];
+                                               index = this.active[0]._index;
+
+                                               this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 1);
+                                               this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
+                                               this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
+                                               this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth);
+                                               break;
+                                       case 'label':
+                                               for (var i = 0; i < this.active.length; i++) {
+                                                       dataset = this.data.datasets[this.active[i]._datasetIndex];
+                                                       index = this.active[i]._index;
+
+                                                       this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 1);
+                                                       this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
+                                                       this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
+                                                       this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth);
+                                               }
+                                               break;
+                                       case 'dataset':
+                                               break;
+                                       default:
+                                               // Don't change anything
+                               }
+                       }
+
+
+                       // Built in Tooltips
+                       if (this.options.tooltips.enabled) {
+
+                               // The usual updates
+                               this.tooltip.initialize();
+
+                               // Active
+                               if (this.active.length) {
+                                       this.tooltip._model.opacity = 1;
+
+                                       helpers.extend(this.tooltip, {
+                                               _active: this.active,
+                                       });
+
+                                       this.tooltip.update();
+                               } else {
+                                       // Inactive
+                                       this.tooltip._model.opacity = 0;
+                               }
+                       }
+
+
+                       // Hover animations
+                       this.tooltip.pivot();
+
+                       if (!this.animating) {
+                               var changed;
+
+                               helpers.each(this.active, function(element, index) {
+                                       if (element !== this.lastActive[index]) {
+                                               changed = true;
+                                       }
+                               }, this);
+
+                               // If entering, leaving, or changing elements, animate the change via pivot
+                               if ((!this.lastActive.length && this.active.length) ||
+                                       (this.lastActive.length && !this.active.length) ||
+                                       (this.lastActive.length && this.active.length && changed)) {
+
+                                       this.stop();
+                                       this.render(this.options.hover.animationDuration);
+                               }
+                       }
+
+                       // Remember Last Active
+                       this.lastActive = this.active;
+                       return this;
+               },
+               getSliceAtEvent: function(e) {
+                       var elements = [];
+
+                       var location = helpers.getRelativePosition(e);
+
+                       this.eachElement(function(slice, index) {
+                               if (slice.inRange(location.x, location.y)) {
+                                       elements.push(slice);
+                               }
+                       }, this);
+                       return elements;
+               },
+               /*getSlicesAtEvent: function(e) {
+                       var elements = [];
+
+                       var location = helpers.getRelativePosition(e);
+
+                       this.eachElement(function(slice, index) {
+                               if (slice.inGroupRange(location.x, location.y)) {
+                                       elements.push(slice);
+                               }
+                       }, this);
+                       return elements;
+               },*/
+       });
 
 }).call(this);
index 57c7351369b229d8c7b935b9f76f8c91474d83b9..2fcf88ba050d94aa44c1594675d1b721e9d3314b 100644 (file)
 (function() {
-    "use strict";
+       "use strict";
 
-    var root = this,
-        Chart = root.Chart,
-        helpers = Chart.helpers;
+       var root = this,
+               Chart = root.Chart,
+               helpers = Chart.helpers;
 
 
 
-    Chart.Type.extend({
-        name: "Radar",
-        defaults: {
+       Chart.Type.extend({
+               name: "Radar",
+               defaults: {
 
-            scale: {
-                type: "radialLinear",
-                display: true,
+                       scale: {
+                               type: "radialLinear",
+                               display: true,
 
-                //Boolean - Whether to animate scaling the chart from the centre
-                animate: false,
+                               //Boolean - Whether to animate scaling the chart from the centre
+                               animate: false,
 
-                lineArc: false,
+                               lineArc: false,
 
-                // grid line settings
-                gridLines: {
-                    show: true,
-                    color: "rgba(0, 0, 0, 0.05)",
-                    lineWidth: 1,
-                },
+                               // grid line settings
+                               gridLines: {
+                                       show: true,
+                                       color: "rgba(0, 0, 0, 0.05)",
+                                       lineWidth: 1,
+                               },
 
-                angleLines: {
-                    show: true,
-                    color: "rgba(0,0,0,.1)",
-                    lineWidth: 1
-                },
+                               angleLines: {
+                                       show: true,
+                                       color: "rgba(0,0,0,.1)",
+                                       lineWidth: 1
+                               },
 
-                // scale numbers
-                beginAtZero: true,
+                               // scale numbers
+                               beginAtZero: true,
 
-                // label settings
-                labels: {
-                    show: true,
-                    template: "<%=value.toLocaleString()%>",
-                    fontSize: 12,
-                    fontStyle: "normal",
-                    fontColor: "#666",
-                    fontFamily: "Helvetica Neue",
-
-                    //Boolean - Show a backdrop to the scale label
-                    showLabelBackdrop: true,
-
-                    //String - The colour of the label backdrop
-                    backdropColor: "rgba(255,255,255,0.75)",
-
-                    //Number - The backdrop padding above & below the label in pixels
-                    backdropPaddingY: 2,
-
-                    //Number - The backdrop padding to the side of the label in pixels
-                    backdropPaddingX: 2,
-                },
-
-                pointLabels: {
-                    //String - Point label font declaration
-                    fontFamily: "'Arial'",
-
-                    //String - Point label font weight
-                    fontStyle: "normal",
-
-                    //Number - Point label font size in pixels
-                    fontSize: 10,
-
-                    //String - Point label font colour
-                    fontColor: "#666",
-                },
-            },
-
-            elements: {
-                line: {
-                    tension: 0, // no bezier in radar
-                }
-            },
-
-            //String - A legend template
-            legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
-
-        },
-
-        initialize: function() {
-
-            // Events
-            helpers.bindEvents(this, this.options.events, this.events);
-
-            // Create a new line and its points for each dataset and piece of data
-            helpers.each(this.data.datasets, function(dataset, datasetIndex) {
-
-                dataset.metaDataset = new Chart.Line({
-                    _chart: this.chart,
-                    _datasetIndex: datasetIndex,
-                    _points: dataset.metaData,
-                    _loop: true
-                });
-
-                dataset.metaData = [];
-
-                helpers.each(dataset.data, function(dataPoint, index) {
-                    dataset.metaData.push(new Chart.Point({
-                        _datasetIndex: datasetIndex,
-                        _index: index,
-                        _chart: this.chart,
-                        _model: {
-                            x: 0, //xScale.getPixelForValue(null, index, true),
-                            y: 0, //this.chartArea.bottom,
-                        },
-                    }));
-
-                }, this);
-            }, this);
-
-            // Build the scale.
-            this.buildScale();
-
-            // Create tooltip instance exclusively for this chart with some defaults.
-            this.tooltip = new Chart.Tooltip({
-                _chart: this.chart,
-                _data: this.data,
-                _options: this.options,
-            }, this);
-
-            // Need to fit scales before we reset elements. 
-            Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
-            // Reset so that we animation from the baseline
-            this.resetElements();
-
-            // Update that shiz
-            this.update();
-        },
-        nextPoint: function(collection, index) {
-            return collection[index + 1] || collection[0];
-        },
-        previousPoint: function(collection, index) {
-            return collection[index - 1] || collection[collection.length - 1];
-        },
-        resetElements: function() {
-
-            // Update the points
-            this.eachElement(function(point, index, dataset, datasetIndex) {
-                helpers.extend(point, {
-                    // Utility
-                    _chart: this.chart,
-                    _datasetIndex: datasetIndex,
-                    _index: index,
-                    _scale: this.scale,
-
-                    // Desired view properties
-                    _model: {
-                        x: this.scale.xCenter,
-                        y: this.scale.yCenter,
-
-                        // Appearance
-                        tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
-                        radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
-                        backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
-                        borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
-                        borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
-                        skip: this.data.datasets[datasetIndex].data[index] === null,
-
-                        // Tooltip
-                        hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].hitRadius, index, this.options.elements.point.hitRadius),
-                    },
-                });
-            }, this);
-
-            // Update control points for the bezier curve
-            this.eachElement(function(point, index, dataset, datasetIndex) {
-                var controlPoints = helpers.splineCurve(
-                    this.previousPoint(dataset, index)._model,
-                    point._model,
-                    this.nextPoint(dataset, index)._model,
-                    point._model.tension
-                );
-
-                point._model.controlPointPreviousX = this.scale.xCenter;
-                point._model.controlPointPreviousY = this.scale.yCenter;
-                point._model.controlPointNextX = this.scale.xCenter;
-                point._model.controlPointNextY = this.scale.yCenter;
-
-                // Now pivot the point for animation
-                point.pivot();
-            }, this);
-        },
-        update: function(animationDuration) {
-            Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
-
-            // Update the lines
-            this.eachDataset(function(dataset, datasetIndex) {
-                var scaleBase;
-
-                if (this.scale.min < 0 && this.scale.max < 0) {
-                    scaleBase = this.scale.getPointPosition(0, this.scale.max);
-                } else if (this.scale.min > 0 && this.scale.max > 0) {
-                    scaleBase = this.scale.getPointPosition(0, this.scale.min);
-                } else {
-                    scaleBase = this.scale.getPointPosition(0, 0);
-                }
-
-                helpers.extend(dataset.metaDataset, {
-                    // Utility
-                    _datasetIndex: datasetIndex,
-
-                    // Data
-                    _children: dataset.metaData,
-
-                    // Model
-                    _model: {
-                        // Appearance
-                        tension: dataset.tension || this.options.elements.line.tension,
-                        backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
-                        borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
-                        borderColor: dataset.borderColor || this.options.elements.line.borderColor,
-                        fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
-                        skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
-                        drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
-
-                        // Scale
-                        scaleTop: this.scale.top,
-                        scaleBottom: this.scale.bottom,
-                        scaleZero: scaleBase,
-                    },
-                });
-
-                dataset.metaDataset.pivot();
-            });
-
-            // Update the points
-            this.eachElement(function(point, index, dataset, datasetIndex) {
-                var pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(this.data.datasets[datasetIndex].data[index]));
-
-                helpers.extend(point, {
-                    // Utility
-                    _chart: this.chart,
-                    _datasetIndex: datasetIndex,
-                    _index: index,
-
-                    // Desired view properties
-                    _model: {
-                        x: pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
-                        y: pointPosition.y,
-
-                        // Appearance
-                        tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
-                        radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
-                        backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
-                        borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
-                        borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
-                        skip: this.data.datasets[datasetIndex].data[index] === null,
-
-                        // Tooltip
-                        hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].hitRadius, index, this.options.elements.point.hitRadius),
-                    },
-                });
-            }, this);
-
-
-            // Update control points for the bezier curve
-            this.eachElement(function(point, index, dataset, datasetIndex) {
-                var controlPoints = helpers.splineCurve(
-                    this.previousPoint(dataset, index)._model,
-                    point._model,
-                    this.nextPoint(dataset, index)._model,
-                    point._model.tension
-                );
-
-                point._model.controlPointPreviousX = controlPoints.previous.x;
-                point._model.controlPointNextX = controlPoints.next.x;
-
-                // Prevent the bezier going outside of the bounds of the graph
-
-                // Cap puter bezier handles to the upper/lower scale bounds
-                if (controlPoints.next.y > this.chartArea.bottom) {
-                    point._model.controlPointNextY = this.chartArea.bottom;
-                } else if (controlPoints.next.y < this.chartArea.top) {
-                    point._model.controlPointNextY = this.chartArea.top;
-                } else {
-                    point._model.controlPointNextY = controlPoints.next.y;
-                }
-
-                // Cap inner bezier handles to the upper/lower scale bounds
-                if (controlPoints.previous.y > this.chartArea.bottom) {
-                    point._model.controlPointPreviousY = this.chartArea.bottom;
-                } else if (controlPoints.previous.y < this.chartArea.top) {
-                    point._model.controlPointPreviousY = this.chartArea.top;
-                } else {
-                    point._model.controlPointPreviousY = controlPoints.previous.y;
-                }
-
-                // Now pivot the point for animation
-                point.pivot();
-            }, this);
-
-            this.render(animationDuration);
-        },
-        buildScale: function() {
-            var self = this;
-
-            var ScaleConstructor = Chart.scales.getScaleConstructor(this.options.scale.type);
-            this.scale = new ScaleConstructor({
-                options: this.options.scale,
-                height: this.chart.height,
-                width: this.chart.width,
-                xCenter: this.chart.width / 2,
-                yCenter: this.chart.height / 2,
-                ctx: this.chart.ctx,
-                labels: this.data.labels,
-                valuesCount: this.data.datasets[0].data.length,
-                calculateRange: function() {
-                    this.min = null;
-                    this.max = null;
-
-                    helpers.each(self.data.datasets, function(dataset) {
-                        if (dataset.yAxisID === this.id) {
-                            helpers.each(dataset.data, function(value, index) {
-                                if (this.min === null) {
-                                    this.min = value;
-                                } else if (value < this.min) {
-                                    this.min = value;
-                                }
-
-                                if (this.max === null) {
-                                    this.max = value;
-                                } else if (value > this.max) {
-                                    this.max = value;
-                                }
-                            }, this);
-                        }
-                    }, this);
-                }
-            });
-
-            this.scale.setScaleSize();
-            this.scale.calculateRange();
-            this.scale.generateTicks();
-            this.scale.buildYLabels();
-        },
-        draw: function(ease) {
-            var easingDecimal = ease || 1;
-            this.clear();
-
-            // Draw all the scales
-            this.scale.draw(this.chartArea);
-
-            // reverse for-loop for proper stacking
-            for (var i = this.data.datasets.length - 1; i >= 0; i--) {
-
-                var dataset = this.data.datasets[i];
-
-                // Transition Point Locations
-                helpers.each(dataset.metaData, function(point, index) {
-                    point.transition(easingDecimal);
-                }, this);
-
-                // Transition and Draw the line
-                dataset.metaDataset.transition(easingDecimal).draw();
-
-                // Draw the points
-                helpers.each(dataset.metaData, function(point) {
-                    point.draw();
-                });
-            }
-
-            // Finally draw the tooltip
-            this.tooltip.transition(easingDecimal).draw();
-        },
-        events: function(e) {
-
-            this.lastActive = this.lastActive || [];
-
-            // Find Active Elements
-            // If exiting chart
-            if (e.type == 'mouseout') {
-                this.active = [];
-            } else {
-                this.active = function() {
-                    switch (this.options.hover.mode) {
-                        case 'single':
-                            return this.getElementAtEvent(e);
-                        case 'label':
-                            return this.getElementsAtEvent(e);
-                        case 'dataset':
-                            return this.getDatasetAtEvent(e);
-                        default:
-                            return e;
-                    }
-                }.call(this);
-            }
-
-            // On Hover hook
-            if (this.options.hover.onHover) {
-                this.options.hover.onHover.call(this, this.active);
-            }
-
-            if (e.type == 'mouseup' || e.type == 'click') {
-                if (this.options.onClick) {
-                    this.options.onClick.call(this, e, this.active);
-                }
-            }
-
-            var dataset;
-            var index;
-            // Remove styling for last active (even if it may still be active)
-            if (this.lastActive.length) {
-                switch (this.options.hover.mode) {
-                    case 'single':
-                        dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
-                        index = this.lastActive[0]._index;
-
-                        this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
-                        this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
-                        this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
-                        this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
-                        break;
-                    case 'label':
-                        for (var i = 0; i < this.lastActive.length; i++) {
-                            dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
-                            index = this.lastActive[i]._index;
-
-                            this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
-                            this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
-                            this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
-                            this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
-                        }
-                        break;
-                    case 'dataset':
-                        break;
-                    default:
-                        // Don't change anything
-                }
-            }
-
-            // Built in hover styling
-            if (this.active.length && this.options.hover.mode) {
-                switch (this.options.hover.mode) {
-                    case 'single':
-                        dataset = this.data.datasets[this.active[0]._datasetIndex];
-                        index = this.active[0]._index;
-
-                        this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
-                        this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
-                        this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
-                        this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
-                        break;
-                    case 'label':
-                        for (var i = 0; i < this.active.length; i++) {
-                            dataset = this.data.datasets[this.active[i]._datasetIndex];
-                            index = this.active[i]._index;
-
-                            this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
-                            this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
-                            this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
-                            this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
-                        }
-                        break;
-                    case 'dataset':
-                        break;
-                    default:
-                        // Don't change anything
-                }
-            }
-
-            // Built in Tooltips
-            if (this.options.tooltips.enabled) {
-
-                // The usual updates
-                this.tooltip.initialize();
-
-                // Active
-                if (this.active.length) {
-                    this.tooltip._model.opacity = 1;
-
-                    helpers.extend(this.tooltip, {
-                        _active: this.active,
-                    });
-
-                    this.tooltip.update();
-                } else {
-                    // Inactive
-                    this.tooltip._model.opacity = 0;
-                }
-            }
-
-            // Hover animations
-            this.tooltip.pivot();
-
-            if (!this.animating) {
-                var changed;
-
-                helpers.each(this.active, function(element, index) {
-                    if (element !== this.lastActive[index]) {
-                        changed = true;
-                    }
-                }, this);
-
-                // If entering, leaving, or changing elements, animate the change via pivot
-                if ((!this.lastActive.length && this.active.length) ||
-                    (this.lastActive.length && !this.active.length) ||
-                    (this.lastActive.length && this.active.length && changed)) {
-
-                    this.stop();
-                    this.render(this.options.hover.animationDuration);
-                }
-            }
-
-            // Remember Last Active
-            this.lastActive = this.active;
-            return this;
-        },
-    });
+                               // label settings
+                               labels: {
+                                       show: true,
+                                       template: "<%=value.toLocaleString()%>",
+                                       fontSize: 12,
+                                       fontStyle: "normal",
+                                       fontColor: "#666",
+                                       fontFamily: "Helvetica Neue",
+
+                                       //Boolean - Show a backdrop to the scale label
+                                       showLabelBackdrop: true,
+
+                                       //String - The colour of the label backdrop
+                                       backdropColor: "rgba(255,255,255,0.75)",
+
+                                       //Number - The backdrop padding above & below the label in pixels
+                                       backdropPaddingY: 2,
+
+                                       //Number - The backdrop padding to the side of the label in pixels
+                                       backdropPaddingX: 2,
+                               },
+
+                               pointLabels: {
+                                       //String - Point label font declaration
+                                       fontFamily: "'Arial'",
+
+                                       //String - Point label font weight
+                                       fontStyle: "normal",
+
+                                       //Number - Point label font size in pixels
+                                       fontSize: 10,
+
+                                       //String - Point label font colour
+                                       fontColor: "#666",
+                               },
+                       },
+
+                       elements: {
+                               line: {
+                                       tension: 0, // no bezier in radar
+                               }
+                       },
+
+                       //String - A legend template
+                       legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
+
+               },
+
+               initialize: function() {
+
+                       // Events
+                       helpers.bindEvents(this, this.options.events, this.events);
+
+                       // Create a new line and its points for each dataset and piece of data
+                       helpers.each(this.data.datasets, function(dataset, datasetIndex) {
+
+                               dataset.metaDataset = new Chart.Line({
+                                       _chart: this.chart,
+                                       _datasetIndex: datasetIndex,
+                                       _points: dataset.metaData,
+                                       _loop: true
+                               });
+
+                               dataset.metaData = [];
+
+                               helpers.each(dataset.data, function(dataPoint, index) {
+                                       dataset.metaData.push(new Chart.Point({
+                                               _datasetIndex: datasetIndex,
+                                               _index: index,
+                                               _chart: this.chart,
+                                               _model: {
+                                                       x: 0, //xScale.getPixelForValue(null, index, true),
+                                                       y: 0, //this.chartArea.bottom,
+                                               },
+                                       }));
+
+                               }, this);
+                       }, this);
+
+                       // Build the scale.
+                       this.buildScale();
+
+                       // Create tooltip instance exclusively for this chart with some defaults.
+                       this.tooltip = new Chart.Tooltip({
+                               _chart: this.chart,
+                               _data: this.data,
+                               _options: this.options,
+                       }, this);
+
+                       // Need to fit scales before we reset elements. 
+                       Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
+
+                       // Reset so that we animation from the baseline
+                       this.resetElements();
+
+                       // Update that shiz
+                       this.update();
+               },
+               nextPoint: function(collection, index) {
+                       return collection[index + 1] || collection[0];
+               },
+               previousPoint: function(collection, index) {
+                       return collection[index - 1] || collection[collection.length - 1];
+               },
+               resetElements: function() {
+
+                       // Update the points
+                       this.eachElement(function(point, index, dataset, datasetIndex) {
+                               helpers.extend(point, {
+                                       // Utility
+                                       _chart: this.chart,
+                                       _datasetIndex: datasetIndex,
+                                       _index: index,
+                                       _scale: this.scale,
+
+                                       // Desired view properties
+                                       _model: {
+                                               x: this.scale.xCenter,
+                                               y: this.scale.yCenter,
+
+                                               // Appearance
+                                               tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
+                                               radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
+                                               backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
+                                               borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
+                                               borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
+                                               skip: this.data.datasets[datasetIndex].data[index] === null,
+
+                                               // Tooltip
+                                               hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].hitRadius, index, this.options.elements.point.hitRadius),
+                                       },
+                               });
+                       }, this);
+
+                       // Update control points for the bezier curve
+                       this.eachElement(function(point, index, dataset, datasetIndex) {
+                               var controlPoints = helpers.splineCurve(
+                                       this.previousPoint(dataset, index)._model,
+                                       point._model,
+                                       this.nextPoint(dataset, index)._model,
+                                       point._model.tension
+                               );
+
+                               point._model.controlPointPreviousX = this.scale.xCenter;
+                               point._model.controlPointPreviousY = this.scale.yCenter;
+                               point._model.controlPointNextX = this.scale.xCenter;
+                               point._model.controlPointNextY = this.scale.yCenter;
+
+                               // Now pivot the point for animation
+                               point.pivot();
+                       }, this);
+               },
+               update: function(animationDuration) {
+                       Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
+
+                       // Update the lines
+                       this.eachDataset(function(dataset, datasetIndex) {
+                               var scaleBase;
+
+                               if (this.scale.min < 0 && this.scale.max < 0) {
+                                       scaleBase = this.scale.getPointPosition(0, this.scale.max);
+                               } else if (this.scale.min > 0 && this.scale.max > 0) {
+                                       scaleBase = this.scale.getPointPosition(0, this.scale.min);
+                               } else {
+                                       scaleBase = this.scale.getPointPosition(0, 0);
+                               }
+
+                               helpers.extend(dataset.metaDataset, {
+                                       // Utility
+                                       _datasetIndex: datasetIndex,
+
+                                       // Data
+                                       _children: dataset.metaData,
+
+                                       // Model
+                                       _model: {
+                                               // Appearance
+                                               tension: dataset.tension || this.options.elements.line.tension,
+                                               backgroundColor: dataset.backgroundColor || this.options.elements.line.backgroundColor,
+                                               borderWidth: dataset.borderWidth || this.options.elements.line.borderWidth,
+                                               borderColor: dataset.borderColor || this.options.elements.line.borderColor,
+                                               fill: dataset.fill !== undefined ? dataset.fill : this.options.elements.line.fill, // use the value from the dataset if it was provided. else fall back to the default
+                                               skipNull: dataset.skipNull !== undefined ? dataset.skipNull : this.options.elements.line.skipNull,
+                                               drawNull: dataset.drawNull !== undefined ? dataset.drawNull : this.options.elements.line.drawNull,
+
+                                               // Scale
+                                               scaleTop: this.scale.top,
+                                               scaleBottom: this.scale.bottom,
+                                               scaleZero: scaleBase,
+                                       },
+                               });
+
+                               dataset.metaDataset.pivot();
+                       });
+
+                       // Update the points
+                       this.eachElement(function(point, index, dataset, datasetIndex) {
+                               var pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(this.data.datasets[datasetIndex].data[index]));
+
+                               helpers.extend(point, {
+                                       // Utility
+                                       _chart: this.chart,
+                                       _datasetIndex: datasetIndex,
+                                       _index: index,
+
+                                       // Desired view properties
+                                       _model: {
+                                               x: pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
+                                               y: pointPosition.y,
+
+                                               // Appearance
+                                               tension: point.custom && point.custom.tension ? point.custom.tension : this.options.elements.line.tension,
+                                               radius: point.custom && point.custom.radius ? point.custom.pointRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointRadius, index, this.options.elements.point.radius),
+                                               backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBackgroundColor, index, this.options.elements.point.backgroundColor),
+                                               borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderColor, index, this.options.elements.point.borderColor),
+                                               borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].pointBorderWidth, index, this.options.elements.point.borderWidth),
+                                               skip: this.data.datasets[datasetIndex].data[index] === null,
+
+                                               // Tooltip
+                                               hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.data.datasets[datasetIndex].hitRadius, index, this.options.elements.point.hitRadius),
+                                       },
+                               });
+                       }, this);
+
+
+                       // Update control points for the bezier curve
+                       this.eachElement(function(point, index, dataset, datasetIndex) {
+                               var controlPoints = helpers.splineCurve(
+                                       this.previousPoint(dataset, index)._model,
+                                       point._model,
+                                       this.nextPoint(dataset, index)._model,
+                                       point._model.tension
+                               );
+
+                               point._model.controlPointPreviousX = controlPoints.previous.x;
+                               point._model.controlPointNextX = controlPoints.next.x;
+
+                               // Prevent the bezier going outside of the bounds of the graph
+
+                               // Cap puter bezier handles to the upper/lower scale bounds
+                               if (controlPoints.next.y > this.chartArea.bottom) {
+                                       point._model.controlPointNextY = this.chartArea.bottom;
+                               } else if (controlPoints.next.y < this.chartArea.top) {
+                                       point._model.controlPointNextY = this.chartArea.top;
+                               } else {
+                                       point._model.controlPointNextY = controlPoints.next.y;
+                               }
+
+                               // Cap inner bezier handles to the upper/lower scale bounds
+                               if (controlPoints.previous.y > this.chartArea.bottom) {
+                                       point._model.controlPointPreviousY = this.chartArea.bottom;
+                               } else if (controlPoints.previous.y < this.chartArea.top) {
+                                       point._model.controlPointPreviousY = this.chartArea.top;
+                               } else {
+                                       point._model.controlPointPreviousY = controlPoints.previous.y;
+                               }
+
+                               // Now pivot the point for animation
+                               point.pivot();
+                       }, this);
+
+                       this.render(animationDuration);
+               },
+               buildScale: function() {
+                       var self = this;
+
+                       var ScaleConstructor = Chart.scales.getScaleConstructor(this.options.scale.type);
+                       this.scale = new ScaleConstructor({
+                               options: this.options.scale,
+                               height: this.chart.height,
+                               width: this.chart.width,
+                               xCenter: this.chart.width / 2,
+                               yCenter: this.chart.height / 2,
+                               ctx: this.chart.ctx,
+                               labels: this.data.labels,
+                               valuesCount: this.data.datasets[0].data.length,
+                               data: this.data,
+                       });
+
+                       this.scale.setScaleSize();
+                       this.scale.calculateRange();
+                       this.scale.generateTicks();
+                       this.scale.buildYLabels();
+               },
+               draw: function(ease) {
+                       var easingDecimal = ease || 1;
+                       this.clear();
+
+                       // Draw all the scales
+                       this.scale.draw(this.chartArea);
+
+                       // reverse for-loop for proper stacking
+                       for (var i = this.data.datasets.length - 1; i >= 0; i--) {
+
+                               var dataset = this.data.datasets[i];
+
+                               // Transition Point Locations
+                               helpers.each(dataset.metaData, function(point, index) {
+                                       point.transition(easingDecimal);
+                               }, this);
+
+                               // Transition and Draw the line
+                               dataset.metaDataset.transition(easingDecimal).draw();
+
+                               // Draw the points
+                               helpers.each(dataset.metaData, function(point) {
+                                       point.draw();
+                               });
+                       }
+
+                       // Finally draw the tooltip
+                       this.tooltip.transition(easingDecimal).draw();
+               },
+               events: function(e) {
+
+                       this.lastActive = this.lastActive || [];
+
+                       // Find Active Elements
+                       // If exiting chart
+                       if (e.type == 'mouseout') {
+                               this.active = [];
+                       } else {
+                               this.active = function() {
+                                       switch (this.options.hover.mode) {
+                                               case 'single':
+                                                       return this.getElementAtEvent(e);
+                                               case 'label':
+                                                       return this.getElementsAtEvent(e);
+                                               case 'dataset':
+                                                       return this.getDatasetAtEvent(e);
+                                               default:
+                                                       return e;
+                                       }
+                               }.call(this);
+                       }
+
+                       // On Hover hook
+                       if (this.options.hover.onHover) {
+                               this.options.hover.onHover.call(this, this.active);
+                       }
+
+                       if (e.type == 'mouseup' || e.type == 'click') {
+                               if (this.options.onClick) {
+                                       this.options.onClick.call(this, e, this.active);
+                               }
+                       }
+
+                       var dataset;
+                       var index;
+                       // Remove styling for last active (even if it may still be active)
+                       if (this.lastActive.length) {
+                               switch (this.options.hover.mode) {
+                                       case 'single':
+                                               dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
+                                               index = this.lastActive[0]._index;
+
+                                               this.lastActive[0]._model.radius = this.lastActive[0].custom && this.lastActive[0].custom.radius ? this.lastActive[0].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
+                                               this.lastActive[0]._model.backgroundColor = this.lastActive[0].custom && this.lastActive[0].custom.backgroundColor ? this.lastActive[0].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
+                                               this.lastActive[0]._model.borderColor = this.lastActive[0].custom && this.lastActive[0].custom.borderColor ? this.lastActive[0].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
+                                               this.lastActive[0]._model.borderWidth = this.lastActive[0].custom && this.lastActive[0].custom.borderWidth ? this.lastActive[0].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
+                                               break;
+                                       case 'label':
+                                               for (var i = 0; i < this.lastActive.length; i++) {
+                                                       dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
+                                                       index = this.lastActive[i]._index;
+
+                                                       this.lastActive[i]._model.radius = this.lastActive[i].custom && this.lastActive[i].custom.radius ? this.lastActive[i].custom.pointRadius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, this.options.elements.point.radius);
+                                                       this.lastActive[i]._model.backgroundColor = this.lastActive[i].custom && this.lastActive[i].custom.backgroundColor ? this.lastActive[i].custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, this.options.elements.point.backgroundColor);
+                                                       this.lastActive[i]._model.borderColor = this.lastActive[i].custom && this.lastActive[i].custom.borderColor ? this.lastActive[i].custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, this.options.elements.point.borderColor);
+                                                       this.lastActive[i]._model.borderWidth = this.lastActive[i].custom && this.lastActive[i].custom.borderWidth ? this.lastActive[i].custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.options.elements.point.borderWidth);
+                                               }
+                                               break;
+                                       case 'dataset':
+                                               break;
+                                       default:
+                                               // Don't change anything
+                               }
+                       }
+
+                       // Built in hover styling
+                       if (this.active.length && this.options.hover.mode) {
+                               switch (this.options.hover.mode) {
+                                       case 'single':
+                                               dataset = this.data.datasets[this.active[0]._datasetIndex];
+                                               index = this.active[0]._index;
+
+                                               this.active[0]._model.radius = this.active[0].custom && this.active[0].custom.hoverRadius ? this.active[0].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[0]._model.radius + 2);
+                                               this.active[0]._model.backgroundColor = this.active[0].custom && this.active[0].custom.hoverBackgroundColor ? this.active[0].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[0]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
+                                               this.active[0]._model.borderColor = this.active[0].custom && this.active[0].custom.hoverBorderColor ? this.active[0].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[0]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
+                                               this.active[0]._model.borderWidth = this.active[0].custom && this.active[0].custom.hoverBorderWidth ? this.active[0].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[0]._model.borderWidth + 2);
+                                               break;
+                                       case 'label':
+                                               for (var i = 0; i < this.active.length; i++) {
+                                                       dataset = this.data.datasets[this.active[i]._datasetIndex];
+                                                       index = this.active[i]._index;
+
+                                                       this.active[i]._model.radius = this.active[i].custom && this.active[i].custom.hoverRadius ? this.active[i].custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.active[i]._model.radius + 2);
+                                                       this.active[i]._model.backgroundColor = this.active[i].custom && this.active[i].custom.hoverBackgroundColor ? this.active[i].custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(this.active[i]._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
+                                                       this.active[i]._model.borderColor = this.active[i].custom && this.active[i].custom.hoverBorderColor ? this.active[i].custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(this.active[i]._model.borderColor).saturate(0.5).darken(0.1).rgbString());
+                                                       this.active[i]._model.borderWidth = this.active[i].custom && this.active[i].custom.hoverBorderWidth ? this.active[i].custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, this.active[i]._model.borderWidth + 2);
+                                               }
+                                               break;
+                                       case 'dataset':
+                                               break;
+                                       default:
+                                               // Don't change anything
+                               }
+                       }
+
+                       // Built in Tooltips
+                       if (this.options.tooltips.enabled) {
+
+                               // The usual updates
+                               this.tooltip.initialize();
+
+                               // Active
+                               if (this.active.length) {
+                                       this.tooltip._model.opacity = 1;
+
+                                       helpers.extend(this.tooltip, {
+                                               _active: this.active,
+                                       });
+
+                                       this.tooltip.update();
+                               } else {
+                                       // Inactive
+                                       this.tooltip._model.opacity = 0;
+                               }
+                       }
+
+                       // Hover animations
+                       this.tooltip.pivot();
+
+                       if (!this.animating) {
+                               var changed;
+
+                               helpers.each(this.active, function(element, index) {
+                                       if (element !== this.lastActive[index]) {
+                                               changed = true;
+                                       }
+                               }, this);
+
+                               // If entering, leaving, or changing elements, animate the change via pivot
+                               if ((!this.lastActive.length && this.active.length) ||
+                                       (this.lastActive.length && !this.active.length) ||
+                                       (this.lastActive.length && this.active.length && changed)) {
+
+                                       this.stop();
+                                       this.render(this.options.hover.animationDuration);
+                               }
+                       }
+
+                       // Remember Last Active
+                       this.lastActive = this.active;
+                       return this;
+               },
+       });
 }).call(this);
index 2372e270ef38f0004e3060f4f2b5134d1baa6659..7bcda29c942d976dc8b58f67d3f0105c30600814 100644 (file)
 (function() {
-    "use strict";
-
-    var root = this,
-        Chart = root.Chart,
-        helpers = Chart.helpers;
-
-    var LinearRadialScale = Chart.Element.extend({
-        initialize: function() {
-            this.size = helpers.min([this.height, this.width]);
-            this.drawingArea = (this.options.display) ? (this.size / 2) - (this.options.labels.fontSize / 2 + this.options.labels.backdropPaddingY) : (this.size / 2);
-        },
-        calculateCenterOffset: function(value) {
-            // Take into account half font size + the yPadding of the top value
-            var scalingFactor = this.drawingArea / (this.max - this.min);
-            return (value - this.min) * scalingFactor;
-        },
-        update: function() {
-            if (!this.options.lineArc) {
-                this.setScaleSize();
-            } else {
-                this.drawingArea = (this.options.display) ? (this.size / 2) - (this.fontSize / 2 + this.backdropPaddingY) : (this.size / 2);
-            }
-
-            this.buildYLabels();
-        },
-        calculateRange: helpers.noop, // overridden in chart
-        generateTicks: function() {
-            // We need to decide how many ticks we are going to have. Each tick draws a grid line.
-            // There are two possibilities. The first is that the user has manually overridden the scale
-            // calculations in which case the job is easy. The other case is that we have to do it ourselves
-            // 
-            // We assume at this point that the scale object has been updated with the following values
-            // by the chart.
-            //  min: this is the minimum value of the scale
-            //  max: this is the maximum value of the scale
-            //  options: contains the options for the scale. This is referenced from the user settings
-            //      rather than being cloned. This ensures that updates always propogate to a redraw
-
-            // Reset the ticks array. Later on, we will draw a grid line at these positions
-            // The array simply contains the numerical value of the spots where ticks will be
-            this.ticks = [];
-
-            if (this.options.override) {
-                // The user has specified the manual override. We use <= instead of < so that 
-                // we get the final line
-                for (var i = 0; i <= this.options.override.steps; ++i) {
-                    var value = this.options.override.start + (i * this.options.override.stepWidth);
-                    ticks.push(value);
-                }
-            } else {
-                // Figure out what the max number of ticks we can support it is based on the size of
-                // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
-                // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 
-                // the graph
-
-                var maxTicks = Math.min(11, Math.ceil(this.drawingArea / (2 * this.options.labels.fontSize)));
-
-                // Make sure we always have at least 2 ticks 
-                maxTicks = Math.max(2, maxTicks);
-
-                // To get a "nice" value for the tick spacing, we will use the appropriately named 
-                // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
-                // for details.
-
-                // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
-                // do nothing since that would make the chart weird. If the user really wants a weird chart
-                // axis, they can manually override it
-                if (this.options.beginAtZero) {
-                    var minSign = helpers.sign(this.min);
-                    var maxSign = helpers.sign(this.max);
-
-                    if (minSign < 0 && maxSign < 0) {
-                        // move the top up to 0
-                        this.max = 0;
-                    } else if (minSign > 0 && maxSign > 0) {
-                        // move the botttom down to 0
-                        this.min = 0;
-                    }
-                }
-
-                var niceRange = helpers.niceNum(this.max - this.min, false);
-                var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
-                var niceMin = Math.floor(this.min / spacing) * spacing;
-                var niceMax = Math.ceil(this.max / spacing) * spacing;
-
-                // Put the values into the ticks array
-                for (var j = niceMin; j <= niceMax; j += spacing) {
-                    this.ticks.push(j);
-                }
-            }
-
-            if (this.options.position == "left" || this.options.position == "right") {
-                // We are in a vertical orientation. The top value is the highest. So reverse the array
-                this.ticks.reverse();
-            }
-
-            // At this point, we need to update our max and min given the tick values since we have expanded the
-            // range of the scale
-            this.max = helpers.max(this.ticks);
-            this.min = helpers.min(this.ticks);
-        },
-        buildYLabels: function() {
-            this.yLabels = [];
-
-            helpers.each(this.ticks, function(tick, index, ticks) {
-                var label;
-
-                if (this.options.labels.userCallback) {
-                    // If the user provided a callback for label generation, use that as first priority
-                    label = this.options.labels.userCallback(tick, index, ticks);
-                } else if (this.options.labels.template) {
-                    // else fall back to the template string
-                    label = helpers.template(this.options.labels.template, {
-                        value: tick
-                    });
-                }
-
-                this.yLabels.push(label ? label : "");
-            }, this);
-        },
-        getCircumference: function() {
-            return ((Math.PI * 2) / this.valuesCount);
-        },
-        setScaleSize: function() {
-            /*
-             * Right, this is really confusing and there is a lot of maths going on here
-             * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
-             *
-             * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
-             *
-             * Solution:
-             *
-             * We assume the radius of the polygon is half the size of the canvas at first
-             * at each index we check if the text overlaps.
-             *
-             * Where it does, we store that angle and that index.
-             *
-             * After finding the largest index and angle we calculate how much we need to remove
-             * from the shape radius to move the point inwards by that x.
-             *
-             * We average the left and right distances to get the maximum shape radius that can fit in the box
-             * along with labels.
-             *
-             * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
-             * on each side, removing that from the size, halving it and adding the left x protrusion width.
-             *
-             * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
-             * and position it in the most space efficient manner
-             *
-             * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
-             */
-
-
-            // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
-            // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
-            var largestPossibleRadius = helpers.min([(this.height / 2 - this.options.pointLabels.fontSize - 5), this.width / 2]),
-                pointPosition,
-                i,
-                textWidth,
-                halfTextWidth,
-                furthestRight = this.width,
-                furthestRightIndex,
-                furthestRightAngle,
-                furthestLeft = 0,
-                furthestLeftIndex,
-                furthestLeftAngle,
-                xProtrusionLeft,
-                xProtrusionRight,
-                radiusReductionRight,
-                radiusReductionLeft,
-                maxWidthRadius;
-            this.ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
-            for (i = 0; i < this.valuesCount; i++) {
-                // 5px to space the text slightly out - similar to what we do in the draw function.
-                pointPosition = this.getPointPosition(i, largestPossibleRadius);
-                textWidth = this.ctx.measureText(helpers.template(this.options.labels.template, {
-                    value: this.labels[i]
-                })).width + 5;
-                if (i === 0 || i === this.valuesCount / 2) {
-                    // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
-                    // of the radar chart, so text will be aligned centrally, so we'll half it and compare
-                    // w/left and right text sizes
-                    halfTextWidth = textWidth / 2;
-                    if (pointPosition.x + halfTextWidth > furthestRight) {
-                        furthestRight = pointPosition.x + halfTextWidth;
-                        furthestRightIndex = i;
-                    }
-                    if (pointPosition.x - halfTextWidth < furthestLeft) {
-                        furthestLeft = pointPosition.x - halfTextWidth;
-                        furthestLeftIndex = i;
-                    }
-                } else if (i < this.valuesCount / 2) {
-                    // Less than half the values means we'll left align the text
-                    if (pointPosition.x + textWidth > furthestRight) {
-                        furthestRight = pointPosition.x + textWidth;
-                        furthestRightIndex = i;
-                    }
-                } else if (i > this.valuesCount / 2) {
-                    // More than half the values means we'll right align the text
-                    if (pointPosition.x - textWidth < furthestLeft) {
-                        furthestLeft = pointPosition.x - textWidth;
-                        furthestLeftIndex = i;
-                    }
-                }
-            }
-
-            xProtrusionLeft = furthestLeft;
-
-            xProtrusionRight = Math.ceil(furthestRight - this.width);
-
-            furthestRightAngle = this.getIndexAngle(furthestRightIndex);
-
-            furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
-
-            radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
-
-            radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
-
-            // Ensure we actually need to reduce the size of the chart
-            radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
-            radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
-
-            this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2;
-
-            //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
-            this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
-
-        },
-        setCenterPoint: function(leftMovement, rightMovement) {
-
-            var maxRight = this.width - rightMovement - this.drawingArea,
-                maxLeft = leftMovement + this.drawingArea;
-
-            this.xCenter = (maxLeft + maxRight) / 2;
-            // Always vertically in the centre as the text height doesn't change
-            this.yCenter = (this.height / 2);
-        },
-
-        getIndexAngle: function(index) {
-            var angleMultiplier = (Math.PI * 2) / this.valuesCount;
-            // Start from the top instead of right, so remove a quarter of the circle
-
-            return index * angleMultiplier - (Math.PI / 2);
-        },
-        getPointPosition: function(index, distanceFromCenter) {
-            var thisAngle = this.getIndexAngle(index);
-            return {
-                x: (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
-                y: (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
-            };
-        },
-        draw: function() {
-            if (this.options.display) {
-                var ctx = this.ctx;
-                helpers.each(this.yLabels, function(label, index) {
-                    // Don't draw a centre value
-                    if (index > 0) {
-                        var yCenterOffset = index * (this.drawingArea / Math.max(this.ticks.length, 1)),
-                            yHeight = this.yCenter - yCenterOffset,
-                            pointPosition;
-
-                        // Draw circular lines around the scale
-                        if (this.options.gridLines.show) {
-                            ctx.strokeStyle = this.options.gridLines.color;
-                            ctx.lineWidth = this.options.gridLines.lineWidth;
-
-                            if (this.options.lineArc) {
-                                ctx.beginPath();
-                                ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2);
-                                ctx.closePath();
-                                ctx.stroke();
-                            } else {
-                                ctx.beginPath();
-                                for (var i = 0; i < this.valuesCount; i++) {
-                                    pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.ticks[index]));
-                                    if (i === 0) {
-                                        ctx.moveTo(pointPosition.x, pointPosition.y);
-                                    } else {
-                                        ctx.lineTo(pointPosition.x, pointPosition.y);
-                                    }
-                                }
-                                ctx.closePath();
-                                ctx.stroke();
-                            }
-                        }
-
-                        if (this.options.labels.show) {
-                            ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
-
-                            if (this.showLabelBackdrop) {
-                                var labelWidth = ctx.measureText(label).width;
-                                ctx.fillStyle = this.options.labels.backdropColor;
-                                ctx.fillRect(
-                                    this.xCenter - labelWidth / 2 - this.options.labels.backdropPaddingX,
-                                    yHeight - this.fontSize / 2 - this.options.labels.backdropPaddingY,
-                                    labelWidth + this.options.labels.backdropPaddingX * 2,
-                                    this.options.labels.fontSize + this.options.lables.backdropPaddingY * 2
-                                );
-                            }
-
-                            ctx.textAlign = 'center';
-                            ctx.textBaseline = "middle";
-                            ctx.fillStyle = this.options.labels.fontColor;
-                            ctx.fillText(label, this.xCenter, yHeight);
-                        }
-                    }
-                }, this);
-
-                if (!this.options.lineArc) {
-                    ctx.lineWidth = this.options.angleLines.lineWidth;
-                    ctx.strokeStyle = this.options.angleLines.color;
-
-                    for (var i = this.valuesCount - 1; i >= 0; i--) {
-                        if (this.options.angleLines.show) {
-                            var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));
-                            ctx.beginPath();
-                            ctx.moveTo(this.xCenter, this.yCenter);
-                            ctx.lineTo(outerPosition.x, outerPosition.y);
-                            ctx.stroke();
-                            ctx.closePath();
-                        }
-                        // Extra 3px out for some label spacing
-                        var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
-                        ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
-                        ctx.fillStyle = this.options.pointLabels.fontColor;
-
-                        var labelsCount = this.labels.length,
-                            halfLabelsCount = this.labels.length / 2,
-                            quarterLabelsCount = halfLabelsCount / 2,
-                            upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
-                            exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
-                        if (i === 0) {
-                            ctx.textAlign = 'center';
-                        } else if (i === halfLabelsCount) {
-                            ctx.textAlign = 'center';
-                        } else if (i < halfLabelsCount) {
-                            ctx.textAlign = 'left';
-                        } else {
-                            ctx.textAlign = 'right';
-                        }
-
-                        // Set the correct text baseline based on outer positioning
-                        if (exactQuarter) {
-                            ctx.textBaseline = 'middle';
-                        } else if (upperHalf) {
-                            ctx.textBaseline = 'bottom';
-                        } else {
-                            ctx.textBaseline = 'top';
-                        }
-
-                        ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
-                    }
-                }
-            }
-        }
-    });
-    Chart.scales.registerScaleType("radialLinear", LinearRadialScale);
+       "use strict";
+
+       var root = this,
+               Chart = root.Chart,
+               helpers = Chart.helpers;
+
+       var LinearRadialScale = Chart.Element.extend({
+               initialize: function() {
+                       this.size = helpers.min([this.height, this.width]);
+                       this.drawingArea = (this.options.display) ? (this.size / 2) - (this.options.labels.fontSize / 2 + this.options.labels.backdropPaddingY) : (this.size / 2);
+               },
+               calculateCenterOffset: function(value) {
+                       // Take into account half font size + the yPadding of the top value
+                       var scalingFactor = this.drawingArea / (this.max - this.min);
+                       return (value - this.min) * scalingFactor;
+               },
+               update: function() {
+                       if (!this.options.lineArc) {
+                               this.setScaleSize();
+                       } else {
+                               this.drawingArea = (this.options.display) ? (this.size / 2) - (this.fontSize / 2 + this.backdropPaddingY) : (this.size / 2);
+                       }
+
+                       this.buildYLabels();
+               },
+               calculateRange: function() {
+                       this.min = null;
+                       this.max = null;
+
+                       helpers.each(this.data.datasets, function(dataset) {
+                               helpers.each(dataset.data, function(value, index) {
+                                       if (this.min === null) {
+                                               this.min = value;
+                                       } else if (value < this.min) {
+                                               this.min = value;
+                                       }
+
+                                       if (this.max === null) {
+                                               this.max = value;
+                                       } else if (value > this.max) {
+                                               this.max = value;
+                                       }
+                               }, this);
+                       }, this);
+               },
+               generateTicks: function() {
+                       // We need to decide how many ticks we are going to have. Each tick draws a grid line.
+                       // There are two possibilities. The first is that the user has manually overridden the scale
+                       // calculations in which case the job is easy. The other case is that we have to do it ourselves
+                       // 
+                       // We assume at this point that the scale object has been updated with the following values
+                       // by the chart.
+                       //  min: this is the minimum value of the scale
+                       //  max: this is the maximum value of the scale
+                       //  options: contains the options for the scale. This is referenced from the user settings
+                       //      rather than being cloned. This ensures that updates always propogate to a redraw
+
+                       // Reset the ticks array. Later on, we will draw a grid line at these positions
+                       // The array simply contains the numerical value of the spots where ticks will be
+                       this.ticks = [];
+
+                       if (this.options.override) {
+                               // The user has specified the manual override. We use <= instead of < so that 
+                               // we get the final line
+                               for (var i = 0; i <= this.options.override.steps; ++i) {
+                                       var value = this.options.override.start + (i * this.options.override.stepWidth);
+                                       ticks.push(value);
+                               }
+                       } else {
+                               // Figure out what the max number of ticks we can support it is based on the size of
+                               // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
+                               // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 
+                               // the graph
+
+                               var maxTicks = Math.min(11, Math.ceil(this.drawingArea / (2 * this.options.labels.fontSize)));
+
+                               // Make sure we always have at least 2 ticks 
+                               maxTicks = Math.max(2, maxTicks);
+
+                               // To get a "nice" value for the tick spacing, we will use the appropriately named 
+                               // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
+                               // for details.
+
+                               // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
+                               // do nothing since that would make the chart weird. If the user really wants a weird chart
+                               // axis, they can manually override it
+                               if (this.options.beginAtZero) {
+                                       var minSign = helpers.sign(this.min);
+                                       var maxSign = helpers.sign(this.max);
+
+                                       if (minSign < 0 && maxSign < 0) {
+                                               // move the top up to 0
+                                               this.max = 0;
+                                       } else if (minSign > 0 && maxSign > 0) {
+                                               // move the botttom down to 0
+                                               this.min = 0;
+                                       }
+                               }
+
+                               var niceRange = helpers.niceNum(this.max - this.min, false);
+                               var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
+                               var niceMin = Math.floor(this.min / spacing) * spacing;
+                               var niceMax = Math.ceil(this.max / spacing) * spacing;
+
+                               // Put the values into the ticks array
+                               for (var j = niceMin; j <= niceMax; j += spacing) {
+                                       this.ticks.push(j);
+                               }
+                       }
+
+                       if (this.options.position == "left" || this.options.position == "right") {
+                               // We are in a vertical orientation. The top value is the highest. So reverse the array
+                               this.ticks.reverse();
+                       }
+
+                       // At this point, we need to update our max and min given the tick values since we have expanded the
+                       // range of the scale
+                       this.max = helpers.max(this.ticks);
+                       this.min = helpers.min(this.ticks);
+               },
+               buildYLabels: function() {
+                       this.yLabels = [];
+
+                       helpers.each(this.ticks, function(tick, index, ticks) {
+                               var label;
+
+                               if (this.options.labels.userCallback) {
+                                       // If the user provided a callback for label generation, use that as first priority
+                                       label = this.options.labels.userCallback(tick, index, ticks);
+                               } else if (this.options.labels.template) {
+                                       // else fall back to the template string
+                                       label = helpers.template(this.options.labels.template, {
+                                               value: tick
+                                       });
+                               }
+
+                               this.yLabels.push(label ? label : "");
+                       }, this);
+               },
+               getCircumference: function() {
+                       return ((Math.PI * 2) / this.valuesCount);
+               },
+               setScaleSize: function() {
+                       /*
+                        * Right, this is really confusing and there is a lot of maths going on here
+                        * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
+                        *
+                        * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
+                        *
+                        * Solution:
+                        *
+                        * We assume the radius of the polygon is half the size of the canvas at first
+                        * at each index we check if the text overlaps.
+                        *
+                        * Where it does, we store that angle and that index.
+                        *
+                        * After finding the largest index and angle we calculate how much we need to remove
+                        * from the shape radius to move the point inwards by that x.
+                        *
+                        * We average the left and right distances to get the maximum shape radius that can fit in the box
+                        * along with labels.
+                        *
+                        * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
+                        * on each side, removing that from the size, halving it and adding the left x protrusion width.
+                        *
+                        * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
+                        * and position it in the most space efficient manner
+                        *
+                        * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
+                        */
+
+
+                       // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
+                       // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
+                       var largestPossibleRadius = helpers.min([(this.height / 2 - this.options.pointLabels.fontSize - 5), this.width / 2]),
+                               pointPosition,
+                               i,
+                               textWidth,
+                               halfTextWidth,
+                               furthestRight = this.width,
+                               furthestRightIndex,
+                               furthestRightAngle,
+                               furthestLeft = 0,
+                               furthestLeftIndex,
+                               furthestLeftAngle,
+                               xProtrusionLeft,
+                               xProtrusionRight,
+                               radiusReductionRight,
+                               radiusReductionLeft,
+                               maxWidthRadius;
+                       this.ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
+                       for (i = 0; i < this.valuesCount; i++) {
+                               // 5px to space the text slightly out - similar to what we do in the draw function.
+                               pointPosition = this.getPointPosition(i, largestPossibleRadius);
+                               textWidth = this.ctx.measureText(helpers.template(this.options.labels.template, {
+                                       value: this.labels[i]
+                               })).width + 5;
+                               if (i === 0 || i === this.valuesCount / 2) {
+                                       // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
+                                       // of the radar chart, so text will be aligned centrally, so we'll half it and compare
+                                       // w/left and right text sizes
+                                       halfTextWidth = textWidth / 2;
+                                       if (pointPosition.x + halfTextWidth > furthestRight) {
+                                               furthestRight = pointPosition.x + halfTextWidth;
+                                               furthestRightIndex = i;
+                                       }
+                                       if (pointPosition.x - halfTextWidth < furthestLeft) {
+                                               furthestLeft = pointPosition.x - halfTextWidth;
+                                               furthestLeftIndex = i;
+                                       }
+                               } else if (i < this.valuesCount / 2) {
+                                       // Less than half the values means we'll left align the text
+                                       if (pointPosition.x + textWidth > furthestRight) {
+                                               furthestRight = pointPosition.x + textWidth;
+                                               furthestRightIndex = i;
+                                       }
+                               } else if (i > this.valuesCount / 2) {
+                                       // More than half the values means we'll right align the text
+                                       if (pointPosition.x - textWidth < furthestLeft) {
+                                               furthestLeft = pointPosition.x - textWidth;
+                                               furthestLeftIndex = i;
+                                       }
+                               }
+                       }
+
+                       xProtrusionLeft = furthestLeft;
+
+                       xProtrusionRight = Math.ceil(furthestRight - this.width);
+
+                       furthestRightAngle = this.getIndexAngle(furthestRightIndex);
+
+                       furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
+
+                       radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
+
+                       radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
+
+                       // Ensure we actually need to reduce the size of the chart
+                       radiusReductionRight = (helpers.isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
+                       radiusReductionLeft = (helpers.isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
+
+                       this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2;
+
+                       //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
+                       this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
+
+               },
+               setCenterPoint: function(leftMovement, rightMovement) {
+
+                       var maxRight = this.width - rightMovement - this.drawingArea,
+                               maxLeft = leftMovement + this.drawingArea;
+
+                       this.xCenter = (maxLeft + maxRight) / 2;
+                       // Always vertically in the centre as the text height doesn't change
+                       this.yCenter = (this.height / 2);
+               },
+
+               getIndexAngle: function(index) {
+                       var angleMultiplier = (Math.PI * 2) / this.valuesCount;
+                       // Start from the top instead of right, so remove a quarter of the circle
+
+                       return index * angleMultiplier - (Math.PI / 2);
+               },
+               getPointPosition: function(index, distanceFromCenter) {
+                       var thisAngle = this.getIndexAngle(index);
+                       return {
+                               x: (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
+                               y: (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
+                       };
+               },
+               draw: function() {
+                       if (this.options.display) {
+                               var ctx = this.ctx;
+                               helpers.each(this.yLabels, function(label, index) {
+                                       // Don't draw a centre value
+                                       if (index > 0) {
+                                               var yCenterOffset = index * (this.drawingArea / Math.max(this.ticks.length, 1)),
+                                                       yHeight = this.yCenter - yCenterOffset,
+                                                       pointPosition;
+
+                                               // Draw circular lines around the scale
+                                               if (this.options.gridLines.show) {
+                                                       ctx.strokeStyle = this.options.gridLines.color;
+                                                       ctx.lineWidth = this.options.gridLines.lineWidth;
+
+                                                       if (this.options.lineArc) {
+                                                               ctx.beginPath();
+                                                               ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2);
+                                                               ctx.closePath();
+                                                               ctx.stroke();
+                                                       } else {
+                                                               ctx.beginPath();
+                                                               for (var i = 0; i < this.valuesCount; i++) {
+                                                                       pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.ticks[index]));
+                                                                       if (i === 0) {
+                                                                               ctx.moveTo(pointPosition.x, pointPosition.y);
+                                                                       } else {
+                                                                               ctx.lineTo(pointPosition.x, pointPosition.y);
+                                                                       }
+                                                               }
+                                                               ctx.closePath();
+                                                               ctx.stroke();
+                                                       }
+                                               }
+
+                                               if (this.options.labels.show) {
+                                                       ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+
+                                                       if (this.showLabelBackdrop) {
+                                                               var labelWidth = ctx.measureText(label).width;
+                                                               ctx.fillStyle = this.options.labels.backdropColor;
+                                                               ctx.fillRect(
+                                                                       this.xCenter - labelWidth / 2 - this.options.labels.backdropPaddingX,
+                                                                       yHeight - this.fontSize / 2 - this.options.labels.backdropPaddingY,
+                                                                       labelWidth + this.options.labels.backdropPaddingX * 2,
+                                                                       this.options.labels.fontSize + this.options.lables.backdropPaddingY * 2
+                                                               );
+                                                       }
+
+                                                       ctx.textAlign = 'center';
+                                                       ctx.textBaseline = "middle";
+                                                       ctx.fillStyle = this.options.labels.fontColor;
+                                                       ctx.fillText(label, this.xCenter, yHeight);
+                                               }
+                                       }
+                               }, this);
+
+                               if (!this.options.lineArc) {
+                                       ctx.lineWidth = this.options.angleLines.lineWidth;
+                                       ctx.strokeStyle = this.options.angleLines.color;
+
+                                       for (var i = this.valuesCount - 1; i >= 0; i--) {
+                                               if (this.options.angleLines.show) {
+                                                       var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));
+                                                       ctx.beginPath();
+                                                       ctx.moveTo(this.xCenter, this.yCenter);
+                                                       ctx.lineTo(outerPosition.x, outerPosition.y);
+                                                       ctx.stroke();
+                                                       ctx.closePath();
+                                               }
+                                               // Extra 3px out for some label spacing
+                                               var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
+                                               ctx.font = helpers.fontString(this.options.pointLabels.fontSize, this.options.pointLabels.fontStyle, this.options.pointLabels.fontFamily);
+                                               ctx.fillStyle = this.options.pointLabels.fontColor;
+
+                                               var labelsCount = this.labels.length,
+                                                       halfLabelsCount = this.labels.length / 2,
+                                                       quarterLabelsCount = halfLabelsCount / 2,
+                                                       upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
+                                                       exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
+                                               if (i === 0) {
+                                                       ctx.textAlign = 'center';
+                                               } else if (i === halfLabelsCount) {
+                                                       ctx.textAlign = 'center';
+                                               } else if (i < halfLabelsCount) {
+                                                       ctx.textAlign = 'left';
+                                               } else {
+                                                       ctx.textAlign = 'right';
+                                               }
+
+                                               // Set the correct text baseline based on outer positioning
+                                               if (exactQuarter) {
+                                                       ctx.textBaseline = 'middle';
+                                               } else if (upperHalf) {
+                                                       ctx.textBaseline = 'bottom';
+                                               } else {
+                                                       ctx.textBaseline = 'top';
+                                               }
+
+                                               ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
+                                       }
+                               }
+                       }
+               }
+       });
+       Chart.scales.registerScaleType("radialLinear", LinearRadialScale);
 
 
 }).call(this);