(function(){ "use strict"; var root = this, Chart = root.Chart, //Cache a local reference to Chart.helpers helpers = Chart.helpers; var defaultConfig = { //Boolean - Whether we should show a stroke on each segment segmentShowStroke : true, //String - The colour of each segment stroke segmentStrokeColor : "#fff", //Number - The width of each segment stroke segmentStrokeWidth : 2, //The percentage of the chart that we cut out of the middle. percentageInnerCutout : 50, //Number - Amount of animation steps animationSteps : 100, //String - Animation easing effect animationEasing : "easeOutBounce", //Boolean - Whether we animate the rotation of the Doughnut animateRotate : true, //Boolean - Whether we animate scaling the Doughnut from the centre animateScale : false, //String - A legend template legendTemplate : "" }; Chart.Type.extend({ //Passing in a name registers this chart in the Chart namespace name: "Doughnut", //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(data){ //Declare segments as a static property to prevent inheriting across the Chart type prototype this.segments = []; this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; this.SegmentArc = Chart.Arc.extend({ ctx : this.chart.ctx, x : this.chart.width/2, y : this.chart.height/2 }); //Set up tooltip events on the chart if (this.options.showTooltips){ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){ var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : []; helpers.each(this.segments,function(segment){ segment.restore(["fillColor"]); }); helpers.each(activeSegments,function(activeSegment){ activeSegment.fillColor = activeSegment.highlightColor; }); this.showTooltip(activeSegments); }); } this.calculateTotal(data); helpers.each(data,function(datapoint, index){ this.addData(datapoint, index, true); },this); this.render(); }, getSegmentsAtEvent : function(e){ var segmentsArray = []; var location = helpers.getRelativePosition(e); helpers.each(this.segments,function(segment){ if (segment.inRange(location.x,location.y)) segmentsArray.push(segment); },this); return segmentsArray; }, addData : function(segment, atIndex, silent){ var index = atIndex || this.segments.length; this.segments.splice(index, 0, new this.SegmentArc({ value : segment.value, outerRadius : (this.options.animateScale) ? 0 : this.outerRadius, innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout, fillColor : segment.color, highlightColor : segment.highlight || segment.color, showStroke : this.options.segmentShowStroke, strokeWidth : this.options.segmentStrokeWidth, strokeColor : this.options.segmentStrokeColor, startAngle : Math.PI * 1.5, circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value), label : segment.label })); if (!silent){ this.reflow(); this.update(); } }, calculateCircumference : function(value){ return (Math.PI*2)*(Math.abs(value) / this.total); }, calculateTotal : function(data){ this.total = 0; helpers.each(data,function(segment){ this.total += Math.abs(segment.value); },this); }, update : function(){ this.calculateTotal(this.segments); // Reset any highlight colours before updating. helpers.each(this.activeElements, function(activeElement){ activeElement.restore(['fillColor']); }); helpers.each(this.segments,function(segment){ segment.save(); }); this.render(); }, removeData: function(atIndex){ var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1; this.segments.splice(indexToDelete, 1); this.reflow(); this.update(); }, reflow : function(){ helpers.extend(this.SegmentArc.prototype,{ x : this.chart.width/2, y : this.chart.height/2 }); this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2; helpers.each(this.segments, function(segment){ segment.update({ outerRadius : this.outerRadius, innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout }); }, this); }, draw : function(easeDecimal){ var animDecimal = (easeDecimal) ? easeDecimal : 1; this.clear(); helpers.each(this.segments,function(segment,index){ segment.transition({ circumference : this.calculateCircumference(segment.value), outerRadius : this.outerRadius, innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout },animDecimal); segment.endAngle = segment.startAngle + segment.circumference; segment.draw(); if (index === 0){ segment.startAngle = Math.PI * 1.5; } //Check to see if it's the last segment, if not get the next and update the start angle if (index < this.segments.length-1){ this.segments[index+1].startAngle = segment.endAngle; } },this); } }); Chart.types.Doughnut.extend({ name : "Pie", defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0}) }); }).call(this);