2015-05-16 04:54:01 +02:00
( function ( ) {
"use strict" ;
var root = this ,
Chart = root . Chart ,
//Cache a local reference to Chart.helpers
helpers = Chart . helpers ;
var defaultConfig = {
2015-05-27 01:30:34 +02:00
animation : {
//Boolean - Whether we animate the rotation of the Doughnut
animateRotate : true ,
2015-05-16 04:54:01 +02:00
2015-05-27 01:30:34 +02:00
//Boolean - Whether we animate scaling the Doughnut from the centre
animateScale : false ,
} ,
2015-05-16 04:54:01 +02:00
2015-06-05 00:39:05 +02:00
hover : {
mode : 'single'
} ,
2015-05-27 01:30:34 +02:00
//The percentage of the chart that we cut out of the middle.
2015-06-03 22:14:23 +02:00
2015-06-03 22:17:10 +02:00
cutoutPercentage : 50 ,
2015-05-16 04:54:01 +02:00
} ;
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
2015-05-20 15:03:22 +02:00
initialize : function ( ) {
2015-05-16 04:54:01 +02:00
//Set up tooltip events on the chart
2015-06-03 22:14:23 +02:00
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 ) ;
2015-05-16 04:54:01 +02:00
} , 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 ) ;
2015-06-04 01:16:42 +02:00
this . resetElements ( ) ;
2015-06-03 22:14:23 +02:00
// Update the chart with the latest data.
2015-05-16 04:54:01 +02:00
this . update ( ) ;
2015-06-03 22:14:23 +02:00
} ,
calculateCircumference : function ( dataset , value ) {
if ( dataset . total > 0 ) {
return ( Math . PI * 2 ) * ( value / dataset . total ) ;
} else {
return 0 ;
}
} ,
2015-06-04 01:16:42 +02:00
resetElements : function ( ) {
2015-06-12 23:37:38 +02:00
this . outerRadius = ( helpers . min ( [ this . chart . width , this . chart . height ] ) - this . options . elements . arc . borderWidth / 2 ) / 2 ;
2015-06-04 01:16:42 +02:00
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 ) {
// So that calculateCircumference works
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 , {
_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 ,
2015-06-12 23:37:38 +02:00
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 ) ,
2015-06-04 01:16:42 +02:00
label : helpers . getValueAtIndexOrDefault ( dataset . label , index , this . data . labels [ index ] )
} ,
} ) ;
slice . pivot ( ) ;
} , this ) ;
} , this ) ;
} ,
2015-06-08 23:57:47 +02:00
update : function ( animationDuration ) {
2015-06-03 22:14:23 +02:00
2015-06-12 23:37:38 +02:00
this . outerRadius = ( helpers . min ( [ this . chart . width , this . chart . height ] ) - this . options . elements . arc . borderWidth / 2 ) / 2 ;
2015-06-03 22:17:10 +02:00
this . innerRadius = this . options . cutoutPercentage ? ( this . outerRadius / 100 ) * ( this . options . cutoutPercentage ) : 1 ;
2015-06-03 22:14:23 +02:00
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 ,
2015-06-12 23:37:38 +02:00
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 ) ,
2015-06-03 22:14:23 +02:00
label : helpers . getValueAtIndexOrDefault ( dataset . label , index , this . data . labels [ index ] )
} ,
} ) ;
if ( index === 0 ) {
2015-06-04 01:16:42 +02:00
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
2015-06-03 22:14:23 +02:00
} 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 ) ;
2015-06-08 23:57:47 +02:00
this . render ( animationDuration ) ;
2015-06-03 22:14:23 +02:00
} ,
draw : function ( easeDecimal ) {
easeDecimal = easeDecimal || 1 ;
this . clear ( ) ;
this . eachElement ( function ( slice ) {
slice . transition ( easeDecimal ) . draw ( ) ;
} , this ) ;
this . tooltip . transition ( easeDecimal ) . draw ( ) ;
2015-05-16 04:54:01 +02:00
} ,
2015-06-03 22:14:23 +02:00
events : function ( e ) {
2015-05-16 04:54:01 +02:00
this . lastActive = this . lastActive || [ ] ;
// Find Active Elements
2015-06-11 09:05:44 +02:00
if ( e . type == 'mouseout' ) {
2015-06-12 23:37:38 +02:00
this . active = [ ] ;
} else {
2015-06-11 09:05:44 +02:00
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 ) ;
}
2015-05-16 04:54:01 +02:00
// On Hover hook
2015-06-04 00:31:25 +02:00
if ( this . options . hover . onHover ) {
this . options . hover . onHover . call ( this , this . active ) ;
2015-05-16 04:54:01 +02:00
}
2015-06-04 00:31:25 +02:00
if ( e . type == 'mouseup' || e . type == 'click' ) {
if ( this . options . onClick ) {
this . options . onClick . call ( this , e , this . active ) ;
}
}
var dataset ;
var index ;
2015-05-16 04:54:01 +02:00
// Remove styling for last active (even if it may still be active)
if ( this . lastActive . length ) {
2015-06-04 00:31:25 +02:00
switch ( this . options . hover . mode ) {
case 'single' :
dataset = this . data . datasets [ this . lastActive [ 0 ] . _datasetIndex ] ;
index = this . lastActive [ 0 ] . _index ;
2015-06-12 23:37:38 +02:00
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 ) ;
2015-06-04 00:31:25 +02:00
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 ;
2015-06-12 23:37:38 +02:00
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 ) ;
2015-06-04 00:31:25 +02:00
}
break ;
case 'dataset' :
break ;
default :
// Don't change anything
}
2015-05-16 04:54:01 +02:00
}
// Built in hover styling
2015-05-27 03:16:18 +02:00
if ( this . active . length && this . options . hover . mode ) {
2015-06-04 00:31:25 +02:00
switch ( this . options . hover . mode ) {
case 'single' :
dataset = this . data . datasets [ this . active [ 0 ] . _datasetIndex ] ;
index = this . active [ 0 ] . _index ;
2015-06-11 09:05:44 +02:00
this . active [ 0 ] . _model . backgroundColor = this . active [ 0 ] . custom && this . active [ 0 ] . custom . hoverBackgroundColor ? this . active [ 0 ] . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBackgroundColor , index , helpers . color ( this . active [ 0 ] . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
2015-06-05 00:39:05 +02:00
this . active [ 0 ] . _model . borderColor = this . active [ 0 ] . custom && this . active [ 0 ] . custom . hoverBorderColor ? this . active [ 0 ] . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderColor , index , this . active [ 0 ] . _model . borderColor ) ;
this . active [ 0 ] . _model . borderWidth = this . active [ 0 ] . custom && this . active [ 0 ] . custom . hoverBorderWidth ? this . active [ 0 ] . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderWidth , index , this . active [ 0 ] . _model . borderWidth ) ;
2015-06-04 00:31:25 +02:00
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 ;
2015-06-11 09:05:44 +02:00
this . active [ i ] . _model . backgroundColor = this . active [ i ] . custom && this . active [ i ] . custom . hoverBackgroundColor ? this . active [ i ] . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBackgroundColor , index , helpers . color ( this . active [ i ] . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
2015-06-05 00:39:05 +02:00
this . active [ i ] . _model . borderColor = this . active [ i ] . custom && this . active [ i ] . custom . hoverBorderColor ? this . active [ i ] . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderColor , index , this . active [ 0 ] . _model . borderColor ) ;
this . active [ i ] . _model . borderWidth = this . active [ i ] . custom && this . active [ i ] . custom . hoverBorderWidth ? this . active [ i ] . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderWidth , index , this . active [ i ] . _model . borderWidth ) ;
2015-06-04 00:31:25 +02:00
}
break ;
case 'dataset' :
break ;
default :
// Don't change anything
}
2015-05-16 04:54:01 +02:00
}
2015-06-04 00:31:25 +02:00
2015-05-16 04:54:01 +02:00
// Built in Tooltips
2015-05-27 03:16:18 +02:00
if ( this . options . tooltips . enabled ) {
2015-05-16 04:54:01 +02:00
// The usual updates
this . tooltip . initialize ( ) ;
// Active
if ( this . active . length ) {
2015-06-04 00:31:25 +02:00
this . tooltip . _model . opacity = 1 ;
2015-05-16 04:54:01 +02:00
helpers . extend ( this . tooltip , {
_active : this . active ,
} ) ;
this . tooltip . update ( ) ;
} else {
// Inactive
2015-06-04 00:31:25 +02:00
this . tooltip . _model . opacity = 0 ;
2015-05-16 04:54:01 +02:00
}
}
// 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 ( ) ;
2015-05-27 01:30:34 +02:00
this . render ( this . options . hover . animationDuration ) ;
2015-05-16 04:54:01 +02:00
}
}
// Remember Last Active
this . lastActive = this . active ;
return this ;
2015-06-04 00:31:25 +02:00
} ,
getSliceAtEvent : function ( e ) {
var elements = [ ] ;
2015-05-16 04:54:01 +02:00
2015-06-04 00:31:25 +02:00
var location = helpers . getRelativePosition ( e ) ;
this . eachElement ( function ( slice , index ) {
if ( slice . inRange ( location . x , location . y ) ) {
elements . push ( slice ) ;
}
} , this ) ;
return elements ;
2015-05-16 04:54:01 +02:00
} ,
2015-06-04 00:31:25 +02:00
/ * g e t S l i c e s A t E v e n t : f u n c t i o n ( 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 ;
} , * /
2015-05-16 04:54:01 +02:00
} ) ;
Chart . types . Doughnut . extend ( {
name : "Pie" ,
defaults : helpers . merge ( defaultConfig , {
2015-05-16 06:34:08 +02:00
cutoutPercentage : 0
2015-05-16 04:54:01 +02:00
} )
} ) ;
2014-06-29 19:36:25 +02:00
2014-10-21 03:49:41 +02:00
} ) . call ( this ) ;