(function() {
"use strict";
- return;
-
var root = this,
Chart = root.Chart,
//Cache a local reference to Chart.helpers
helpers = Chart.helpers;
- var defaultConfig = {
-
+ Chart.defaults.doughnut = {
animation: {
//Boolean - Whether we animate the rotation of the Doughnut
animateRotate: true,
-
//Boolean - Whether we animate scaling the Doughnut from the centre
animateScale: false,
},
-
hover: {
mode: 'single'
},
-
//The percentage of the chart that we cut out of the middle.
-
cutoutPercentage: 50,
};
+
+ Chart.controllers.doughnut = function(chart, datasetIndex) {
+ this.initialize.call(this, chart, datasetIndex);
+ };
+
+ helpers.extend(Chart.controllers.doughnut.prototype, {
+
+ initialize: function(chart, datasetIndex) {
+ this.chart = chart;
+ this.index = datasetIndex;
+ this.linkScales();
+ this.addElements();
+ },
+
+ linkScales: function() {
+ // no scales for doughnut
+ },
+
+ getDataset: function() {
+ return this.chart.data.datasets[this.index];
+ },
+
+ getScaleForId: function(scaleID) {
+ return this.chart.scales[scaleID];
+ },
+
+ addElements: function() {
+ this.getDataset().metaData = this.getDataset().metaData || [];
+ helpers.each(this.getDataset().data, function(value, index) {
+ this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
+ _chart: this.chart.chart,
+ _datasetIndex: this.index,
+ _index: index,
+ });
+ }, this);
+ },
+
+ reset: function() {
+ this.update(true);
+ },
+
+ update: function(reset) {
+
+ this.chart.outerRadius = (helpers.min([this.chart.chart.width, this.chart.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2;
+ this.chart.innerRadius = this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1;
+ this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.chart.data.datasets.length;
+
+
+ this.getDataset().total = 0;
+ helpers.each(this.getDataset().data, function(value) {
+ this.getDataset().total += Math.abs(value);
+ }, this);
+
+ this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index);
+ this.innerRadius = this.chart.outerRadius - this.chart.radiusLength;
+
+ helpers.each(this.getDataset().metaData, function(arc, index) {
+
+ var resetModel = {
+ x: this.chart.chart.width / 2,
+ y: this.chart.chart.height / 2,
+ startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
+ circumference: (this.chart.options.animation.animateRotate) ? 0 : this.calculateCircumference(this.getDataset().data[index]),
+ outerRadius: (this.chart.options.animation.animateScale) ? 0 : this.outerRadius,
+ innerRadius: (this.chart.options.animation.animateScale) ? 0 : this.innerRadius,
+ };
+
+ helpers.extend(arc, {
+ // Utility
+ _chart: this.chart.chart,
+ _datasetIndex: this.index,
+ _index: index,
+
+ // Desired view properties
+ _model: reset ? resetModel : {
+ x: this.chart.chart.width / 2,
+ y: this.chart.chart.height / 2,
+ circumference: this.calculateCircumference(this.getDataset().data[index]),
+ outerRadius: this.outerRadius,
+ innerRadius: this.innerRadius,
+
+ backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
+ hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
+ borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
+ borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
+
+ label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index])
+ },
+ });
+
+ if (!reset) {
+
+ if (index === 0) {
+ arc._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
+ } else {
+ arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle;
+ }
+
+ arc._model.endAngle = arc._model.startAngle + arc._model.circumference;
+
+
+ //Check to see if it's the last arc, if not get the next and update its start angle
+ if (index < this.getDataset().data.length - 1) {
+ this.getDataset().metaData[index + 1]._model.startAngle = arc._model.endAngle;
+ }
+ }
+
+ arc.pivot();
+ }, this);
+ },
+
+ draw: function(ease) {
+ var easingDecimal = ease || 1;
+ helpers.each(this.getDataset().metaData, function(arc, index) {
+ arc.transition(easingDecimal).draw();
+ }, this);
+ },
+
+
+
+ setHoverStyle: function(rectangle) {
+ var dataset = this.chart.data.datasets[rectangle._datasetIndex];
+ var index = rectangle._index;
+
+ rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
+ rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString());
+ rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangle._model.borderWidth);
+ },
+
+ removeHoverStyle: function(rectangle) {
+ // TODO
+ },
+
+ calculateCircumference: function(value) {
+ if (this.getDataset().total > 0) {
+ return (Math.PI * 2) * (value / this.getDataset().total);
+ } else {
+ return 0;
+ }
+ },
+
+ });
+
+
+
+
+
+
+
+
+
+ return;
+
Chart.Type.extend({
//Passing in a name registers this chart in the Chart namespace
name: "Doughnut",
initialize: function() {
//Set up tooltip events on the chart
- helpers.bindEvents(this, this.options.events, this.events);
+ helpers.bindEvents(this, this.chart.options.events, this.events);
//Create a new bar for each piece of data
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
},
- calculateCircumference: function(dataset, value) {
- if (dataset.total > 0) {
- return (Math.PI * 2) * (value / dataset.total);
- } else {
- return 0;
- }
- },
resetElements: function() {
- this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2;
- this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
+ this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.chart.options.elements.arc.borderWidth / 2) / 2;
+ this.innerRadius = this.chart.options.cutoutPercentage ? (this.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1;
this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
// Update the points
dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
dataset.innerRadius = dataset.outerRadius - this.radiusLength;
- helpers.each(dataset.metaData, function(slice, index) {
- helpers.extend(slice, {
+ helpers.each(dataset.metaData, function(arc, index) {
+ helpers.extend(arc, {
_model: {
x: this.chart.width / 2,
y: this.chart.height / 2,
startAngle: Math.PI * -0.5, // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
- circumference: (this.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
- outerRadius: (this.options.animation.animateScale) ? 0 : dataset.outerRadius,
- innerRadius: (this.options.animation.animateScale) ? 0 : dataset.innerRadius,
+ circumference: (this.chart.options.animation.animateRotate) ? 0 : this.calculateCircumference(metaSlice.value),
+ outerRadius: (this.chart.options.animation.animateScale) ? 0 : dataset.outerRadius,
+ innerRadius: (this.chart.options.animation.animateScale) ? 0 : dataset.innerRadius,
- backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor),
- hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
- borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth),
- borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor),
+ backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
+ hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
+ borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.chart.options.elements.arc.borderWidth),
+ borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.chart.options.elements.arc.borderColor),
- label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
+ label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.chart.data.labels[index])
},
});
- slice.pivot();
+ arc.pivot();
}, this);
}, this);
},
update: function(animationDuration) {
- this.outerRadius = (helpers.min([this.chart.width, this.chart.height]) - this.options.elements.arc.borderWidth / 2) / 2;
- this.innerRadius = this.options.cutoutPercentage ? (this.outerRadius / 100) * (this.options.cutoutPercentage) : 1;
- this.radiusLength = (this.outerRadius - this.innerRadius) / this.data.datasets.length;
-
-
- // Update the points
- helpers.each(this.data.datasets, function(dataset, datasetIndex) {
-
- dataset.total = 0;
- helpers.each(dataset.data, function(value) {
- dataset.total += Math.abs(value);
- }, this);
-
-
- dataset.outerRadius = this.outerRadius - (this.radiusLength * datasetIndex);
-
- dataset.innerRadius = dataset.outerRadius - this.radiusLength;
-
- helpers.each(dataset.metaData, function(slice, index) {
-
- helpers.extend(slice, {
- // Utility
- _chart: this.chart,
- _datasetIndex: datasetIndex,
- _index: index,
-
- // Desired view properties
- _model: {
- x: this.chart.width / 2,
- y: this.chart.height / 2,
- circumference: this.calculateCircumference(dataset, dataset.data[index]),
- outerRadius: dataset.outerRadius,
- innerRadius: dataset.innerRadius,
-
- backgroundColor: slice.custom && slice.custom.backgroundColor ? slice.custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, this.options.elements.arc.backgroundColor),
- hoverBackgroundColor: slice.custom && slice.custom.hoverBackgroundColor ? slice.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, this.options.elements.arc.hoverBackgroundColor),
- borderWidth: slice.custom && slice.custom.borderWidth ? slice.custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, this.options.elements.arc.borderWidth),
- borderColor: slice.custom && slice.custom.borderColor ? slice.custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, this.options.elements.arc.borderColor),
-
- label: helpers.getValueAtIndexOrDefault(dataset.label, index, this.data.labels[index])
- },
- });
-
- if (index === 0) {
- slice._model.startAngle = Math.PI * -0.5; // use - PI / 2 instead of 3PI / 2 to make animations better. It means that we never deal with overflow during the transition function
- } else {
- slice._model.startAngle = dataset.metaData[index - 1]._model.endAngle;
- }
-
- slice._model.endAngle = slice._model.startAngle + slice._model.circumference;
-
-
- //Check to see if it's the last slice, if not get the next and update its start angle
- if (index < dataset.data.length - 1) {
- dataset.metaData[index + 1]._model.startAngle = slice._model.endAngle;
- }
-
- slice.pivot();
- }, this);
-
- }, this);
- this.render(animationDuration);
},
draw: function(easeDecimal) {
easeDecimal = easeDecimal || 1;
this.clear();
- this.eachElement(function(slice) {
- slice.transition(easeDecimal).draw();
+ this.eachElement(function(arc) {
+ arc.transition(easeDecimal).draw();
}, this);
this.tooltip.transition(easeDecimal).draw();
} else {
this.active = function() {
- switch (this.options.hover.mode) {
+ switch (this.chart.options.hover.mode) {
case 'single':
return this.getSliceAtEvent(e);
case 'label':
}
// On Hover hook
- if (this.options.hover.onHover) {
- this.options.hover.onHover.call(this, this.active);
+ if (this.chart.options.hover.onHover) {
+ this.chart.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);
+ if (this.chart.options.onClick) {
+ this.chart.options.onClick.call(this, e, this.active);
}
}
var index;
// Remove styling for last active (even if it may still be active)
if (this.lastActive.length) {
- switch (this.options.hover.mode) {
+ switch (this.chart.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);
+ 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.chart.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.chart.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.chart.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);
+ 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.chart.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.chart.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.chart.options.elements.arc.borderWidth);
}
break;
case 'dataset':
}
// Built in hover styling
- if (this.active.length && this.options.hover.mode) {
- switch (this.options.hover.mode) {
+ if (this.active.length && this.chart.options.hover.mode) {
+ switch (this.chart.options.hover.mode) {
case 'single':
dataset = this.data.datasets[this.active[0]._datasetIndex];
index = this.active[0]._index;
// Built in Tooltips
- if (this.options.tooltips.enabled) {
+ if (this.chart.options.tooltips.enabled) {
// The usual updates
this.tooltip.initialize();
(this.lastActive.length && this.active.length && changed)) {
this.stop();
- this.render(this.options.hover.animationDuration);
+ this.render(this.chart.options.hover.animationDuration);
}
}
var location = helpers.getRelativePosition(e);
- this.eachElement(function(slice, index) {
- if (slice.inRange(location.x, location.y)) {
- elements.push(slice);
+ this.eachElement(function(arc, index) {
+ if (arc.inRange(location.x, location.y)) {
+ elements.push(arc);
}
}, this);
return elements;
var location = helpers.getRelativePosition(e);
- this.eachElement(function(slice, index) {
- if (slice.inLabelRange(location.x, location.y)) {
- elements.push(slice);
+ this.eachElement(function(arc, index) {
+ if (arc.inLabelRange(location.x, location.y)) {
+ elements.push(arc);
}
}, this);
return elements;