2016-05-24 07:01:16 +00:00
( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; var f = new Error ( "Cannot find module '" + o + "'" ) ; throw f . code = "MODULE_NOT_FOUND" , f } var l = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( l . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , l , l . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( require , module , exports ) {
} , { } ] , 2 : [ function ( require , module , exports ) {
/* MIT license */
module . exports = {
rgb2hsl : rgb2hsl ,
rgb2hsv : rgb2hsv ,
rgb2hwb : rgb2hwb ,
rgb2cmyk : rgb2cmyk ,
rgb2keyword : rgb2keyword ,
rgb2xyz : rgb2xyz ,
rgb2lab : rgb2lab ,
rgb2lch : rgb2lch ,
hsl2rgb : hsl2rgb ,
hsl2hsv : hsl2hsv ,
hsl2hwb : hsl2hwb ,
hsl2cmyk : hsl2cmyk ,
hsl2keyword : hsl2keyword ,
hsv2rgb : hsv2rgb ,
hsv2hsl : hsv2hsl ,
hsv2hwb : hsv2hwb ,
hsv2cmyk : hsv2cmyk ,
hsv2keyword : hsv2keyword ,
hwb2rgb : hwb2rgb ,
hwb2hsl : hwb2hsl ,
hwb2hsv : hwb2hsv ,
hwb2cmyk : hwb2cmyk ,
hwb2keyword : hwb2keyword ,
cmyk2rgb : cmyk2rgb ,
cmyk2hsl : cmyk2hsl ,
cmyk2hsv : cmyk2hsv ,
cmyk2hwb : cmyk2hwb ,
cmyk2keyword : cmyk2keyword ,
keyword2rgb : keyword2rgb ,
keyword2hsl : keyword2hsl ,
keyword2hsv : keyword2hsv ,
keyword2hwb : keyword2hwb ,
keyword2cmyk : keyword2cmyk ,
keyword2lab : keyword2lab ,
keyword2xyz : keyword2xyz ,
xyz2rgb : xyz2rgb ,
xyz2lab : xyz2lab ,
xyz2lch : xyz2lch ,
lab2xyz : lab2xyz ,
lab2rgb : lab2rgb ,
lab2lch : lab2lch ,
lch2lab : lch2lab ,
lch2xyz : lch2xyz ,
lch2rgb : lch2rgb
}
function rgb2hsl ( rgb ) {
var r = rgb [ 0 ] / 255 ,
g = rgb [ 1 ] / 255 ,
b = rgb [ 2 ] / 255 ,
min = Math . min ( r , g , b ) ,
max = Math . max ( r , g , b ) ,
delta = max - min ,
h , s , l ;
if ( max == min )
h = 0 ;
else if ( r == max )
h = ( g - b ) / delta ;
else if ( g == max )
h = 2 + ( b - r ) / delta ;
else if ( b == max )
h = 4 + ( r - g ) / delta ;
h = Math . min ( h * 60 , 360 ) ;
if ( h < 0 )
h += 360 ;
l = ( min + max ) / 2 ;
if ( max == min )
s = 0 ;
else if ( l <= 0.5 )
s = delta / ( max + min ) ;
else
s = delta / ( 2 - max - min ) ;
return [ h , s * 100 , l * 100 ] ;
}
function rgb2hsv ( rgb ) {
var r = rgb [ 0 ] ,
g = rgb [ 1 ] ,
b = rgb [ 2 ] ,
min = Math . min ( r , g , b ) ,
max = Math . max ( r , g , b ) ,
delta = max - min ,
h , s , v ;
if ( max == 0 )
s = 0 ;
else
s = ( delta / max * 1000 ) / 10 ;
if ( max == min )
h = 0 ;
else if ( r == max )
h = ( g - b ) / delta ;
else if ( g == max )
h = 2 + ( b - r ) / delta ;
else if ( b == max )
h = 4 + ( r - g ) / delta ;
h = Math . min ( h * 60 , 360 ) ;
if ( h < 0 )
h += 360 ;
v = ( ( max / 255 ) * 1000 ) / 10 ;
return [ h , s , v ] ;
}
function rgb2hwb ( rgb ) {
var r = rgb [ 0 ] ,
g = rgb [ 1 ] ,
b = rgb [ 2 ] ,
h = rgb2hsl ( rgb ) [ 0 ] ,
w = 1 / 255 * Math . min ( r , Math . min ( g , b ) ) ,
b = 1 - 1 / 255 * Math . max ( r , Math . max ( g , b ) ) ;
return [ h , w * 100 , b * 100 ] ;
}
function rgb2cmyk ( rgb ) {
var r = rgb [ 0 ] / 255 ,
g = rgb [ 1 ] / 255 ,
b = rgb [ 2 ] / 255 ,
c , m , y , k ;
k = Math . min ( 1 - r , 1 - g , 1 - b ) ;
c = ( 1 - r - k ) / ( 1 - k ) || 0 ;
m = ( 1 - g - k ) / ( 1 - k ) || 0 ;
y = ( 1 - b - k ) / ( 1 - k ) || 0 ;
return [ c * 100 , m * 100 , y * 100 , k * 100 ] ;
}
function rgb2keyword ( rgb ) {
return reverseKeywords [ JSON . stringify ( rgb ) ] ;
}
function rgb2xyz ( rgb ) {
var r = rgb [ 0 ] / 255 ,
g = rgb [ 1 ] / 255 ,
b = rgb [ 2 ] / 255 ;
// assume sRGB
r = r > 0.04045 ? Math . pow ( ( ( r + 0.055 ) / 1.055 ) , 2.4 ) : ( r / 12.92 ) ;
g = g > 0.04045 ? Math . pow ( ( ( g + 0.055 ) / 1.055 ) , 2.4 ) : ( g / 12.92 ) ;
b = b > 0.04045 ? Math . pow ( ( ( b + 0.055 ) / 1.055 ) , 2.4 ) : ( b / 12.92 ) ;
var x = ( r * 0.4124 ) + ( g * 0.3576 ) + ( b * 0.1805 ) ;
var y = ( r * 0.2126 ) + ( g * 0.7152 ) + ( b * 0.0722 ) ;
var z = ( r * 0.0193 ) + ( g * 0.1192 ) + ( b * 0.9505 ) ;
return [ x * 100 , y * 100 , z * 100 ] ;
}
function rgb2lab ( rgb ) {
var xyz = rgb2xyz ( rgb ) ,
x = xyz [ 0 ] ,
y = xyz [ 1 ] ,
z = xyz [ 2 ] ,
l , a , b ;
x /= 95.047 ;
y /= 100 ;
z /= 108.883 ;
x = x > 0.008856 ? Math . pow ( x , 1 / 3 ) : ( 7.787 * x ) + ( 16 / 116 ) ;
y = y > 0.008856 ? Math . pow ( y , 1 / 3 ) : ( 7.787 * y ) + ( 16 / 116 ) ;
z = z > 0.008856 ? Math . pow ( z , 1 / 3 ) : ( 7.787 * z ) + ( 16 / 116 ) ;
l = ( 116 * y ) - 16 ;
a = 500 * ( x - y ) ;
b = 200 * ( y - z ) ;
return [ l , a , b ] ;
}
function rgb2lch ( args ) {
return lab2lch ( rgb2lab ( args ) ) ;
}
function hsl2rgb ( hsl ) {
var h = hsl [ 0 ] / 360 ,
s = hsl [ 1 ] / 100 ,
l = hsl [ 2 ] / 100 ,
t1 , t2 , t3 , rgb , val ;
if ( s == 0 ) {
val = l * 255 ;
return [ val , val , val ] ;
}
if ( l < 0.5 )
t2 = l * ( 1 + s ) ;
else
t2 = l + s - l * s ;
t1 = 2 * l - t2 ;
rgb = [ 0 , 0 , 0 ] ;
for ( var i = 0 ; i < 3 ; i ++ ) {
t3 = h + 1 / 3 * - ( i - 1 ) ;
t3 < 0 && t3 ++ ;
t3 > 1 && t3 -- ;
if ( 6 * t3 < 1 )
val = t1 + ( t2 - t1 ) * 6 * t3 ;
else if ( 2 * t3 < 1 )
val = t2 ;
else if ( 3 * t3 < 2 )
val = t1 + ( t2 - t1 ) * ( 2 / 3 - t3 ) * 6 ;
else
val = t1 ;
rgb [ i ] = val * 255 ;
}
return rgb ;
}
function hsl2hsv ( hsl ) {
var h = hsl [ 0 ] ,
s = hsl [ 1 ] / 100 ,
l = hsl [ 2 ] / 100 ,
sv , v ;
if ( l === 0 ) {
// no need to do calc on black
// also avoids divide by 0 error
return [ 0 , 0 , 0 ] ;
}
l *= 2 ;
s *= ( l <= 1 ) ? l : 2 - l ;
v = ( l + s ) / 2 ;
sv = ( 2 * s ) / ( l + s ) ;
return [ h , sv * 100 , v * 100 ] ;
}
function hsl2hwb ( args ) {
return rgb2hwb ( hsl2rgb ( args ) ) ;
}
function hsl2cmyk ( args ) {
return rgb2cmyk ( hsl2rgb ( args ) ) ;
}
function hsl2keyword ( args ) {
return rgb2keyword ( hsl2rgb ( args ) ) ;
}
function hsv2rgb ( hsv ) {
var h = hsv [ 0 ] / 60 ,
s = hsv [ 1 ] / 100 ,
v = hsv [ 2 ] / 100 ,
hi = Math . floor ( h ) % 6 ;
var f = h - Math . floor ( h ) ,
p = 255 * v * ( 1 - s ) ,
q = 255 * v * ( 1 - ( s * f ) ) ,
t = 255 * v * ( 1 - ( s * ( 1 - f ) ) ) ,
v = 255 * v ;
switch ( hi ) {
case 0 :
return [ v , t , p ] ;
case 1 :
return [ q , v , p ] ;
case 2 :
return [ p , v , t ] ;
case 3 :
return [ p , q , v ] ;
case 4 :
return [ t , p , v ] ;
case 5 :
return [ v , p , q ] ;
}
}
function hsv2hsl ( hsv ) {
var h = hsv [ 0 ] ,
s = hsv [ 1 ] / 100 ,
v = hsv [ 2 ] / 100 ,
sl , l ;
l = ( 2 - s ) * v ;
sl = s * v ;
sl /= ( l <= 1 ) ? l : 2 - l ;
sl = sl || 0 ;
l /= 2 ;
return [ h , sl * 100 , l * 100 ] ;
}
function hsv2hwb ( args ) {
return rgb2hwb ( hsv2rgb ( args ) )
}
function hsv2cmyk ( args ) {
return rgb2cmyk ( hsv2rgb ( args ) ) ;
}
function hsv2keyword ( args ) {
return rgb2keyword ( hsv2rgb ( args ) ) ;
}
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
function hwb2rgb ( hwb ) {
var h = hwb [ 0 ] / 360 ,
wh = hwb [ 1 ] / 100 ,
bl = hwb [ 2 ] / 100 ,
ratio = wh + bl ,
i , v , f , n ;
// wh + bl cant be > 1
if ( ratio > 1 ) {
wh /= ratio ;
bl /= ratio ;
}
i = Math . floor ( 6 * h ) ;
v = 1 - bl ;
f = 6 * h - i ;
if ( ( i & 0x01 ) != 0 ) {
f = 1 - f ;
}
n = wh + f * ( v - wh ) ; // linear interpolation
switch ( i ) {
default :
case 6 :
case 0 : r = v ; g = n ; b = wh ; break ;
case 1 : r = n ; g = v ; b = wh ; break ;
case 2 : r = wh ; g = v ; b = n ; break ;
case 3 : r = wh ; g = n ; b = v ; break ;
case 4 : r = n ; g = wh ; b = v ; break ;
case 5 : r = v ; g = wh ; b = n ; break ;
}
return [ r * 255 , g * 255 , b * 255 ] ;
}
function hwb2hsl ( args ) {
return rgb2hsl ( hwb2rgb ( args ) ) ;
}
function hwb2hsv ( args ) {
return rgb2hsv ( hwb2rgb ( args ) ) ;
}
function hwb2cmyk ( args ) {
return rgb2cmyk ( hwb2rgb ( args ) ) ;
}
function hwb2keyword ( args ) {
return rgb2keyword ( hwb2rgb ( args ) ) ;
}
function cmyk2rgb ( cmyk ) {
var c = cmyk [ 0 ] / 100 ,
m = cmyk [ 1 ] / 100 ,
y = cmyk [ 2 ] / 100 ,
k = cmyk [ 3 ] / 100 ,
r , g , b ;
r = 1 - Math . min ( 1 , c * ( 1 - k ) + k ) ;
g = 1 - Math . min ( 1 , m * ( 1 - k ) + k ) ;
b = 1 - Math . min ( 1 , y * ( 1 - k ) + k ) ;
return [ r * 255 , g * 255 , b * 255 ] ;
}
function cmyk2hsl ( args ) {
return rgb2hsl ( cmyk2rgb ( args ) ) ;
}
function cmyk2hsv ( args ) {
return rgb2hsv ( cmyk2rgb ( args ) ) ;
}
function cmyk2hwb ( args ) {
return rgb2hwb ( cmyk2rgb ( args ) ) ;
}
function cmyk2keyword ( args ) {
return rgb2keyword ( cmyk2rgb ( args ) ) ;
}
function xyz2rgb ( xyz ) {
var x = xyz [ 0 ] / 100 ,
y = xyz [ 1 ] / 100 ,
z = xyz [ 2 ] / 100 ,
r , g , b ;
r = ( x * 3.2406 ) + ( y * - 1.5372 ) + ( z * - 0.4986 ) ;
g = ( x * - 0.9689 ) + ( y * 1.8758 ) + ( z * 0.0415 ) ;
b = ( x * 0.0557 ) + ( y * - 0.2040 ) + ( z * 1.0570 ) ;
// assume sRGB
r = r > 0.0031308 ? ( ( 1.055 * Math . pow ( r , 1.0 / 2.4 ) ) - 0.055 )
: r = ( r * 12.92 ) ;
g = g > 0.0031308 ? ( ( 1.055 * Math . pow ( g , 1.0 / 2.4 ) ) - 0.055 )
: g = ( g * 12.92 ) ;
b = b > 0.0031308 ? ( ( 1.055 * Math . pow ( b , 1.0 / 2.4 ) ) - 0.055 )
: b = ( b * 12.92 ) ;
r = Math . min ( Math . max ( 0 , r ) , 1 ) ;
g = Math . min ( Math . max ( 0 , g ) , 1 ) ;
b = Math . min ( Math . max ( 0 , b ) , 1 ) ;
return [ r * 255 , g * 255 , b * 255 ] ;
}
function xyz2lab ( xyz ) {
var x = xyz [ 0 ] ,
y = xyz [ 1 ] ,
z = xyz [ 2 ] ,
l , a , b ;
x /= 95.047 ;
y /= 100 ;
z /= 108.883 ;
x = x > 0.008856 ? Math . pow ( x , 1 / 3 ) : ( 7.787 * x ) + ( 16 / 116 ) ;
y = y > 0.008856 ? Math . pow ( y , 1 / 3 ) : ( 7.787 * y ) + ( 16 / 116 ) ;
z = z > 0.008856 ? Math . pow ( z , 1 / 3 ) : ( 7.787 * z ) + ( 16 / 116 ) ;
l = ( 116 * y ) - 16 ;
a = 500 * ( x - y ) ;
b = 200 * ( y - z ) ;
return [ l , a , b ] ;
}
function xyz2lch ( args ) {
return lab2lch ( xyz2lab ( args ) ) ;
}
function lab2xyz ( lab ) {
var l = lab [ 0 ] ,
a = lab [ 1 ] ,
b = lab [ 2 ] ,
x , y , z , y2 ;
if ( l <= 8 ) {
y = ( l * 100 ) / 903.3 ;
y2 = ( 7.787 * ( y / 100 ) ) + ( 16 / 116 ) ;
} else {
y = 100 * Math . pow ( ( l + 16 ) / 116 , 3 ) ;
y2 = Math . pow ( y / 100 , 1 / 3 ) ;
}
x = x / 95.047 <= 0.008856 ? x = ( 95.047 * ( ( a / 500 ) + y2 - ( 16 / 116 ) ) ) / 7.787 : 95.047 * Math . pow ( ( a / 500 ) + y2 , 3 ) ;
z = z / 108.883 <= 0.008859 ? z = ( 108.883 * ( y2 - ( b / 200 ) - ( 16 / 116 ) ) ) / 7.787 : 108.883 * Math . pow ( y2 - ( b / 200 ) , 3 ) ;
return [ x , y , z ] ;
}
function lab2lch ( lab ) {
var l = lab [ 0 ] ,
a = lab [ 1 ] ,
b = lab [ 2 ] ,
hr , h , c ;
hr = Math . atan2 ( b , a ) ;
h = hr * 360 / 2 / Math . PI ;
if ( h < 0 ) {
h += 360 ;
}
c = Math . sqrt ( a * a + b * b ) ;
return [ l , c , h ] ;
}
function lab2rgb ( args ) {
return xyz2rgb ( lab2xyz ( args ) ) ;
}
function lch2lab ( lch ) {
var l = lch [ 0 ] ,
c = lch [ 1 ] ,
h = lch [ 2 ] ,
a , b , hr ;
hr = h / 360 * 2 * Math . PI ;
a = c * Math . cos ( hr ) ;
b = c * Math . sin ( hr ) ;
return [ l , a , b ] ;
}
function lch2xyz ( args ) {
return lab2xyz ( lch2lab ( args ) ) ;
}
function lch2rgb ( args ) {
return lab2rgb ( lch2lab ( args ) ) ;
}
function keyword2rgb ( keyword ) {
return cssKeywords [ keyword ] ;
}
function keyword2hsl ( args ) {
return rgb2hsl ( keyword2rgb ( args ) ) ;
}
function keyword2hsv ( args ) {
return rgb2hsv ( keyword2rgb ( args ) ) ;
}
function keyword2hwb ( args ) {
return rgb2hwb ( keyword2rgb ( args ) ) ;
}
function keyword2cmyk ( args ) {
return rgb2cmyk ( keyword2rgb ( args ) ) ;
}
function keyword2lab ( args ) {
return rgb2lab ( keyword2rgb ( args ) ) ;
}
function keyword2xyz ( args ) {
return rgb2xyz ( keyword2rgb ( args ) ) ;
}
var cssKeywords = {
aliceblue : [ 240 , 248 , 255 ] ,
antiquewhite : [ 250 , 235 , 215 ] ,
aqua : [ 0 , 255 , 255 ] ,
aquamarine : [ 127 , 255 , 212 ] ,
azure : [ 240 , 255 , 255 ] ,
beige : [ 245 , 245 , 220 ] ,
bisque : [ 255 , 228 , 196 ] ,
black : [ 0 , 0 , 0 ] ,
blanchedalmond : [ 255 , 235 , 205 ] ,
blue : [ 0 , 0 , 255 ] ,
blueviolet : [ 138 , 43 , 226 ] ,
brown : [ 165 , 42 , 42 ] ,
burlywood : [ 222 , 184 , 135 ] ,
cadetblue : [ 95 , 158 , 160 ] ,
chartreuse : [ 127 , 255 , 0 ] ,
chocolate : [ 210 , 105 , 30 ] ,
coral : [ 255 , 127 , 80 ] ,
cornflowerblue : [ 100 , 149 , 237 ] ,
cornsilk : [ 255 , 248 , 220 ] ,
crimson : [ 220 , 20 , 60 ] ,
cyan : [ 0 , 255 , 255 ] ,
darkblue : [ 0 , 0 , 139 ] ,
darkcyan : [ 0 , 139 , 139 ] ,
darkgoldenrod : [ 184 , 134 , 11 ] ,
darkgray : [ 169 , 169 , 169 ] ,
darkgreen : [ 0 , 100 , 0 ] ,
darkgrey : [ 169 , 169 , 169 ] ,
darkkhaki : [ 189 , 183 , 107 ] ,
darkmagenta : [ 139 , 0 , 139 ] ,
darkolivegreen : [ 85 , 107 , 47 ] ,
darkorange : [ 255 , 140 , 0 ] ,
darkorchid : [ 153 , 50 , 204 ] ,
darkred : [ 139 , 0 , 0 ] ,
darksalmon : [ 233 , 150 , 122 ] ,
darkseagreen : [ 143 , 188 , 143 ] ,
darkslateblue : [ 72 , 61 , 139 ] ,
darkslategray : [ 47 , 79 , 79 ] ,
darkslategrey : [ 47 , 79 , 79 ] ,
darkturquoise : [ 0 , 206 , 209 ] ,
darkviolet : [ 148 , 0 , 211 ] ,
deeppink : [ 255 , 20 , 147 ] ,
deepskyblue : [ 0 , 191 , 255 ] ,
dimgray : [ 105 , 105 , 105 ] ,
dimgrey : [ 105 , 105 , 105 ] ,
dodgerblue : [ 30 , 144 , 255 ] ,
firebrick : [ 178 , 34 , 34 ] ,
floralwhite : [ 255 , 250 , 240 ] ,
forestgreen : [ 34 , 139 , 34 ] ,
fuchsia : [ 255 , 0 , 255 ] ,
gainsboro : [ 220 , 220 , 220 ] ,
ghostwhite : [ 248 , 248 , 255 ] ,
gold : [ 255 , 215 , 0 ] ,
goldenrod : [ 218 , 165 , 32 ] ,
gray : [ 128 , 128 , 128 ] ,
green : [ 0 , 128 , 0 ] ,
greenyellow : [ 173 , 255 , 47 ] ,
grey : [ 128 , 128 , 128 ] ,
honeydew : [ 240 , 255 , 240 ] ,
hotpink : [ 255 , 105 , 180 ] ,
indianred : [ 205 , 92 , 92 ] ,
indigo : [ 75 , 0 , 130 ] ,
ivory : [ 255 , 255 , 240 ] ,
khaki : [ 240 , 230 , 140 ] ,
lavender : [ 230 , 230 , 250 ] ,
lavenderblush : [ 255 , 240 , 245 ] ,
lawngreen : [ 124 , 252 , 0 ] ,
lemonchiffon : [ 255 , 250 , 205 ] ,
lightblue : [ 173 , 216 , 230 ] ,
lightcoral : [ 240 , 128 , 128 ] ,
lightcyan : [ 224 , 255 , 255 ] ,
lightgoldenrodyellow : [ 250 , 250 , 210 ] ,
lightgray : [ 211 , 211 , 211 ] ,
lightgreen : [ 144 , 238 , 144 ] ,
lightgrey : [ 211 , 211 , 211 ] ,
lightpink : [ 255 , 182 , 193 ] ,
lightsalmon : [ 255 , 160 , 122 ] ,
lightseagreen : [ 32 , 178 , 170 ] ,
lightskyblue : [ 135 , 206 , 250 ] ,
lightslategray : [ 119 , 136 , 153 ] ,
lightslategrey : [ 119 , 136 , 153 ] ,
lightsteelblue : [ 176 , 196 , 222 ] ,
lightyellow : [ 255 , 255 , 224 ] ,
lime : [ 0 , 255 , 0 ] ,
limegreen : [ 50 , 205 , 50 ] ,
linen : [ 250 , 240 , 230 ] ,
magenta : [ 255 , 0 , 255 ] ,
maroon : [ 128 , 0 , 0 ] ,
mediumaquamarine : [ 102 , 205 , 170 ] ,
mediumblue : [ 0 , 0 , 205 ] ,
mediumorchid : [ 186 , 85 , 211 ] ,
mediumpurple : [ 147 , 112 , 219 ] ,
mediumseagreen : [ 60 , 179 , 113 ] ,
mediumslateblue : [ 123 , 104 , 238 ] ,
mediumspringgreen : [ 0 , 250 , 154 ] ,
mediumturquoise : [ 72 , 209 , 204 ] ,
mediumvioletred : [ 199 , 21 , 133 ] ,
midnightblue : [ 25 , 25 , 112 ] ,
mintcream : [ 245 , 255 , 250 ] ,
mistyrose : [ 255 , 228 , 225 ] ,
moccasin : [ 255 , 228 , 181 ] ,
navajowhite : [ 255 , 222 , 173 ] ,
navy : [ 0 , 0 , 128 ] ,
oldlace : [ 253 , 245 , 230 ] ,
olive : [ 128 , 128 , 0 ] ,
olivedrab : [ 107 , 142 , 35 ] ,
orange : [ 255 , 165 , 0 ] ,
orangered : [ 255 , 69 , 0 ] ,
orchid : [ 218 , 112 , 214 ] ,
palegoldenrod : [ 238 , 232 , 170 ] ,
palegreen : [ 152 , 251 , 152 ] ,
paleturquoise : [ 175 , 238 , 238 ] ,
palevioletred : [ 219 , 112 , 147 ] ,
papayawhip : [ 255 , 239 , 213 ] ,
peachpuff : [ 255 , 218 , 185 ] ,
peru : [ 205 , 133 , 63 ] ,
pink : [ 255 , 192 , 203 ] ,
plum : [ 221 , 160 , 221 ] ,
powderblue : [ 176 , 224 , 230 ] ,
purple : [ 128 , 0 , 128 ] ,
rebeccapurple : [ 102 , 51 , 153 ] ,
red : [ 255 , 0 , 0 ] ,
rosybrown : [ 188 , 143 , 143 ] ,
royalblue : [ 65 , 105 , 225 ] ,
saddlebrown : [ 139 , 69 , 19 ] ,
salmon : [ 250 , 128 , 114 ] ,
sandybrown : [ 244 , 164 , 96 ] ,
seagreen : [ 46 , 139 , 87 ] ,
seashell : [ 255 , 245 , 238 ] ,
sienna : [ 160 , 82 , 45 ] ,
silver : [ 192 , 192 , 192 ] ,
skyblue : [ 135 , 206 , 235 ] ,
slateblue : [ 106 , 90 , 205 ] ,
slategray : [ 112 , 128 , 144 ] ,
slategrey : [ 112 , 128 , 144 ] ,
snow : [ 255 , 250 , 250 ] ,
springgreen : [ 0 , 255 , 127 ] ,
steelblue : [ 70 , 130 , 180 ] ,
tan : [ 210 , 180 , 140 ] ,
teal : [ 0 , 128 , 128 ] ,
thistle : [ 216 , 191 , 216 ] ,
tomato : [ 255 , 99 , 71 ] ,
turquoise : [ 64 , 224 , 208 ] ,
violet : [ 238 , 130 , 238 ] ,
wheat : [ 245 , 222 , 179 ] ,
white : [ 255 , 255 , 255 ] ,
whitesmoke : [ 245 , 245 , 245 ] ,
yellow : [ 255 , 255 , 0 ] ,
yellowgreen : [ 154 , 205 , 50 ]
} ;
var reverseKeywords = { } ;
for ( var key in cssKeywords ) {
reverseKeywords [ JSON . stringify ( cssKeywords [ key ] ) ] = key ;
}
} , { } ] , 3 : [ function ( require , module , exports ) {
var conversions = require ( "./conversions" ) ;
var convert = function ( ) {
return new Converter ( ) ;
}
for ( var func in conversions ) {
// export Raw versions
convert [ func + "Raw" ] = ( function ( func ) {
// accept array or plain args
return function ( arg ) {
if ( typeof arg == "number" )
arg = Array . prototype . slice . call ( arguments ) ;
return conversions [ func ] ( arg ) ;
}
} ) ( func ) ;
var pair = /(\w+)2(\w+)/ . exec ( func ) ,
from = pair [ 1 ] ,
to = pair [ 2 ] ;
// export rgb2hsl and ["rgb"]["hsl"]
convert [ from ] = convert [ from ] || { } ;
convert [ from ] [ to ] = convert [ func ] = ( function ( func ) {
return function ( arg ) {
if ( typeof arg == "number" )
arg = Array . prototype . slice . call ( arguments ) ;
var val = conversions [ func ] ( arg ) ;
if ( typeof val == "string" || val === undefined )
return val ; // keyword
for ( var i = 0 ; i < val . length ; i ++ )
val [ i ] = Math . round ( val [ i ] ) ;
return val ;
}
} ) ( func ) ;
}
/* Converter does lazy conversion and caching */
var Converter = function ( ) {
this . convs = { } ;
} ;
/ * E i t h e r g e t t h e v a l u e s f o r a s p a c e o r
set the values for a space , depending on args * /
Converter . prototype . routeSpace = function ( space , args ) {
var values = args [ 0 ] ;
if ( values === undefined ) {
// color.rgb()
return this . getValues ( space ) ;
}
// color.rgb(10, 10, 10)
if ( typeof values == "number" ) {
values = Array . prototype . slice . call ( args ) ;
}
return this . setValues ( space , values ) ;
} ;
/* Set the values for a space, invalidating cache */
Converter . prototype . setValues = function ( space , values ) {
this . space = space ;
this . convs = { } ;
this . convs [ space ] = values ;
return this ;
} ;
/ * G e t t h e v a l u e s f o r a s p a c e . I f t h e r e ' s a l r e a d y
a conversion for the space , fetch it , otherwise
compute it * /
Converter . prototype . getValues = function ( space ) {
var vals = this . convs [ space ] ;
if ( ! vals ) {
var fspace = this . space ,
from = this . convs [ fspace ] ;
vals = convert [ fspace ] [ space ] ( from ) ;
this . convs [ space ] = vals ;
}
return vals ;
} ;
[ "rgb" , "hsl" , "hsv" , "cmyk" , "keyword" ] . forEach ( function ( space ) {
Converter . prototype [ space ] = function ( vals ) {
return this . routeSpace ( space , arguments ) ;
}
} ) ;
module . exports = convert ;
} , { "./conversions" : 2 } ] , 4 : [ function ( require , module , exports ) {
/* MIT license */
var colorNames = require ( 'color-name' ) ;
module . exports = {
getRgba : getRgba ,
getHsla : getHsla ,
getRgb : getRgb ,
getHsl : getHsl ,
getHwb : getHwb ,
getAlpha : getAlpha ,
hexString : hexString ,
rgbString : rgbString ,
rgbaString : rgbaString ,
percentString : percentString ,
percentaString : percentaString ,
hslString : hslString ,
hslaString : hslaString ,
hwbString : hwbString ,
keyword : keyword
}
function getRgba ( string ) {
if ( ! string ) {
return ;
}
var abbr = /^#([a-fA-F0-9]{3})$/ ,
hex = /^#([a-fA-F0-9]{6})$/ ,
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/ ,
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/ ,
keyword = /(\w+)/ ;
var rgb = [ 0 , 0 , 0 ] ,
a = 1 ,
match = string . match ( abbr ) ;
if ( match ) {
match = match [ 1 ] ;
for ( var i = 0 ; i < rgb . length ; i ++ ) {
rgb [ i ] = parseInt ( match [ i ] + match [ i ] , 16 ) ;
}
}
else if ( match = string . match ( hex ) ) {
match = match [ 1 ] ;
for ( var i = 0 ; i < rgb . length ; i ++ ) {
rgb [ i ] = parseInt ( match . slice ( i * 2 , i * 2 + 2 ) , 16 ) ;
}
}
else if ( match = string . match ( rgba ) ) {
for ( var i = 0 ; i < rgb . length ; i ++ ) {
rgb [ i ] = parseInt ( match [ i + 1 ] ) ;
}
a = parseFloat ( match [ 4 ] ) ;
}
else if ( match = string . match ( per ) ) {
for ( var i = 0 ; i < rgb . length ; i ++ ) {
rgb [ i ] = Math . round ( parseFloat ( match [ i + 1 ] ) * 2.55 ) ;
}
a = parseFloat ( match [ 4 ] ) ;
}
else if ( match = string . match ( keyword ) ) {
if ( match [ 1 ] == "transparent" ) {
return [ 0 , 0 , 0 , 0 ] ;
}
rgb = colorNames [ match [ 1 ] ] ;
if ( ! rgb ) {
return ;
}
}
for ( var i = 0 ; i < rgb . length ; i ++ ) {
rgb [ i ] = scale ( rgb [ i ] , 0 , 255 ) ;
}
if ( ! a && a != 0 ) {
a = 1 ;
}
else {
a = scale ( a , 0 , 1 ) ;
}
rgb [ 3 ] = a ;
return rgb ;
}
function getHsla ( string ) {
if ( ! string ) {
return ;
}
var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/ ;
var match = string . match ( hsl ) ;
if ( match ) {
var alpha = parseFloat ( match [ 4 ] ) ;
var h = scale ( parseInt ( match [ 1 ] ) , 0 , 360 ) ,
s = scale ( parseFloat ( match [ 2 ] ) , 0 , 100 ) ,
l = scale ( parseFloat ( match [ 3 ] ) , 0 , 100 ) ,
a = scale ( isNaN ( alpha ) ? 1 : alpha , 0 , 1 ) ;
return [ h , s , l , a ] ;
}
}
function getHwb ( string ) {
if ( ! string ) {
return ;
}
var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/ ;
var match = string . match ( hwb ) ;
if ( match ) {
var alpha = parseFloat ( match [ 4 ] ) ;
var h = scale ( parseInt ( match [ 1 ] ) , 0 , 360 ) ,
w = scale ( parseFloat ( match [ 2 ] ) , 0 , 100 ) ,
b = scale ( parseFloat ( match [ 3 ] ) , 0 , 100 ) ,
a = scale ( isNaN ( alpha ) ? 1 : alpha , 0 , 1 ) ;
return [ h , w , b , a ] ;
}
}
function getRgb ( string ) {
var rgba = getRgba ( string ) ;
return rgba && rgba . slice ( 0 , 3 ) ;
}
function getHsl ( string ) {
var hsla = getHsla ( string ) ;
return hsla && hsla . slice ( 0 , 3 ) ;
}
function getAlpha ( string ) {
var vals = getRgba ( string ) ;
if ( vals ) {
return vals [ 3 ] ;
}
else if ( vals = getHsla ( string ) ) {
return vals [ 3 ] ;
}
else if ( vals = getHwb ( string ) ) {
return vals [ 3 ] ;
}
}
// generators
function hexString ( rgb ) {
return "#" + hexDouble ( rgb [ 0 ] ) + hexDouble ( rgb [ 1 ] )
+ hexDouble ( rgb [ 2 ] ) ;
}
function rgbString ( rgba , alpha ) {
if ( alpha < 1 || ( rgba [ 3 ] && rgba [ 3 ] < 1 ) ) {
return rgbaString ( rgba , alpha ) ;
}
return "rgb(" + rgba [ 0 ] + ", " + rgba [ 1 ] + ", " + rgba [ 2 ] + ")" ;
}
function rgbaString ( rgba , alpha ) {
if ( alpha === undefined ) {
alpha = ( rgba [ 3 ] !== undefined ? rgba [ 3 ] : 1 ) ;
}
return "rgba(" + rgba [ 0 ] + ", " + rgba [ 1 ] + ", " + rgba [ 2 ]
+ ", " + alpha + ")" ;
}
function percentString ( rgba , alpha ) {
if ( alpha < 1 || ( rgba [ 3 ] && rgba [ 3 ] < 1 ) ) {
return percentaString ( rgba , alpha ) ;
}
var r = Math . round ( rgba [ 0 ] / 255 * 100 ) ,
g = Math . round ( rgba [ 1 ] / 255 * 100 ) ,
b = Math . round ( rgba [ 2 ] / 255 * 100 ) ;
return "rgb(" + r + "%, " + g + "%, " + b + "%)" ;
}
function percentaString ( rgba , alpha ) {
var r = Math . round ( rgba [ 0 ] / 255 * 100 ) ,
g = Math . round ( rgba [ 1 ] / 255 * 100 ) ,
b = Math . round ( rgba [ 2 ] / 255 * 100 ) ;
return "rgba(" + r + "%, " + g + "%, " + b + "%, " + ( alpha || rgba [ 3 ] || 1 ) + ")" ;
}
function hslString ( hsla , alpha ) {
if ( alpha < 1 || ( hsla [ 3 ] && hsla [ 3 ] < 1 ) ) {
return hslaString ( hsla , alpha ) ;
}
return "hsl(" + hsla [ 0 ] + ", " + hsla [ 1 ] + "%, " + hsla [ 2 ] + "%)" ;
}
function hslaString ( hsla , alpha ) {
if ( alpha === undefined ) {
alpha = ( hsla [ 3 ] !== undefined ? hsla [ 3 ] : 1 ) ;
}
return "hsla(" + hsla [ 0 ] + ", " + hsla [ 1 ] + "%, " + hsla [ 2 ] + "%, "
+ alpha + ")" ;
}
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
// (hwb have alpha optional & 1 is default value)
function hwbString ( hwb , alpha ) {
if ( alpha === undefined ) {
alpha = ( hwb [ 3 ] !== undefined ? hwb [ 3 ] : 1 ) ;
}
return "hwb(" + hwb [ 0 ] + ", " + hwb [ 1 ] + "%, " + hwb [ 2 ] + "%"
+ ( alpha !== undefined && alpha !== 1 ? ", " + alpha : "" ) + ")" ;
}
function keyword ( rgb ) {
return reverseNames [ rgb . slice ( 0 , 3 ) ] ;
}
// helpers
function scale ( num , min , max ) {
return Math . min ( Math . max ( min , num ) , max ) ;
}
function hexDouble ( num ) {
var str = num . toString ( 16 ) . toUpperCase ( ) ;
return ( str . length < 2 ) ? "0" + str : str ;
}
//create a list of reverse color names
var reverseNames = { } ;
for ( var name in colorNames ) {
reverseNames [ colorNames [ name ] ] = name ;
}
} , { "color-name" : 5 } ] , 5 : [ function ( require , module , exports ) {
module . exports = {
"aliceblue" : [ 240 , 248 , 255 ] ,
"antiquewhite" : [ 250 , 235 , 215 ] ,
"aqua" : [ 0 , 255 , 255 ] ,
"aquamarine" : [ 127 , 255 , 212 ] ,
"azure" : [ 240 , 255 , 255 ] ,
"beige" : [ 245 , 245 , 220 ] ,
"bisque" : [ 255 , 228 , 196 ] ,
"black" : [ 0 , 0 , 0 ] ,
"blanchedalmond" : [ 255 , 235 , 205 ] ,
"blue" : [ 0 , 0 , 255 ] ,
"blueviolet" : [ 138 , 43 , 226 ] ,
"brown" : [ 165 , 42 , 42 ] ,
"burlywood" : [ 222 , 184 , 135 ] ,
"cadetblue" : [ 95 , 158 , 160 ] ,
"chartreuse" : [ 127 , 255 , 0 ] ,
"chocolate" : [ 210 , 105 , 30 ] ,
"coral" : [ 255 , 127 , 80 ] ,
"cornflowerblue" : [ 100 , 149 , 237 ] ,
"cornsilk" : [ 255 , 248 , 220 ] ,
"crimson" : [ 220 , 20 , 60 ] ,
"cyan" : [ 0 , 255 , 255 ] ,
"darkblue" : [ 0 , 0 , 139 ] ,
"darkcyan" : [ 0 , 139 , 139 ] ,
"darkgoldenrod" : [ 184 , 134 , 11 ] ,
"darkgray" : [ 169 , 169 , 169 ] ,
"darkgreen" : [ 0 , 100 , 0 ] ,
"darkgrey" : [ 169 , 169 , 169 ] ,
"darkkhaki" : [ 189 , 183 , 107 ] ,
"darkmagenta" : [ 139 , 0 , 139 ] ,
"darkolivegreen" : [ 85 , 107 , 47 ] ,
"darkorange" : [ 255 , 140 , 0 ] ,
"darkorchid" : [ 153 , 50 , 204 ] ,
"darkred" : [ 139 , 0 , 0 ] ,
"darksalmon" : [ 233 , 150 , 122 ] ,
"darkseagreen" : [ 143 , 188 , 143 ] ,
"darkslateblue" : [ 72 , 61 , 139 ] ,
"darkslategray" : [ 47 , 79 , 79 ] ,
"darkslategrey" : [ 47 , 79 , 79 ] ,
"darkturquoise" : [ 0 , 206 , 209 ] ,
"darkviolet" : [ 148 , 0 , 211 ] ,
"deeppink" : [ 255 , 20 , 147 ] ,
"deepskyblue" : [ 0 , 191 , 255 ] ,
"dimgray" : [ 105 , 105 , 105 ] ,
"dimgrey" : [ 105 , 105 , 105 ] ,
"dodgerblue" : [ 30 , 144 , 255 ] ,
"firebrick" : [ 178 , 34 , 34 ] ,
"floralwhite" : [ 255 , 250 , 240 ] ,
"forestgreen" : [ 34 , 139 , 34 ] ,
"fuchsia" : [ 255 , 0 , 255 ] ,
"gainsboro" : [ 220 , 220 , 220 ] ,
"ghostwhite" : [ 248 , 248 , 255 ] ,
"gold" : [ 255 , 215 , 0 ] ,
"goldenrod" : [ 218 , 165 , 32 ] ,
"gray" : [ 128 , 128 , 128 ] ,
"green" : [ 0 , 128 , 0 ] ,
"greenyellow" : [ 173 , 255 , 47 ] ,
"grey" : [ 128 , 128 , 128 ] ,
"honeydew" : [ 240 , 255 , 240 ] ,
"hotpink" : [ 255 , 105 , 180 ] ,
"indianred" : [ 205 , 92 , 92 ] ,
"indigo" : [ 75 , 0 , 130 ] ,
"ivory" : [ 255 , 255 , 240 ] ,
"khaki" : [ 240 , 230 , 140 ] ,
"lavender" : [ 230 , 230 , 250 ] ,
"lavenderblush" : [ 255 , 240 , 245 ] ,
"lawngreen" : [ 124 , 252 , 0 ] ,
"lemonchiffon" : [ 255 , 250 , 205 ] ,
"lightblue" : [ 173 , 216 , 230 ] ,
"lightcoral" : [ 240 , 128 , 128 ] ,
"lightcyan" : [ 224 , 255 , 255 ] ,
"lightgoldenrodyellow" : [ 250 , 250 , 210 ] ,
"lightgray" : [ 211 , 211 , 211 ] ,
"lightgreen" : [ 144 , 238 , 144 ] ,
"lightgrey" : [ 211 , 211 , 211 ] ,
"lightpink" : [ 255 , 182 , 193 ] ,
"lightsalmon" : [ 255 , 160 , 122 ] ,
"lightseagreen" : [ 32 , 178 , 170 ] ,
"lightskyblue" : [ 135 , 206 , 250 ] ,
"lightslategray" : [ 119 , 136 , 153 ] ,
"lightslategrey" : [ 119 , 136 , 153 ] ,
"lightsteelblue" : [ 176 , 196 , 222 ] ,
"lightyellow" : [ 255 , 255 , 224 ] ,
"lime" : [ 0 , 255 , 0 ] ,
"limegreen" : [ 50 , 205 , 50 ] ,
"linen" : [ 250 , 240 , 230 ] ,
"magenta" : [ 255 , 0 , 255 ] ,
"maroon" : [ 128 , 0 , 0 ] ,
"mediumaquamarine" : [ 102 , 205 , 170 ] ,
"mediumblue" : [ 0 , 0 , 205 ] ,
"mediumorchid" : [ 186 , 85 , 211 ] ,
"mediumpurple" : [ 147 , 112 , 219 ] ,
"mediumseagreen" : [ 60 , 179 , 113 ] ,
"mediumslateblue" : [ 123 , 104 , 238 ] ,
"mediumspringgreen" : [ 0 , 250 , 154 ] ,
"mediumturquoise" : [ 72 , 209 , 204 ] ,
"mediumvioletred" : [ 199 , 21 , 133 ] ,
"midnightblue" : [ 25 , 25 , 112 ] ,
"mintcream" : [ 245 , 255 , 250 ] ,
"mistyrose" : [ 255 , 228 , 225 ] ,
"moccasin" : [ 255 , 228 , 181 ] ,
"navajowhite" : [ 255 , 222 , 173 ] ,
"navy" : [ 0 , 0 , 128 ] ,
"oldlace" : [ 253 , 245 , 230 ] ,
"olive" : [ 128 , 128 , 0 ] ,
"olivedrab" : [ 107 , 142 , 35 ] ,
"orange" : [ 255 , 165 , 0 ] ,
"orangered" : [ 255 , 69 , 0 ] ,
"orchid" : [ 218 , 112 , 214 ] ,
"palegoldenrod" : [ 238 , 232 , 170 ] ,
"palegreen" : [ 152 , 251 , 152 ] ,
"paleturquoise" : [ 175 , 238 , 238 ] ,
"palevioletred" : [ 219 , 112 , 147 ] ,
"papayawhip" : [ 255 , 239 , 213 ] ,
"peachpuff" : [ 255 , 218 , 185 ] ,
"peru" : [ 205 , 133 , 63 ] ,
"pink" : [ 255 , 192 , 203 ] ,
"plum" : [ 221 , 160 , 221 ] ,
"powderblue" : [ 176 , 224 , 230 ] ,
"purple" : [ 128 , 0 , 128 ] ,
"rebeccapurple" : [ 102 , 51 , 153 ] ,
"red" : [ 255 , 0 , 0 ] ,
"rosybrown" : [ 188 , 143 , 143 ] ,
"royalblue" : [ 65 , 105 , 225 ] ,
"saddlebrown" : [ 139 , 69 , 19 ] ,
"salmon" : [ 250 , 128 , 114 ] ,
"sandybrown" : [ 244 , 164 , 96 ] ,
"seagreen" : [ 46 , 139 , 87 ] ,
"seashell" : [ 255 , 245 , 238 ] ,
"sienna" : [ 160 , 82 , 45 ] ,
"silver" : [ 192 , 192 , 192 ] ,
"skyblue" : [ 135 , 206 , 235 ] ,
"slateblue" : [ 106 , 90 , 205 ] ,
"slategray" : [ 112 , 128 , 144 ] ,
"slategrey" : [ 112 , 128 , 144 ] ,
"snow" : [ 255 , 250 , 250 ] ,
"springgreen" : [ 0 , 255 , 127 ] ,
"steelblue" : [ 70 , 130 , 180 ] ,
"tan" : [ 210 , 180 , 140 ] ,
"teal" : [ 0 , 128 , 128 ] ,
"thistle" : [ 216 , 191 , 216 ] ,
"tomato" : [ 255 , 99 , 71 ] ,
"turquoise" : [ 64 , 224 , 208 ] ,
"violet" : [ 238 , 130 , 238 ] ,
"wheat" : [ 245 , 222 , 179 ] ,
"white" : [ 255 , 255 , 255 ] ,
"whitesmoke" : [ 245 , 245 , 245 ] ,
"yellow" : [ 255 , 255 , 0 ] ,
"yellowgreen" : [ 154 , 205 , 50 ]
} ;
} , { } ] , 6 : [ function ( require , module , exports ) {
/* MIT license */
var convert = require ( "color-convert" ) ,
string = require ( "color-string" ) ;
var Color = function ( obj ) {
if ( obj instanceof Color ) return obj ;
if ( ! ( this instanceof Color ) ) return new Color ( obj ) ;
this . values = {
rgb : [ 0 , 0 , 0 ] ,
hsl : [ 0 , 0 , 0 ] ,
hsv : [ 0 , 0 , 0 ] ,
hwb : [ 0 , 0 , 0 ] ,
cmyk : [ 0 , 0 , 0 , 0 ] ,
alpha : 1
}
// parse Color() argument
if ( typeof obj == "string" ) {
var vals = string . getRgba ( obj ) ;
if ( vals ) {
this . setValues ( "rgb" , vals ) ;
} else if ( vals = string . getHsla ( obj ) ) {
this . setValues ( "hsl" , vals ) ;
} else if ( vals = string . getHwb ( obj ) ) {
this . setValues ( "hwb" , vals ) ;
} else {
throw new Error ( "Unable to parse color from string \"" + obj + "\"" ) ;
}
} else if ( typeof obj == "object" ) {
var vals = obj ;
if ( vals [ "r" ] !== undefined || vals [ "red" ] !== undefined ) {
this . setValues ( "rgb" , vals )
} else if ( vals [ "l" ] !== undefined || vals [ "lightness" ] !== undefined ) {
this . setValues ( "hsl" , vals )
} else if ( vals [ "v" ] !== undefined || vals [ "value" ] !== undefined ) {
this . setValues ( "hsv" , vals )
} else if ( vals [ "w" ] !== undefined || vals [ "whiteness" ] !== undefined ) {
this . setValues ( "hwb" , vals )
} else if ( vals [ "c" ] !== undefined || vals [ "cyan" ] !== undefined ) {
this . setValues ( "cmyk" , vals )
} else {
throw new Error ( "Unable to parse color from object " + JSON . stringify ( obj ) ) ;
}
}
}
Color . prototype = {
rgb : function ( vals ) {
return this . setSpace ( "rgb" , arguments ) ;
} ,
hsl : function ( vals ) {
return this . setSpace ( "hsl" , arguments ) ;
} ,
hsv : function ( vals ) {
return this . setSpace ( "hsv" , arguments ) ;
} ,
hwb : function ( vals ) {
return this . setSpace ( "hwb" , arguments ) ;
} ,
cmyk : function ( vals ) {
return this . setSpace ( "cmyk" , arguments ) ;
} ,
rgbArray : function ( ) {
return this . values . rgb ;
} ,
hslArray : function ( ) {
return this . values . hsl ;
} ,
hsvArray : function ( ) {
return this . values . hsv ;
} ,
hwbArray : function ( ) {
if ( this . values . alpha !== 1 ) {
return this . values . hwb . concat ( [ this . values . alpha ] )
}
return this . values . hwb ;
} ,
cmykArray : function ( ) {
return this . values . cmyk ;
} ,
rgbaArray : function ( ) {
var rgb = this . values . rgb ;
return rgb . concat ( [ this . values . alpha ] ) ;
} ,
hslaArray : function ( ) {
var hsl = this . values . hsl ;
return hsl . concat ( [ this . values . alpha ] ) ;
} ,
alpha : function ( val ) {
if ( val === undefined ) {
return this . values . alpha ;
}
this . setValues ( "alpha" , val ) ;
return this ;
} ,
red : function ( val ) {
return this . setChannel ( "rgb" , 0 , val ) ;
} ,
green : function ( val ) {
return this . setChannel ( "rgb" , 1 , val ) ;
} ,
blue : function ( val ) {
return this . setChannel ( "rgb" , 2 , val ) ;
} ,
hue : function ( val ) {
return this . setChannel ( "hsl" , 0 , val ) ;
} ,
saturation : function ( val ) {
return this . setChannel ( "hsl" , 1 , val ) ;
} ,
lightness : function ( val ) {
return this . setChannel ( "hsl" , 2 , val ) ;
} ,
saturationv : function ( val ) {
return this . setChannel ( "hsv" , 1 , val ) ;
} ,
whiteness : function ( val ) {
return this . setChannel ( "hwb" , 1 , val ) ;
} ,
blackness : function ( val ) {
return this . setChannel ( "hwb" , 2 , val ) ;
} ,
value : function ( val ) {
return this . setChannel ( "hsv" , 2 , val ) ;
} ,
cyan : function ( val ) {
return this . setChannel ( "cmyk" , 0 , val ) ;
} ,
magenta : function ( val ) {
return this . setChannel ( "cmyk" , 1 , val ) ;
} ,
yellow : function ( val ) {
return this . setChannel ( "cmyk" , 2 , val ) ;
} ,
black : function ( val ) {
return this . setChannel ( "cmyk" , 3 , val ) ;
} ,
hexString : function ( ) {
return string . hexString ( this . values . rgb ) ;
} ,
rgbString : function ( ) {
return string . rgbString ( this . values . rgb , this . values . alpha ) ;
} ,
rgbaString : function ( ) {
return string . rgbaString ( this . values . rgb , this . values . alpha ) ;
} ,
percentString : function ( ) {
return string . percentString ( this . values . rgb , this . values . alpha ) ;
} ,
hslString : function ( ) {
return string . hslString ( this . values . hsl , this . values . alpha ) ;
} ,
hslaString : function ( ) {
return string . hslaString ( this . values . hsl , this . values . alpha ) ;
} ,
hwbString : function ( ) {
return string . hwbString ( this . values . hwb , this . values . alpha ) ;
} ,
keyword : function ( ) {
return string . keyword ( this . values . rgb , this . values . alpha ) ;
} ,
rgbNumber : function ( ) {
return ( this . values . rgb [ 0 ] << 16 ) | ( this . values . rgb [ 1 ] << 8 ) | this . values . rgb [ 2 ] ;
} ,
luminosity : function ( ) {
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
var rgb = this . values . rgb ;
var lum = [ ] ;
for ( var i = 0 ; i < rgb . length ; i ++ ) {
var chan = rgb [ i ] / 255 ;
lum [ i ] = ( chan <= 0.03928 ) ? chan / 12.92 : Math . pow ( ( ( chan + 0.055 ) / 1.055 ) , 2.4 )
}
return 0.2126 * lum [ 0 ] + 0.7152 * lum [ 1 ] + 0.0722 * lum [ 2 ] ;
} ,
contrast : function ( color2 ) {
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
var lum1 = this . luminosity ( ) ;
var lum2 = color2 . luminosity ( ) ;
if ( lum1 > lum2 ) {
return ( lum1 + 0.05 ) / ( lum2 + 0.05 )
} ;
return ( lum2 + 0.05 ) / ( lum1 + 0.05 ) ;
} ,
level : function ( color2 ) {
var contrastRatio = this . contrast ( color2 ) ;
return ( contrastRatio >= 7.1 ) ? 'AAA' : ( contrastRatio >= 4.5 ) ? 'AA' : '' ;
} ,
dark : function ( ) {
// YIQ equation from http://24ways.org/2010/calculating-color-contrast
var rgb = this . values . rgb ,
yiq = ( rgb [ 0 ] * 299 + rgb [ 1 ] * 587 + rgb [ 2 ] * 114 ) / 1000 ;
return yiq < 128 ;
} ,
light : function ( ) {
return ! this . dark ( ) ;
} ,
negate : function ( ) {
var rgb = [ ]
for ( var i = 0 ; i < 3 ; i ++ ) {
rgb [ i ] = 255 - this . values . rgb [ i ] ;
}
this . setValues ( "rgb" , rgb ) ;
return this ;
} ,
lighten : function ( ratio ) {
this . values . hsl [ 2 ] += this . values . hsl [ 2 ] * ratio ;
this . setValues ( "hsl" , this . values . hsl ) ;
return this ;
} ,
darken : function ( ratio ) {
this . values . hsl [ 2 ] -= this . values . hsl [ 2 ] * ratio ;
this . setValues ( "hsl" , this . values . hsl ) ;
return this ;
} ,
saturate : function ( ratio ) {
this . values . hsl [ 1 ] += this . values . hsl [ 1 ] * ratio ;
this . setValues ( "hsl" , this . values . hsl ) ;
return this ;
} ,
desaturate : function ( ratio ) {
this . values . hsl [ 1 ] -= this . values . hsl [ 1 ] * ratio ;
this . setValues ( "hsl" , this . values . hsl ) ;
return this ;
} ,
whiten : function ( ratio ) {
this . values . hwb [ 1 ] += this . values . hwb [ 1 ] * ratio ;
this . setValues ( "hwb" , this . values . hwb ) ;
return this ;
} ,
blacken : function ( ratio ) {
this . values . hwb [ 2 ] += this . values . hwb [ 2 ] * ratio ;
this . setValues ( "hwb" , this . values . hwb ) ;
return this ;
} ,
greyscale : function ( ) {
var rgb = this . values . rgb ;
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
var val = rgb [ 0 ] * 0.3 + rgb [ 1 ] * 0.59 + rgb [ 2 ] * 0.11 ;
this . setValues ( "rgb" , [ val , val , val ] ) ;
return this ;
} ,
clearer : function ( ratio ) {
this . setValues ( "alpha" , this . values . alpha - ( this . values . alpha * ratio ) ) ;
return this ;
} ,
opaquer : function ( ratio ) {
this . setValues ( "alpha" , this . values . alpha + ( this . values . alpha * ratio ) ) ;
return this ;
} ,
rotate : function ( degrees ) {
var hue = this . values . hsl [ 0 ] ;
hue = ( hue + degrees ) % 360 ;
hue = hue < 0 ? 360 + hue : hue ;
this . values . hsl [ 0 ] = hue ;
this . setValues ( "hsl" , this . values . hsl ) ;
return this ;
} ,
mix : function ( color2 , weight ) {
weight = 1 - ( weight == null ? 0.5 : weight ) ;
// algorithm from Sass's mix(). Ratio of first color in mix is
// determined by the alphas of both colors and the weight
var t1 = weight * 2 - 1 ,
d = this . alpha ( ) - color2 . alpha ( ) ;
var weight1 = ( ( ( t1 * d == - 1 ) ? t1 : ( t1 + d ) / ( 1 + t1 * d ) ) + 1 ) / 2 ;
var weight2 = 1 - weight1 ;
2016-09-12 16:04:53 +00:00
var rgb = this . rgb [ ] ;
var rgb2 = color2 . rgb [ ] ;
2016-05-24 07:01:16 +00:00
for ( var i = 0 ; i < rgb . length ; i ++ ) {
rgb [ i ] = rgb [ i ] * weight1 + rgb2 [ i ] * weight2 ;
}
this . setValues ( "rgb" , rgb ) ;
var alpha = this . alpha ( ) * weight + color2 . alpha ( ) * ( 1 - weight ) ;
this . setValues ( "alpha" , alpha ) ;
return this ;
} ,
toJSON : function ( ) {
return this . rgb ( ) ;
} ,
clone : function ( ) {
return new Color ( this . rgb ( ) ) ;
}
}
Color . prototype . getValues = function ( space ) {
var vals = { } ;
for ( var i = 0 ; i < space . length ; i ++ ) {
vals [ space . charAt ( i ) ] = this . values [ space ] [ i ] ;
}
if ( this . values . alpha != 1 ) {
vals [ "a" ] = this . values . alpha ;
}
// {r: 255, g: 255, b: 255, a: 0.4}
return vals ;
}
Color . prototype . setValues = function ( space , vals ) {
var spaces = {
"rgb" : [ "red" , "green" , "blue" ] ,
"hsl" : [ "hue" , "saturation" , "lightness" ] ,
"hsv" : [ "hue" , "saturation" , "value" ] ,
"hwb" : [ "hue" , "whiteness" , "blackness" ] ,
"cmyk" : [ "cyan" , "magenta" , "yellow" , "black" ]
} ;
var maxes = {
"rgb" : [ 255 , 255 , 255 ] ,
"hsl" : [ 360 , 100 , 100 ] ,
"hsv" : [ 360 , 100 , 100 ] ,
"hwb" : [ 360 , 100 , 100 ] ,
"cmyk" : [ 100 , 100 , 100 , 100 ]
} ;
var alpha = 1 ;
if ( space == "alpha" ) {
alpha = vals ;
} else if ( vals . length ) {
// [10, 10, 10]
this . values [ space ] = vals . slice ( 0 , space . length ) ;
alpha = vals [ space . length ] ;
} else if ( vals [ space . charAt ( 0 ) ] !== undefined ) {
// {r: 10, g: 10, b: 10}
for ( var i = 0 ; i < space . length ; i ++ ) {
this . values [ space ] [ i ] = vals [ space . charAt ( i ) ] ;
}
alpha = vals . a ;
} else if ( vals [ spaces [ space ] [ 0 ] ] !== undefined ) {
// {red: 10, green: 10, blue: 10}
var chans = spaces [ space ] ;
for ( var i = 0 ; i < space . length ; i ++ ) {
this . values [ space ] [ i ] = vals [ chans [ i ] ] ;
}
alpha = vals . alpha ;
}
this . values . alpha = Math . max ( 0 , Math . min ( 1 , ( alpha !== undefined ? alpha : this . values . alpha ) ) ) ;
if ( space == "alpha" ) {
return ;
}
// cap values of the space prior converting all values
for ( var i = 0 ; i < space . length ; i ++ ) {
var capped = Math . max ( 0 , Math . min ( maxes [ space ] [ i ] , this . values [ space ] [ i ] ) ) ;
this . values [ space ] [ i ] = Math . round ( capped ) ;
}
// convert to all the other color spaces
for ( var sname in spaces ) {
if ( sname != space ) {
this . values [ sname ] = convert [ space ] [ sname ] ( this . values [ space ] )
}
// cap values
for ( var i = 0 ; i < sname . length ; i ++ ) {
var capped = Math . max ( 0 , Math . min ( maxes [ sname ] [ i ] , this . values [ sname ] [ i ] ) ) ;
this . values [ sname ] [ i ] = Math . round ( capped ) ;
}
}
return true ;
}
Color . prototype . setSpace = function ( space , args ) {
var vals = args [ 0 ] ;
if ( vals === undefined ) {
// color.rgb()
return this . getValues ( space ) ;
}
// color.rgb(10, 10, 10)
if ( typeof vals == "number" ) {
vals = Array . prototype . slice . call ( args ) ;
}
this . setValues ( space , vals ) ;
return this ;
}
Color . prototype . setChannel = function ( space , index , val ) {
if ( val === undefined ) {
// color.red()
return this . values [ space ] [ index ] ;
}
// color.red(100)
this . values [ space ] [ index ] = val ;
this . setValues ( space , this . values [ space ] ) ;
return this ;
}
window . Color = module . exports = Color
} , { "color-convert" : 3 , "color-string" : 4 } ] , 7 : [ function ( require , module , exports ) {
/ * !
* Chart . js
* http : //chartjs.org/
* Version : 2.0 . 2
*
* Copyright 2015 Nick Downie
* Released under the MIT license
* https : //github.com/nnnick/Chart.js/blob/master/LICENSE.md
* /
var Chart = require ( './core/core.js' ) ( ) ;
require ( './core/core.helpers' ) ( Chart ) ;
require ( './core/core.element' ) ( Chart ) ;
require ( './core/core.animation' ) ( Chart ) ;
require ( './core/core.controller' ) ( Chart ) ;
require ( './core/core.datasetController' ) ( Chart ) ;
require ( './core/core.layoutService' ) ( Chart ) ;
require ( './core/core.legend' ) ( Chart ) ;
require ( './core/core.scale' ) ( Chart ) ;
require ( './core/core.scaleService' ) ( Chart ) ;
require ( './core/core.title' ) ( Chart ) ;
require ( './core/core.tooltip' ) ( Chart ) ;
require ( './controllers/controller.bar' ) ( Chart ) ;
require ( './controllers/controller.bubble' ) ( Chart ) ;
require ( './controllers/controller.doughnut' ) ( Chart ) ;
require ( './controllers/controller.line' ) ( Chart ) ;
require ( './controllers/controller.polarArea' ) ( Chart ) ;
require ( './controllers/controller.radar' ) ( Chart ) ;
require ( './scales/scale.category' ) ( Chart ) ;
require ( './scales/scale.linear' ) ( Chart ) ;
require ( './scales/scale.logarithmic' ) ( Chart ) ;
require ( './scales/scale.radialLinear' ) ( Chart ) ;
require ( './scales/scale.time' ) ( Chart ) ;
require ( './elements/element.arc' ) ( Chart ) ;
require ( './elements/element.line' ) ( Chart ) ;
require ( './elements/element.point' ) ( Chart ) ;
require ( './elements/element.rectangle' ) ( Chart ) ;
require ( './charts/Chart.Bar' ) ( Chart ) ;
require ( './charts/Chart.Bubble' ) ( Chart ) ;
require ( './charts/Chart.Doughnut' ) ( Chart ) ;
require ( './charts/Chart.Line' ) ( Chart ) ;
require ( './charts/Chart.PolarArea' ) ( Chart ) ;
require ( './charts/Chart.Radar' ) ( Chart ) ;
require ( './charts/Chart.Scatter' ) ( Chart ) ;
window . Chart = module . exports = Chart ;
} , { "./charts/Chart.Bar" : 8 , "./charts/Chart.Bubble" : 9 , "./charts/Chart.Doughnut" : 10 , "./charts/Chart.Line" : 11 , "./charts/Chart.PolarArea" : 12 , "./charts/Chart.Radar" : 13 , "./charts/Chart.Scatter" : 14 , "./controllers/controller.bar" : 15 , "./controllers/controller.bubble" : 16 , "./controllers/controller.doughnut" : 17 , "./controllers/controller.line" : 18 , "./controllers/controller.polarArea" : 19 , "./controllers/controller.radar" : 20 , "./core/core.animation" : 21 , "./core/core.controller" : 22 , "./core/core.datasetController" : 23 , "./core/core.element" : 24 , "./core/core.helpers" : 25 , "./core/core.js" : 26 , "./core/core.layoutService" : 27 , "./core/core.legend" : 28 , "./core/core.scale" : 29 , "./core/core.scaleService" : 30 , "./core/core.title" : 31 , "./core/core.tooltip" : 32 , "./elements/element.arc" : 33 , "./elements/element.line" : 34 , "./elements/element.point" : 35 , "./elements/element.rectangle" : 36 , "./scales/scale.category" : 37 , "./scales/scale.linear" : 38 , "./scales/scale.logarithmic" : 39 , "./scales/scale.radialLinear" : 40 , "./scales/scale.time" : 41 } ] , 8 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
Chart . Bar = function ( context , config ) {
config . type = 'bar' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 9 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
Chart . Bubble = function ( context , config ) {
config . type = 'bubble' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 10 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
Chart . Doughnut = function ( context , config ) {
config . type = 'doughnut' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 11 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
Chart . Line = function ( context , config ) {
config . type = 'line' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 12 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
Chart . PolarArea = function ( context , config ) {
config . type = 'polarArea' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 13 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
var defaultConfig = {
aspectRatio : 1
} ;
Chart . Radar = function ( context , config ) {
config . options = helpers . configMerge ( defaultConfig , config . options ) ;
config . type = 'radar' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 14 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var defaultConfig = {
hover : {
mode : 'single'
} ,
scales : {
xAxes : [ {
type : "linear" , // scatter should not use a category axis
position : "bottom" ,
id : "x-axis-1" // need an ID so datasets can reference the scale
} ] ,
yAxes : [ {
type : "linear" ,
position : "left" ,
id : "y-axis-1"
} ]
} ,
tooltips : {
callbacks : {
title : function ( tooltipItems , data ) {
// Title doesn't make sense for scatter since we format the data as a point
return '' ;
} ,
label : function ( tooltipItem , data ) {
return '(' + tooltipItem . xLabel + ', ' + tooltipItem . yLabel + ')' ;
}
}
}
} ;
// Register the default config for this type
Chart . defaults . scatter = defaultConfig ;
// Scatter charts use line controllers
Chart . controllers . scatter = Chart . controllers . line ;
Chart . Scatter = function ( context , config ) {
config . type = 'scatter' ;
return new Chart ( context , config ) ;
} ;
} ;
} , { } ] , 15 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . bar = {
hover : {
mode : "label"
} ,
scales : {
xAxes : [ {
type : "category" ,
// Specific to Bar Controller
categoryPercentage : 0.8 ,
barPercentage : 0.9 ,
// grid line settings
gridLines : {
offsetGridLines : true
}
} ] ,
yAxes : [ {
type : "linear"
} ]
}
} ;
Chart . controllers . bar = Chart . DatasetController . extend ( {
initialize : function ( chart , datasetIndex ) {
Chart . DatasetController . prototype . initialize . call ( this , chart , datasetIndex ) ;
// Use this to indicate that this is a bar dataset.
this . getDataset ( ) . bar = true ;
} ,
// Get the number of datasets that display bars. We use this to correctly calculate the bar width
getBarCount : function getBarCount ( ) {
var barCount = 0 ;
helpers . each ( this . chart . data . datasets , function ( dataset ) {
if ( helpers . isDatasetVisible ( dataset ) && dataset . bar ) {
++ barCount ;
}
} ) ;
return barCount ;
} ,
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 . Rectangle ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
} , this ) ;
} ,
addElementAndReset : function ( index ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
var rectangle = new Chart . elements . Rectangle ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
var numBars = this . getBarCount ( ) ;
this . updateElement ( rectangle , index , true , numBars ) ;
this . getDataset ( ) . metaData . splice ( index , 0 , rectangle ) ;
} ,
update : function update ( reset ) {
var numBars = this . getBarCount ( ) ;
helpers . each ( this . getDataset ( ) . metaData , function ( rectangle , index ) {
this . updateElement ( rectangle , index , reset , numBars ) ;
} , this ) ;
} ,
updateElement : function updateElement ( rectangle , index , reset , numBars ) {
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var yScalePoint ;
if ( yScale . min < 0 && yScale . max < 0 ) {
// all less than 0. use the top
yScalePoint = yScale . getPixelForValue ( yScale . max ) ;
} else if ( yScale . min > 0 && yScale . max > 0 ) {
yScalePoint = yScale . getPixelForValue ( yScale . min ) ;
} else {
yScalePoint = yScale . getPixelForValue ( 0 ) ;
}
helpers . extend ( rectangle , {
// Utility
_chart : this . chart . chart ,
_xScale : xScale ,
_yScale : yScale ,
_datasetIndex : this . index ,
_index : index ,
// Desired view properties
_model : {
x : this . calculateBarX ( index , this . index ) ,
y : reset ? yScalePoint : this . calculateBarY ( index , this . index ) ,
// Tooltip
label : this . chart . data . labels [ index ] ,
datasetLabel : this . getDataset ( ) . label ,
// Appearance
base : reset ? yScalePoint : this . calculateBarBase ( this . index , index ) ,
width : this . calculateBarWidth ( numBars ) ,
backgroundColor : rectangle . custom && rectangle . custom . backgroundColor ? rectangle . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . rectangle . backgroundColor ) ,
borderSkipped : rectangle . custom && rectangle . custom . borderSkipped ? rectangle . custom . borderSkipped : this . chart . options . elements . rectangle . borderSkipped ,
borderColor : rectangle . custom && rectangle . custom . borderColor ? rectangle . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderColor , index , this . chart . options . elements . rectangle . borderColor ) ,
borderWidth : rectangle . custom && rectangle . custom . borderWidth ? rectangle . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderWidth , index , this . chart . options . elements . rectangle . borderWidth )
}
} ) ;
rectangle . pivot ( ) ;
} ,
calculateBarBase : function ( datasetIndex , index ) {
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var base = 0 ;
if ( yScale . options . stacked ) {
var value = this . chart . data . datasets [ datasetIndex ] . data [ index ] ;
if ( value < 0 ) {
for ( var i = 0 ; i < datasetIndex ; i ++ ) {
var negDS = this . chart . data . datasets [ i ] ;
if ( helpers . isDatasetVisible ( negDS ) && negDS . yAxisID === yScale . id && negDS . bar ) {
base += negDS . data [ index ] < 0 ? negDS . data [ index ] : 0 ;
}
}
} else {
for ( var j = 0 ; j < datasetIndex ; j ++ ) {
var posDS = this . chart . data . datasets [ j ] ;
if ( helpers . isDatasetVisible ( posDS ) && posDS . yAxisID === yScale . id && posDS . bar ) {
base += posDS . data [ index ] > 0 ? posDS . data [ index ] : 0 ;
}
}
}
return yScale . getPixelForValue ( base ) ;
}
base = yScale . getPixelForValue ( yScale . min ) ;
if ( yScale . beginAtZero || ( ( yScale . min <= 0 && yScale . max >= 0 ) || ( yScale . min >= 0 && yScale . max <= 0 ) ) ) {
base = yScale . getPixelForValue ( 0 , 0 ) ;
//base += yScale.options.gridLines.lineWidth;
} else if ( yScale . min < 0 && yScale . max < 0 ) {
// All values are negative. Use the top as the base
base = yScale . getPixelForValue ( yScale . max ) ;
}
return base ;
} ,
getRuler : function ( ) {
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var datasetCount = this . getBarCount ( ) ;
var tickWidth = ( function ( ) {
var min = xScale . getPixelForTick ( 1 ) - xScale . getPixelForTick ( 0 ) ;
for ( var i = 2 ; i < this . getDataset ( ) . data . length ; i ++ ) {
min = Math . min ( xScale . getPixelForTick ( i ) - xScale . getPixelForTick ( i - 1 ) , min ) ;
}
return min ;
} ) . call ( this ) ;
var categoryWidth = tickWidth * xScale . options . categoryPercentage ;
var categorySpacing = ( tickWidth - ( tickWidth * xScale . options . categoryPercentage ) ) / 2 ;
var fullBarWidth = categoryWidth / datasetCount ;
var barWidth = fullBarWidth * xScale . options . barPercentage ;
var barSpacing = fullBarWidth - ( fullBarWidth * xScale . options . barPercentage ) ;
return {
datasetCount : datasetCount ,
tickWidth : tickWidth ,
categoryWidth : categoryWidth ,
categorySpacing : categorySpacing ,
fullBarWidth : fullBarWidth ,
barWidth : barWidth ,
barSpacing : barSpacing
} ;
} ,
calculateBarWidth : function ( ) {
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var ruler = this . getRuler ( ) ;
return xScale . options . stacked ? ruler . categoryWidth : ruler . barWidth ;
} ,
// Get bar index from the given dataset index accounting for the fact that not all bars are visible
getBarIndex : function ( datasetIndex ) {
var barIndex = 0 ;
for ( var j = 0 ; j < datasetIndex ; ++ j ) {
if ( helpers . isDatasetVisible ( this . chart . data . datasets [ j ] ) && this . chart . data . datasets [ j ] . bar ) {
++ barIndex ;
}
}
return barIndex ;
} ,
calculateBarX : function ( index , datasetIndex ) {
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var barIndex = this . getBarIndex ( datasetIndex ) ;
var ruler = this . getRuler ( ) ;
var leftTick = xScale . getPixelForValue ( null , index , datasetIndex , this . chart . isCombo ) ;
leftTick -= this . chart . isCombo ? ( ruler . tickWidth / 2 ) : 0 ;
if ( xScale . options . stacked ) {
return leftTick + ( ruler . categoryWidth / 2 ) + ruler . categorySpacing ;
}
return leftTick +
( ruler . barWidth / 2 ) +
ruler . categorySpacing +
( ruler . barWidth * barIndex ) +
( ruler . barSpacing / 2 ) +
( ruler . barSpacing * barIndex ) ;
} ,
calculateBarY : function ( index , datasetIndex ) {
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var value = this . getDataset ( ) . data [ index ] ;
if ( yScale . options . stacked ) {
var sumPos = 0 ,
sumNeg = 0 ;
for ( var i = 0 ; i < datasetIndex ; i ++ ) {
var ds = this . chart . data . datasets [ i ] ;
if ( helpers . isDatasetVisible ( ds ) && ds . bar && ds . yAxisID === yScale . id ) {
if ( ds . data [ index ] < 0 ) {
sumNeg += ds . data [ index ] || 0 ;
} else {
sumPos += ds . data [ index ] || 0 ;
}
}
}
if ( value < 0 ) {
return yScale . getPixelForValue ( sumNeg + value ) ;
} else {
return yScale . getPixelForValue ( sumPos + value ) ;
}
return yScale . getPixelForValue ( value ) ;
}
return yScale . getPixelForValue ( value ) ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
helpers . each ( this . getDataset ( ) . metaData , function ( rectangle , index ) {
var d = this . getDataset ( ) . data [ index ] ;
if ( d !== null && d !== undefined && ! isNaN ( d ) ) {
rectangle . 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 . hoverBorderWidth , index , rectangle . _model . borderWidth ) ;
} ,
removeHoverStyle : function ( rectangle ) {
var dataset = this . chart . data . datasets [ rectangle . _datasetIndex ] ;
var index = rectangle . _index ;
rectangle . _model . backgroundColor = rectangle . custom && rectangle . custom . backgroundColor ? rectangle . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . rectangle . backgroundColor ) ;
rectangle . _model . borderColor = rectangle . custom && rectangle . custom . borderColor ? rectangle . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderColor , index , this . chart . options . elements . rectangle . borderColor ) ;
rectangle . _model . borderWidth = rectangle . custom && rectangle . custom . borderWidth ? rectangle . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderWidth , index , this . chart . options . elements . rectangle . borderWidth ) ;
}
} ) ;
} ;
} , { } ] , 16 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . bubble = {
hover : {
mode : "single"
} ,
scales : {
xAxes : [ {
type : "linear" , // bubble should probably use a linear scale by default
position : "bottom" ,
id : "x-axis-0" // need an ID so datasets can reference the scale
} ] ,
yAxes : [ {
type : "linear" ,
position : "left" ,
id : "y-axis-0"
} ]
} ,
tooltips : {
callbacks : {
title : function ( tooltipItems , data ) {
// Title doesn't make sense for scatter since we format the data as a point
return '' ;
} ,
label : function ( tooltipItem , data ) {
var datasetLabel = data . datasets [ tooltipItem . datasetIndex ] . label || '' ;
var dataPoint = data . datasets [ tooltipItem . datasetIndex ] . data [ tooltipItem . index ] ;
return datasetLabel + ': (' + dataPoint . x + ', ' + dataPoint . y + ', ' + dataPoint . r + ')' ;
}
}
}
} ;
Chart . controllers . bubble = Chart . DatasetController . extend ( {
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 . Point ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
} , this ) ;
} ,
addElementAndReset : function ( index ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
var point = new Chart . elements . Point ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
// Reset the point
this . updateElement ( point , index , true ) ;
// Add to the points array
this . getDataset ( ) . metaData . splice ( index , 0 , point ) ;
} ,
update : function update ( reset ) {
var points = this . getDataset ( ) . metaData ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var scaleBase ;
if ( yScale . min < 0 && yScale . max < 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . max ) ;
} else if ( yScale . min > 0 && yScale . max > 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . min ) ;
} else {
scaleBase = yScale . getPixelForValue ( 0 ) ;
}
// Update Points
helpers . each ( points , function ( point , index ) {
this . updateElement ( point , index , reset ) ;
} , this ) ;
} ,
updateElement : function ( point , index , reset ) {
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var scaleBase ;
if ( yScale . min < 0 && yScale . max < 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . max ) ;
} else if ( yScale . min > 0 && yScale . max > 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . min ) ;
} else {
scaleBase = yScale . getPixelForValue ( 0 ) ;
}
helpers . extend ( point , {
// Utility
_chart : this . chart . chart ,
_xScale : xScale ,
_yScale : yScale ,
_datasetIndex : this . index ,
_index : index ,
// Desired view properties
_model : {
x : reset ? xScale . getPixelForDecimal ( 0.5 ) : xScale . getPixelForValue ( this . getDataset ( ) . data [ index ] , index , this . index , this . chart . isCombo ) ,
y : reset ? scaleBase : yScale . getPixelForValue ( this . getDataset ( ) . data [ index ] , index , this . index ) ,
// Appearance
radius : reset ? 0 : point . custom && point . custom . radius ? point . custom . radius : this . getRadius ( this . getDataset ( ) . data [ index ] ) ,
backgroundColor : point . custom && point . custom . backgroundColor ? point . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . point . backgroundColor ) ,
borderColor : point . custom && point . custom . borderColor ? point . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderColor , index , this . chart . options . elements . point . borderColor ) ,
borderWidth : point . custom && point . custom . borderWidth ? point . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderWidth , index , this . chart . options . elements . point . borderWidth ) ,
// Tooltip
hitRadius : point . custom && point . custom . hitRadius ? point . custom . hitRadius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . hitRadius , index , this . chart . options . elements . point . hitRadius )
}
} ) ;
point . _model . skip = point . custom && point . custom . skip ? point . custom . skip : ( isNaN ( point . _model . x ) || isNaN ( point . _model . y ) ) ;
point . pivot ( ) ;
} ,
getRadius : function ( value ) {
return value . r || this . chart . options . elements . point . radius ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
// Transition and Draw the Points
helpers . each ( this . getDataset ( ) . metaData , function ( point , index ) {
point . transition ( easingDecimal ) ;
point . draw ( ) ;
} ) ;
} ,
setHoverStyle : function ( point ) {
// Point
var dataset = this . chart . data . datasets [ point . _datasetIndex ] ;
var index = point . _index ;
point . _model . radius = point . custom && point . custom . hoverRadius ? point . custom . hoverRadius : ( helpers . getValueAtIndexOrDefault ( dataset . hoverRadius , index , this . chart . options . elements . point . hoverRadius ) ) + this . getRadius ( this . getDataset ( ) . data [ point . _index ] ) ;
point . _model . backgroundColor = point . custom && point . custom . hoverBackgroundColor ? point . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBackgroundColor , index , helpers . color ( point . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
point . _model . borderColor = point . custom && point . custom . hoverBorderColor ? point . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderColor , index , helpers . color ( point . _model . borderColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
point . _model . borderWidth = point . custom && point . custom . hoverBorderWidth ? point . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderWidth , index , point . _model . borderWidth ) ;
} ,
removeHoverStyle : function ( point ) {
var dataset = this . chart . data . datasets [ point . _datasetIndex ] ;
var index = point . _index ;
point . _model . radius = point . custom && point . custom . radius ? point . custom . radius : this . getRadius ( this . getDataset ( ) . data [ point . _index ] ) ;
point . _model . backgroundColor = point . custom && point . custom . backgroundColor ? point . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . point . backgroundColor ) ;
point . _model . borderColor = point . custom && point . custom . borderColor ? point . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderColor , index , this . chart . options . elements . point . borderColor ) ;
point . _model . borderWidth = point . custom && point . custom . borderWidth ? point . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderWidth , index , this . chart . options . elements . point . borderWidth ) ;
}
} ) ;
} ;
} , { } ] , 17 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
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
} ,
aspectRatio : 1 ,
hover : {
mode : 'single'
} ,
legendCallback : function ( chart ) {
var text = [ ] ;
text . push ( '<ul class="' + chart . id + '-legend">' ) ;
if ( chart . data . datasets . length ) {
for ( var i = 0 ; i < chart . data . datasets [ 0 ] . data . length ; ++ i ) {
text . push ( '<li><span style="background-color:' + chart . data . datasets [ 0 ] . backgroundColor [ i ] + '">' ) ;
if ( chart . data . labels [ i ] ) {
text . push ( chart . data . labels [ i ] ) ;
}
text . push ( '</span></li>' ) ;
}
}
text . push ( '</ul>' ) ;
return text . join ( "" ) ;
} ,
legend : {
labels : {
generateLabels : function ( data ) {
if ( data . labels . length && data . datasets . length ) {
return data . labels . map ( function ( label , i ) {
return {
text : label ,
fillStyle : data . datasets [ 0 ] . backgroundColor [ i ] ,
hidden : isNaN ( data . datasets [ 0 ] . data [ i ] ) ,
// Extra data used for toggling the correct item
index : i
} ;
} ) ;
} else {
return [ ] ;
}
}
} ,
onClick : function ( e , legendItem ) {
helpers . each ( this . chart . data . datasets , function ( dataset ) {
dataset . metaHiddenData = dataset . metaHiddenData || [ ] ;
var idx = legendItem . index ;
if ( ! isNaN ( dataset . data [ idx ] ) ) {
dataset . metaHiddenData [ idx ] = dataset . data [ idx ] ;
dataset . data [ idx ] = NaN ;
} else if ( ! isNaN ( dataset . metaHiddenData [ idx ] ) ) {
dataset . data [ idx ] = dataset . metaHiddenData [ idx ] ;
}
} ) ;
this . chart . update ( ) ;
}
} ,
//The percentage of the chart that we cut out of the middle.
cutoutPercentage : 50 ,
//The rotation of the chart, where the first data arc begins.
rotation : Math . PI * - 0.5 ,
//The total circumference of the chart.
circumference : Math . PI * 2.0 ,
// Need to override these to give a nice default
tooltips : {
callbacks : {
title : function ( ) {
return '' ;
} ,
label : function ( tooltipItem , data ) {
return data . labels [ tooltipItem . index ] + ': ' + data . datasets [ tooltipItem . datasetIndex ] . data [ tooltipItem . index ] ;
}
}
}
} ;
Chart . defaults . pie = helpers . clone ( Chart . defaults . doughnut ) ;
helpers . extend ( Chart . defaults . pie , {
cutoutPercentage : 0
} ) ;
Chart . controllers . doughnut = Chart . controllers . pie = Chart . DatasetController . extend ( {
linkScales : function ( ) {
// no scales for doughnut
} ,
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 ) ;
} ,
addElementAndReset : function ( index , colorForNewElement ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
var arc = new Chart . elements . Arc ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
if ( colorForNewElement && helpers . isArray ( this . getDataset ( ) . backgroundColor ) ) {
this . getDataset ( ) . backgroundColor . splice ( index , 0 , colorForNewElement ) ;
}
// Reset the point
this . updateElement ( arc , index , true ) ;
// Add to the points array
this . getDataset ( ) . metaData . splice ( index , 0 , arc ) ;
} ,
getVisibleDatasetCount : function getVisibleDatasetCount ( ) {
return helpers . where ( this . chart . data . datasets , function ( ds ) {
return helpers . isDatasetVisible ( ds ) ;
} ) . length ;
} ,
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
getRingIndex : function getRingIndex ( datasetIndex ) {
var ringIndex = 0 ;
for ( var j = 0 ; j < datasetIndex ; ++ j ) {
if ( helpers . isDatasetVisible ( this . chart . data . datasets [ j ] ) ) {
++ ringIndex ;
}
}
return ringIndex ;
} ,
update : function update ( reset ) {
var availableWidth = this . chart . chartArea . right - this . chart . chartArea . left - this . chart . options . elements . arc . borderWidth ;
var availableHeight = this . chart . chartArea . bottom - this . chart . chartArea . top - this . chart . options . elements . arc . borderWidth ;
var minSize = Math . min ( availableWidth , availableHeight ) ;
var offset = { x : 0 , y : 0 } ;
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
if ( this . chart . options . circumference && this . chart . options . circumference < Math . PI * 2.0 ) {
var startAngle = this . chart . options . rotation % ( Math . PI * 2.0 ) ;
startAngle += Math . PI * 2.0 * ( startAngle >= Math . PI ? - 1 : startAngle < - Math . PI ? 1 : 0 ) ;
var endAngle = startAngle + this . chart . options . circumference ;
var start = { x : Math . cos ( startAngle ) , y : Math . sin ( startAngle ) } ;
var end = { x : Math . cos ( endAngle ) , y : Math . sin ( endAngle ) } ;
var contains0 = ( startAngle <= 0 && 0 <= endAngle ) || ( startAngle <= Math . PI * 2.0 && Math . PI * 2.0 <= endAngle ) ;
var contains90 = ( startAngle <= Math . PI * 0.5 && Math . PI * 0.5 <= endAngle ) || ( startAngle <= Math . PI * 2.5 && Math . PI * 2.5 <= endAngle ) ;
var contains180 = ( startAngle <= - Math . PI && - Math . PI <= endAngle ) || ( startAngle <= Math . PI && Math . PI <= endAngle ) ;
var contains270 = ( startAngle <= - Math . PI * 0.5 && - Math . PI * 0.5 <= endAngle ) || ( startAngle <= Math . PI * 1.5 && Math . PI * 1.5 <= endAngle ) ;
var cutout = this . chart . options . cutoutPercentage / 100.0 ;
var min = { x : contains180 ? - 1 : Math . min ( start . x * ( start . x < 0 ? 1 : cutout ) , end . x * ( end . x < 0 ? 1 : cutout ) ) , y : contains270 ? - 1 : Math . min ( start . y * ( start . y < 0 ? 1 : cutout ) , end . y * ( end . y < 0 ? 1 : cutout ) ) } ;
var max = { x : contains0 ? 1 : Math . max ( start . x * ( start . x > 0 ? 1 : cutout ) , end . x * ( end . x > 0 ? 1 : cutout ) ) , y : contains90 ? 1 : Math . max ( start . y * ( start . y > 0 ? 1 : cutout ) , end . y * ( end . y > 0 ? 1 : cutout ) ) } ;
var size = { width : ( max . x - min . x ) * 0.5 , height : ( max . y - min . y ) * 0.5 } ;
minSize = Math . min ( availableWidth / size . width , availableHeight / size . height ) ;
offset = { x : ( max . x + min . x ) * - 0.5 , y : ( max . y + min . y ) * - 0.5 } ;
}
this . chart . outerRadius = Math . max ( minSize / 2 , 0 ) ;
this . chart . innerRadius = Math . max ( this . chart . options . cutoutPercentage ? ( this . chart . outerRadius / 100 ) * ( this . chart . options . cutoutPercentage ) : 1 , 0 ) ;
this . chart . radiusLength = ( this . chart . outerRadius - this . chart . innerRadius ) / this . getVisibleDatasetCount ( ) ;
this . chart . offsetX = offset . x * this . chart . outerRadius ;
this . chart . offsetY = offset . y * this . chart . outerRadius ;
this . getDataset ( ) . total = 0 ;
helpers . each ( this . getDataset ( ) . data , function ( value ) {
if ( ! isNaN ( value ) ) {
this . getDataset ( ) . total += Math . abs ( value ) ;
}
} , this ) ;
this . outerRadius = this . chart . outerRadius - ( this . chart . radiusLength * this . getRingIndex ( this . index ) ) ;
this . innerRadius = this . outerRadius - this . chart . radiusLength ;
helpers . each ( this . getDataset ( ) . metaData , function ( arc , index ) {
this . updateElement ( arc , index , reset ) ;
} , this ) ;
} ,
updateElement : function ( arc , index , reset ) {
var centerX = ( this . chart . chartArea . left + this . chart . chartArea . right ) / 2 ;
var centerY = ( this . chart . chartArea . top + this . chart . chartArea . bottom ) / 2 ;
var startAngle = this . chart . options . rotation || ( Math . PI * - 0.5 ) ; // non reset case handled later
var endAngle = this . chart . options . rotation || ( Math . PI * - 0.5 ) ; // non reset case handled later
var circumference = reset && this . chart . options . animation . animateRotate ? 0 : this . calculateCircumference ( this . getDataset ( ) . data [ index ] ) * ( ( this . chart . options . circumference || ( 2.0 * Math . PI ) ) / ( 2.0 * Math . PI ) ) ;
var innerRadius = reset && this . chart . options . animation . animateScale ? 0 : this . innerRadius ;
var outerRadius = reset && this . chart . options . animation . animateScale ? 0 : this . outerRadius ;
helpers . extend ( arc , {
// Utility
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index ,
// Desired view properties
_model : {
x : centerX + this . chart . offsetX ,
y : centerY + this . chart . offsetY ,
startAngle : startAngle ,
endAngle : endAngle ,
circumference : circumference ,
outerRadius : outerRadius ,
innerRadius : 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 ] )
}
} ) ;
// Set correct angles if not resetting
if ( ! reset ) {
if ( index === 0 ) {
arc . _model . startAngle = this . chart . options . rotation || ( Math . PI * - 0.5 ) ;
} else {
arc . _model . startAngle = this . getDataset ( ) . metaData [ index - 1 ] . _model . endAngle ;
}
arc . _model . endAngle = arc . _model . startAngle + arc . _model . circumference ;
}
arc . pivot ( ) ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
helpers . each ( this . getDataset ( ) . metaData , function ( arc , index ) {
arc . transition ( easingDecimal ) . draw ( ) ;
} ) ;
} ,
setHoverStyle : function ( arc ) {
var dataset = this . chart . data . datasets [ arc . _datasetIndex ] ;
var index = arc . _index ;
arc . _model . backgroundColor = arc . custom && arc . custom . hoverBackgroundColor ? arc . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBackgroundColor , index , helpers . color ( arc . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
arc . _model . borderColor = arc . custom && arc . custom . hoverBorderColor ? arc . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderColor , index , helpers . color ( arc . _model . borderColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
arc . _model . borderWidth = arc . custom && arc . custom . hoverBorderWidth ? arc . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderWidth , index , arc . _model . borderWidth ) ;
} ,
removeHoverStyle : function ( arc ) {
var dataset = this . chart . data . datasets [ arc . _datasetIndex ] ;
var index = arc . _index ;
arc . _model . backgroundColor = arc . custom && arc . custom . backgroundColor ? arc . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . arc . backgroundColor ) ;
arc . _model . borderColor = arc . custom && arc . custom . borderColor ? arc . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderColor , index , this . chart . options . elements . arc . borderColor ) ;
arc . _model . borderWidth = arc . custom && arc . custom . borderWidth ? arc . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderWidth , index , this . chart . options . elements . arc . borderWidth ) ;
} ,
calculateCircumference : function ( value ) {
if ( this . getDataset ( ) . total > 0 && ! isNaN ( value ) ) {
return ( Math . PI * 1.999999 ) * ( value / this . getDataset ( ) . total ) ;
} else {
return 0 ;
}
}
} ) ;
} ;
} , { } ] , 18 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . line = {
showLines : true ,
hover : {
mode : "label"
} ,
scales : {
xAxes : [ {
type : "category" ,
id : 'x-axis-0'
} ] ,
yAxes : [ {
type : "linear" ,
id : 'y-axis-0'
} ]
}
} ;
Chart . controllers . line = Chart . DatasetController . extend ( {
addElements : function ( ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
this . getDataset ( ) . metaDataset = this . getDataset ( ) . metaDataset || new Chart . elements . Line ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_points : this . getDataset ( ) . metaData
} ) ;
helpers . each ( this . getDataset ( ) . data , function ( value , index ) {
this . getDataset ( ) . metaData [ index ] = this . getDataset ( ) . metaData [ index ] || new Chart . elements . Point ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
} , this ) ;
} ,
addElementAndReset : function ( index ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
var point = new Chart . elements . Point ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
// Reset the point
this . updateElement ( point , index , true ) ;
// Add to the points array
this . getDataset ( ) . metaData . splice ( index , 0 , point ) ;
// Make sure bezier control points are updated
if ( this . chart . options . showLines && this . chart . options . elements . line . tension !== 0 )
this . updateBezierControlPoints ( ) ;
} ,
update : function update ( reset ) {
var line = this . getDataset ( ) . metaDataset ;
var points = this . getDataset ( ) . metaData ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var scaleBase ;
if ( yScale . min < 0 && yScale . max < 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . max ) ;
} else if ( yScale . min > 0 && yScale . max > 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . min ) ;
} else {
scaleBase = yScale . getPixelForValue ( 0 ) ;
}
// Update Line
if ( this . chart . options . showLines ) {
// Utility
line . _scale = yScale ;
line . _datasetIndex = this . index ;
// Data
line . _children = points ;
// Model
line . _model = {
// Appearance
tension : line . custom && line . custom . tension ? line . custom . tension : helpers . getValueOrDefault ( this . getDataset ( ) . tension , this . chart . options . elements . line . tension ) ,
backgroundColor : line . custom && line . custom . backgroundColor ? line . custom . backgroundColor : ( this . getDataset ( ) . backgroundColor || this . chart . options . elements . line . backgroundColor ) ,
borderWidth : line . custom && line . custom . borderWidth ? line . custom . borderWidth : ( this . getDataset ( ) . borderWidth || this . chart . options . elements . line . borderWidth ) ,
borderColor : line . custom && line . custom . borderColor ? line . custom . borderColor : ( this . getDataset ( ) . borderColor || this . chart . options . elements . line . borderColor ) ,
borderCapStyle : line . custom && line . custom . borderCapStyle ? line . custom . borderCapStyle : ( this . getDataset ( ) . borderCapStyle || this . chart . options . elements . line . borderCapStyle ) ,
borderDash : line . custom && line . custom . borderDash ? line . custom . borderDash : ( this . getDataset ( ) . borderDash || this . chart . options . elements . line . borderDash ) ,
borderDashOffset : line . custom && line . custom . borderDashOffset ? line . custom . borderDashOffset : ( this . getDataset ( ) . borderDashOffset || this . chart . options . elements . line . borderDashOffset ) ,
borderJoinStyle : line . custom && line . custom . borderJoinStyle ? line . custom . borderJoinStyle : ( this . getDataset ( ) . borderJoinStyle || this . chart . options . elements . line . borderJoinStyle ) ,
fill : line . custom && line . custom . fill ? line . custom . fill : ( this . getDataset ( ) . fill !== undefined ? this . getDataset ( ) . fill : this . chart . options . elements . line . fill ) ,
// Scale
scaleTop : yScale . top ,
scaleBottom : yScale . bottom ,
scaleZero : scaleBase
} ;
line . pivot ( ) ;
}
// Update Points
helpers . each ( points , function ( point , index ) {
this . updateElement ( point , index , reset ) ;
} , this ) ;
if ( this . chart . options . showLines && this . chart . options . elements . line . tension !== 0 )
this . updateBezierControlPoints ( ) ;
} ,
getPointBackgroundColor : function ( point , index ) {
var backgroundColor = this . chart . options . elements . point . backgroundColor ;
var dataset = this . getDataset ( ) ;
if ( point . custom && point . custom . backgroundColor ) {
backgroundColor = point . custom . backgroundColor ;
} else if ( dataset . pointBackgroundColor ) {
backgroundColor = helpers . getValueAtIndexOrDefault ( dataset . pointBackgroundColor , index , backgroundColor ) ;
} else if ( dataset . backgroundColor ) {
backgroundColor = dataset . backgroundColor ;
}
return backgroundColor ;
} ,
getPointBorderColor : function ( point , index ) {
var borderColor = this . chart . options . elements . point . borderColor ;
var dataset = this . getDataset ( ) ;
if ( point . custom && point . custom . borderColor ) {
borderColor = point . custom . borderColor ;
} else if ( dataset . pointBorderColor ) {
borderColor = helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBorderColor , index , borderColor ) ;
} else if ( dataset . borderColor ) {
borderColor = dataset . borderColor ;
}
return borderColor ;
} ,
getPointBorderWidth : function ( point , index ) {
var borderWidth = this . chart . options . elements . point . borderWidth ;
var dataset = this . getDataset ( ) ;
if ( point . custom && point . custom . borderWidth !== undefined ) {
borderWidth = point . custom . borderWidth ;
} else if ( dataset . pointBorderWidth !== undefined ) {
borderWidth = helpers . getValueAtIndexOrDefault ( dataset . pointBorderWidth , index , borderWidth ) ;
} else if ( dataset . borderWidth !== undefined ) {
borderWidth = dataset . borderWidth ;
}
return borderWidth ;
} ,
updateElement : function ( point , index , reset ) {
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var scaleBase ;
if ( yScale . min < 0 && yScale . max < 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . max ) ;
} else if ( yScale . min > 0 && yScale . max > 0 ) {
scaleBase = yScale . getPixelForValue ( yScale . min ) ;
} else {
scaleBase = yScale . getPixelForValue ( 0 ) ;
}
// Utility
point . _chart = this . chart . chart ;
point . _xScale = xScale ;
point . _yScale = yScale ;
point . _datasetIndex = this . index ;
point . _index = index ;
// Desired view properties
point . _model = {
x : xScale . getPixelForValue ( this . getDataset ( ) . data [ index ] , index , this . index , this . chart . isCombo ) ,
y : reset ? scaleBase : this . calculatePointY ( this . getDataset ( ) . data [ index ] , index , this . index , this . chart . isCombo ) ,
// Appearance
tension : point . custom && point . custom . tension ? point . custom . tension : helpers . getValueOrDefault ( this . getDataset ( ) . tension , this . chart . options . elements . line . tension ) ,
radius : point . custom && point . custom . radius ? point . custom . radius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . radius , index , this . chart . options . elements . point . radius ) ,
pointStyle : point . custom && point . custom . pointStyle ? point . custom . pointStyle : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointStyle , index , this . chart . options . elements . point . pointStyle ) ,
backgroundColor : this . getPointBackgroundColor ( point , index ) ,
borderColor : this . getPointBorderColor ( point , index ) ,
borderWidth : this . getPointBorderWidth ( point , index ) ,
// Tooltip
hitRadius : point . custom && point . custom . hitRadius ? point . custom . hitRadius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . hitRadius , index , this . chart . options . elements . point . hitRadius )
} ;
point . _model . skip = point . custom && point . custom . skip ? point . custom . skip : ( isNaN ( point . _model . x ) || isNaN ( point . _model . y ) ) ;
} ,
calculatePointY : function ( value , index , datasetIndex , isCombo ) {
var xScale = this . getScaleForId ( this . getDataset ( ) . xAxisID ) ;
var yScale = this . getScaleForId ( this . getDataset ( ) . yAxisID ) ;
if ( yScale . options . stacked ) {
var sumPos = 0 ,
sumNeg = 0 ;
for ( var i = 0 ; i < datasetIndex ; i ++ ) {
var ds = this . chart . data . datasets [ i ] ;
if ( ds . type === 'line' && helpers . isDatasetVisible ( ds ) ) {
if ( ds . data [ index ] < 0 ) {
sumNeg += ds . data [ index ] || 0 ;
} else {
sumPos += ds . data [ index ] || 0 ;
}
}
}
if ( value < 0 ) {
return yScale . getPixelForValue ( sumNeg + value ) ;
} else {
return yScale . getPixelForValue ( sumPos + value ) ;
}
}
return yScale . getPixelForValue ( value ) ;
} ,
updateBezierControlPoints : function ( ) {
// Update bezier control points
helpers . each ( this . getDataset ( ) . metaData , function ( point , index ) {
var controlPoints = helpers . splineCurve (
helpers . previousItem ( this . getDataset ( ) . metaData , index ) . _model ,
point . _model ,
helpers . nextItem ( this . getDataset ( ) . metaData , index ) . _model ,
point . _model . tension
) ;
// Prevent the bezier going outside of the bounds of the graph
point . _model . controlPointPreviousX = Math . max ( Math . min ( controlPoints . previous . x , this . chart . chartArea . right ) , this . chart . chartArea . left ) ;
point . _model . controlPointPreviousY = Math . max ( Math . min ( controlPoints . previous . y , this . chart . chartArea . bottom ) , this . chart . chartArea . top ) ;
point . _model . controlPointNextX = Math . max ( Math . min ( controlPoints . next . x , this . chart . chartArea . right ) , this . chart . chartArea . left ) ;
point . _model . controlPointNextY = Math . max ( Math . min ( controlPoints . next . y , this . chart . chartArea . bottom ) , this . chart . chartArea . top ) ;
// Now pivot the point for animation
point . pivot ( ) ;
} , this ) ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
// Transition Point Locations
helpers . each ( this . getDataset ( ) . metaData , function ( point ) {
point . transition ( easingDecimal ) ;
} ) ;
// Transition and Draw the line
if ( this . chart . options . showLines )
this . getDataset ( ) . metaDataset . transition ( easingDecimal ) . draw ( ) ;
// Draw the points
helpers . each ( this . getDataset ( ) . metaData , function ( point ) {
point . draw ( ) ;
} ) ;
} ,
setHoverStyle : function ( point ) {
// Point
var dataset = this . chart . data . datasets [ point . _datasetIndex ] ;
var index = point . _index ;
point . _model . radius = point . custom && point . custom . hoverRadius ? point . custom . hoverRadius : helpers . getValueAtIndexOrDefault ( dataset . pointHoverRadius , index , this . chart . options . elements . point . hoverRadius ) ;
point . _model . backgroundColor = point . custom && point . custom . hoverBackgroundColor ? point . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . pointHoverBackgroundColor , index , helpers . color ( point . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
point . _model . borderColor = point . custom && point . custom . hoverBorderColor ? point . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . pointHoverBorderColor , index , helpers . color ( point . _model . borderColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
point . _model . borderWidth = point . custom && point . custom . hoverBorderWidth ? point . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . pointHoverBorderWidth , index , point . _model . borderWidth ) ;
} ,
removeHoverStyle : function ( point ) {
var dataset = this . chart . data . datasets [ point . _datasetIndex ] ;
var index = point . _index ;
point . _model . radius = point . custom && point . custom . radius ? point . custom . radius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . radius , index , this . chart . options . elements . point . radius ) ;
point . _model . backgroundColor = this . getPointBackgroundColor ( point , index ) ;
point . _model . borderColor = this . getPointBorderColor ( point , index ) ;
point . _model . borderWidth = this . getPointBorderWidth ( point , index ) ;
}
} ) ;
} ;
} , { } ] , 19 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . polarArea = {
scale : {
type : "radialLinear" ,
lineArc : true // so that lines are circular
} ,
//Boolean - Whether to animate the rotation of the chart
animateRotate : true ,
animateScale : true ,
aspectRatio : 1 ,
legendCallback : function ( chart ) {
var text = [ ] ;
text . push ( '<ul class="' + chart . id + '-legend">' ) ;
if ( chart . data . datasets . length ) {
for ( var i = 0 ; i < chart . data . datasets [ 0 ] . data . length ; ++ i ) {
text . push ( '<li><span style="background-color:' + chart . data . datasets [ 0 ] . backgroundColor [ i ] + '">' ) ;
if ( chart . data . labels [ i ] ) {
text . push ( chart . data . labels [ i ] ) ;
}
text . push ( '</span></li>' ) ;
}
}
text . push ( '</ul>' ) ;
return text . join ( "" ) ;
} ,
legend : {
labels : {
generateLabels : function ( data ) {
if ( data . labels . length && data . datasets . length ) {
return data . labels . map ( function ( label , i ) {
return {
text : label ,
fillStyle : data . datasets [ 0 ] . backgroundColor [ i ] ,
hidden : isNaN ( data . datasets [ 0 ] . data [ i ] ) ,
// Extra data used for toggling the correct item
index : i
} ;
} ) ;
} else {
return [ ] ;
}
}
} ,
onClick : function ( e , legendItem ) {
helpers . each ( this . chart . data . datasets , function ( dataset ) {
dataset . metaHiddenData = dataset . metaHiddenData || [ ] ;
var idx = legendItem . index ;
if ( ! isNaN ( dataset . data [ idx ] ) ) {
dataset . metaHiddenData [ idx ] = dataset . data [ idx ] ;
dataset . data [ idx ] = NaN ;
} else if ( ! isNaN ( dataset . metaHiddenData [ idx ] ) ) {
dataset . data [ idx ] = dataset . metaHiddenData [ idx ] ;
}
} ) ;
this . chart . update ( ) ;
}
} ,
// Need to override these to give a nice default
tooltips : {
callbacks : {
title : function ( ) {
return '' ;
} ,
label : function ( tooltipItem , data ) {
return data . labels [ tooltipItem . index ] + ': ' + tooltipItem . yLabel ;
}
}
}
} ;
Chart . controllers . polarArea = Chart . DatasetController . extend ( {
linkScales : function ( ) {
// no scales for doughnut
} ,
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 ) ;
} ,
addElementAndReset : function ( index ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
var arc = new Chart . elements . Arc ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
// Reset the point
this . updateElement ( arc , index , true ) ;
// Add to the points array
this . getDataset ( ) . metaData . splice ( index , 0 , arc ) ;
} ,
getVisibleDatasetCount : function getVisibleDatasetCount ( ) {
return helpers . where ( this . chart . data . datasets , function ( ds ) {
return helpers . isDatasetVisible ( ds ) ;
} ) . length ;
} ,
update : function update ( reset ) {
var minSize = Math . min ( this . chart . chartArea . right - this . chart . chartArea . left , this . chart . chartArea . bottom - this . chart . chartArea . top ) ;
this . chart . outerRadius = Math . max ( ( minSize - this . chart . options . elements . arc . borderWidth / 2 ) / 2 , 0 ) ;
this . chart . innerRadius = Math . max ( this . chart . options . cutoutPercentage ? ( this . chart . outerRadius / 100 ) * ( this . chart . options . cutoutPercentage ) : 1 , 0 ) ;
this . chart . radiusLength = ( this . chart . outerRadius - this . chart . innerRadius ) / this . getVisibleDatasetCount ( ) ;
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 . outerRadius - this . chart . radiusLength ;
helpers . each ( this . getDataset ( ) . metaData , function ( arc , index ) {
this . updateElement ( arc , index , reset ) ;
} , this ) ;
} ,
updateElement : function ( arc , index , reset ) {
var circumference = this . calculateCircumference ( this . getDataset ( ) . data [ index ] ) ;
var centerX = ( this . chart . chartArea . left + this . chart . chartArea . right ) / 2 ;
var centerY = ( this . chart . chartArea . top + this . chart . chartArea . bottom ) / 2 ;
// If there is NaN data before us, we need to calculate the starting angle correctly.
// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
var notNullIndex = 0 ;
for ( var i = 0 ; i < index ; ++ i ) {
if ( ! isNaN ( this . getDataset ( ) . data [ i ] ) ) {
++ notNullIndex ;
}
}
var startAngle = ( - 0.5 * Math . PI ) + ( circumference * notNullIndex ) ;
var endAngle = startAngle + circumference ;
var resetModel = {
x : centerX ,
y : centerY ,
innerRadius : 0 ,
outerRadius : this . chart . options . animateScale ? 0 : this . chart . scale . getDistanceFromCenterForValue ( this . getDataset ( ) . data [ index ] ) ,
startAngle : this . chart . options . animateRotate ? Math . PI * - 0.5 : startAngle ,
endAngle : this . chart . options . animateRotate ? Math . PI * - 0.5 : endAngle ,
backgroundColor : arc . custom && arc . custom . backgroundColor ? arc . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . arc . backgroundColor ) ,
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 . chart . data . labels , index , this . chart . data . labels [ index ] )
} ;
helpers . extend ( arc , {
// Utility
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index ,
_scale : this . chart . scale ,
// Desired view properties
_model : reset ? resetModel : {
x : centerX ,
y : centerY ,
innerRadius : 0 ,
outerRadius : this . chart . scale . getDistanceFromCenterForValue ( this . getDataset ( ) . data [ index ] ) ,
startAngle : startAngle ,
endAngle : endAngle ,
backgroundColor : arc . custom && arc . custom . backgroundColor ? arc . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . arc . backgroundColor ) ,
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 . chart . data . labels , index , this . chart . data . labels [ index ] )
}
} ) ;
arc . pivot ( ) ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
helpers . each ( this . getDataset ( ) . metaData , function ( arc , index ) {
arc . transition ( easingDecimal ) . draw ( ) ;
} ) ;
} ,
setHoverStyle : function ( arc ) {
var dataset = this . chart . data . datasets [ arc . _datasetIndex ] ;
var index = arc . _index ;
arc . _model . backgroundColor = arc . custom && arc . custom . hoverBackgroundColor ? arc . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBackgroundColor , index , helpers . color ( arc . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
arc . _model . borderColor = arc . custom && arc . custom . hoverBorderColor ? arc . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderColor , index , helpers . color ( arc . _model . borderColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
arc . _model . borderWidth = arc . custom && arc . custom . hoverBorderWidth ? arc . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . hoverBorderWidth , index , arc . _model . borderWidth ) ;
} ,
removeHoverStyle : function ( arc ) {
var dataset = this . chart . data . datasets [ arc . _datasetIndex ] ;
var index = arc . _index ;
arc . _model . backgroundColor = arc . custom && arc . custom . backgroundColor ? arc . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . backgroundColor , index , this . chart . options . elements . arc . backgroundColor ) ;
arc . _model . borderColor = arc . custom && arc . custom . borderColor ? arc . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderColor , index , this . chart . options . elements . arc . borderColor ) ;
arc . _model . borderWidth = arc . custom && arc . custom . borderWidth ? arc . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . borderWidth , index , this . chart . options . elements . arc . borderWidth ) ;
} ,
calculateCircumference : function ( value ) {
if ( isNaN ( value ) ) {
return 0 ;
} else {
// Count the number of NaN values
var numNaN = helpers . where ( this . getDataset ( ) . data , function ( data ) {
return isNaN ( data ) ;
} ) . length ;
return ( 2 * Math . PI ) / ( this . getDataset ( ) . data . length - numNaN ) ;
}
}
} ) ;
} ;
} , { } ] , 20 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . radar = {
scale : {
type : "radialLinear"
} ,
elements : {
line : {
tension : 0 // no bezier in radar
}
}
} ;
Chart . controllers . radar = Chart . DatasetController . extend ( {
linkScales : function ( ) {
// No need. Single scale only
} ,
addElements : function ( ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
this . getDataset ( ) . metaDataset = this . getDataset ( ) . metaDataset || new Chart . elements . Line ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_points : this . getDataset ( ) . metaData ,
_loop : true
} ) ;
helpers . each ( this . getDataset ( ) . data , function ( value , index ) {
this . getDataset ( ) . metaData [ index ] = this . getDataset ( ) . metaData [ index ] || new Chart . elements . Point ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index ,
_model : {
x : 0 , //xScale.getPixelForValue(null, index, true),
y : 0 //this.chartArea.bottom,
}
} ) ;
} , this ) ;
} ,
addElementAndReset : function ( index ) {
this . getDataset ( ) . metaData = this . getDataset ( ) . metaData || [ ] ;
var point = new Chart . elements . Point ( {
_chart : this . chart . chart ,
_datasetIndex : this . index ,
_index : index
} ) ;
// Reset the point
this . updateElement ( point , index , true ) ;
// Add to the points array
this . getDataset ( ) . metaData . splice ( index , 0 , point ) ;
// Make sure bezier control points are updated
this . updateBezierControlPoints ( ) ;
} ,
update : function update ( reset ) {
var line = this . getDataset ( ) . metaDataset ;
var points = this . getDataset ( ) . metaData ;
var scale = this . chart . scale ;
var scaleBase ;
if ( scale . min < 0 && scale . max < 0 ) {
scaleBase = scale . getPointPositionForValue ( 0 , scale . max ) ;
} else if ( scale . min > 0 && scale . max > 0 ) {
scaleBase = scale . getPointPositionForValue ( 0 , scale . min ) ;
} else {
scaleBase = scale . getPointPositionForValue ( 0 , 0 ) ;
}
helpers . extend ( this . getDataset ( ) . metaDataset , {
// Utility
_datasetIndex : this . index ,
// Data
_children : this . getDataset ( ) . metaData ,
// Model
_model : {
// Appearance
tension : line . custom && line . custom . tension ? line . custom . tension : helpers . getValueOrDefault ( this . getDataset ( ) . tension , this . chart . options . elements . line . tension ) ,
backgroundColor : line . custom && line . custom . backgroundColor ? line . custom . backgroundColor : ( this . getDataset ( ) . backgroundColor || this . chart . options . elements . line . backgroundColor ) ,
borderWidth : line . custom && line . custom . borderWidth ? line . custom . borderWidth : ( this . getDataset ( ) . borderWidth || this . chart . options . elements . line . borderWidth ) ,
borderColor : line . custom && line . custom . borderColor ? line . custom . borderColor : ( this . getDataset ( ) . borderColor || this . chart . options . elements . line . borderColor ) ,
fill : line . custom && line . custom . fill ? line . custom . fill : ( this . getDataset ( ) . fill !== undefined ? this . getDataset ( ) . fill : this . chart . options . elements . line . fill ) ,
borderCapStyle : line . custom && line . custom . borderCapStyle ? line . custom . borderCapStyle : ( this . getDataset ( ) . borderCapStyle || this . chart . options . elements . line . borderCapStyle ) ,
borderDash : line . custom && line . custom . borderDash ? line . custom . borderDash : ( this . getDataset ( ) . borderDash || this . chart . options . elements . line . borderDash ) ,
borderDashOffset : line . custom && line . custom . borderDashOffset ? line . custom . borderDashOffset : ( this . getDataset ( ) . borderDashOffset || this . chart . options . elements . line . borderDashOffset ) ,
borderJoinStyle : line . custom && line . custom . borderJoinStyle ? line . custom . borderJoinStyle : ( this . getDataset ( ) . borderJoinStyle || this . chart . options . elements . line . borderJoinStyle ) ,
// Scale
scaleTop : scale . top ,
scaleBottom : scale . bottom ,
scaleZero : scaleBase
}
} ) ;
this . getDataset ( ) . metaDataset . pivot ( ) ;
// Update Points
helpers . each ( points , function ( point , index ) {
this . updateElement ( point , index , reset ) ;
} , this ) ;
// Update bezier control points
this . updateBezierControlPoints ( ) ;
} ,
updateElement : function ( point , index , reset ) {
var pointPosition = this . chart . scale . getPointPositionForValue ( index , this . getDataset ( ) . data [ index ] ) ;
helpers . extend ( point , {
// Utility
_datasetIndex : this . index ,
_index : index ,
_scale : this . chart . scale ,
// Desired view properties
_model : {
x : reset ? this . chart . scale . xCenter : pointPosition . x , // value not used in dataset scale, but we want a consistent API between scales
y : reset ? this . chart . scale . yCenter : pointPosition . y ,
// Appearance
tension : point . custom && point . custom . tension ? point . custom . tension : helpers . getValueOrDefault ( this . getDataset ( ) . tension , this . chart . options . elements . line . tension ) ,
radius : point . custom && point . custom . radius ? point . custom . radius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointRadius , index , this . chart . options . elements . point . radius ) ,
backgroundColor : point . custom && point . custom . backgroundColor ? point . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBackgroundColor , index , this . chart . options . elements . point . backgroundColor ) ,
borderColor : point . custom && point . custom . borderColor ? point . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBorderColor , index , this . chart . options . elements . point . borderColor ) ,
borderWidth : point . custom && point . custom . borderWidth ? point . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBorderWidth , index , this . chart . options . elements . point . borderWidth ) ,
pointStyle : point . custom && point . custom . pointStyle ? point . custom . pointStyle : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointStyle , index , this . chart . options . elements . point . pointStyle ) ,
// Tooltip
hitRadius : point . custom && point . custom . hitRadius ? point . custom . hitRadius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . hitRadius , index , this . chart . options . elements . point . hitRadius )
}
} ) ;
point . _model . skip = point . custom && point . custom . skip ? point . custom . skip : ( isNaN ( point . _model . x ) || isNaN ( point . _model . y ) ) ;
} ,
updateBezierControlPoints : function ( ) {
helpers . each ( this . getDataset ( ) . metaData , function ( point , index ) {
var controlPoints = helpers . splineCurve (
helpers . previousItem ( this . getDataset ( ) . metaData , index , true ) . _model ,
point . _model ,
helpers . nextItem ( this . getDataset ( ) . metaData , index , true ) . _model ,
point . _model . tension
) ;
// Prevent the bezier going outside of the bounds of the graph
point . _model . controlPointPreviousX = Math . max ( Math . min ( controlPoints . previous . x , this . chart . chartArea . right ) , this . chart . chartArea . left ) ;
point . _model . controlPointPreviousY = Math . max ( Math . min ( controlPoints . previous . y , this . chart . chartArea . bottom ) , this . chart . chartArea . top ) ;
point . _model . controlPointNextX = Math . max ( Math . min ( controlPoints . next . x , this . chart . chartArea . right ) , this . chart . chartArea . left ) ;
point . _model . controlPointNextY = Math . max ( Math . min ( controlPoints . next . y , this . chart . chartArea . bottom ) , this . chart . chartArea . top ) ;
// Now pivot the point for animation
point . pivot ( ) ;
} , this ) ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
// Transition Point Locations
helpers . each ( this . getDataset ( ) . metaData , function ( point , index ) {
point . transition ( easingDecimal ) ;
} ) ;
// Transition and Draw the line
this . getDataset ( ) . metaDataset . transition ( easingDecimal ) . draw ( ) ;
// Draw the points
helpers . each ( this . getDataset ( ) . metaData , function ( point ) {
point . draw ( ) ;
} ) ;
} ,
setHoverStyle : function ( point ) {
// Point
var dataset = this . chart . data . datasets [ point . _datasetIndex ] ;
var index = point . _index ;
point . _model . radius = point . custom && point . custom . hoverRadius ? point . custom . hoverRadius : helpers . getValueAtIndexOrDefault ( dataset . pointHoverRadius , index , this . chart . options . elements . point . hoverRadius ) ;
point . _model . backgroundColor = point . custom && point . custom . hoverBackgroundColor ? point . custom . hoverBackgroundColor : helpers . getValueAtIndexOrDefault ( dataset . pointHoverBackgroundColor , index , helpers . color ( point . _model . backgroundColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
point . _model . borderColor = point . custom && point . custom . hoverBorderColor ? point . custom . hoverBorderColor : helpers . getValueAtIndexOrDefault ( dataset . pointHoverBorderColor , index , helpers . color ( point . _model . borderColor ) . saturate ( 0.5 ) . darken ( 0.1 ) . rgbString ( ) ) ;
point . _model . borderWidth = point . custom && point . custom . hoverBorderWidth ? point . custom . hoverBorderWidth : helpers . getValueAtIndexOrDefault ( dataset . pointHoverBorderWidth , index , point . _model . borderWidth ) ;
} ,
removeHoverStyle : function ( point ) {
var dataset = this . chart . data . datasets [ point . _datasetIndex ] ;
var index = point . _index ;
point . _model . radius = point . custom && point . custom . radius ? point . custom . radius : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . radius , index , this . chart . options . elements . point . radius ) ;
point . _model . backgroundColor = point . custom && point . custom . backgroundColor ? point . custom . backgroundColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBackgroundColor , index , this . chart . options . elements . point . backgroundColor ) ;
point . _model . borderColor = point . custom && point . custom . borderColor ? point . custom . borderColor : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBorderColor , index , this . chart . options . elements . point . borderColor ) ;
point . _model . borderWidth = point . custom && point . custom . borderWidth ? point . custom . borderWidth : helpers . getValueAtIndexOrDefault ( this . getDataset ( ) . pointBorderWidth , index , this . chart . options . elements . point . borderWidth ) ;
}
} ) ;
} ;
} , { } ] , 21 : [ function ( require , module , exports ) {
/*global window: false */
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . animation = {
duration : 1000 ,
easing : "easeOutQuart" ,
onProgress : helpers . noop ,
onComplete : helpers . noop
} ;
Chart . Animation = Chart . Element . extend ( {
currentStep : null , // the current animation step
numSteps : 60 , // default number of steps
easing : "" , // the easing to use for this animation
render : null , // render function used by the animation service
onAnimationProgress : null , // user specified callback to fire on each step of the animation
onAnimationComplete : null // user specified callback to fire when the animation finishes
} ) ;
Chart . animationService = {
frameDuration : 17 ,
animations : [ ] ,
dropFrames : 0 ,
request : null ,
addAnimation : function ( chartInstance , animationObject , duration , lazy ) {
if ( ! lazy ) {
chartInstance . animating = true ;
}
for ( var index = 0 ; index < this . animations . length ; ++ index ) {
if ( this . animations [ index ] . chartInstance === chartInstance ) {
// replacing an in progress animation
this . animations [ index ] . animationObject = animationObject ;
return ;
}
}
this . animations . push ( {
chartInstance : chartInstance ,
animationObject : animationObject
} ) ;
// If there are no animations queued, manually kickstart a digest, for lack of a better word
if ( this . animations . length === 1 ) {
this . requestAnimationFrame ( ) ;
}
} ,
// Cancel the animation for a given chart instance
cancelAnimation : function ( chartInstance ) {
var index = helpers . findIndex ( this . animations , function ( animationWrapper ) {
return animationWrapper . chartInstance === chartInstance ;
} ) ;
if ( index !== - 1 ) {
this . animations . splice ( index , 1 ) ;
chartInstance . animating = false ;
}
} ,
requestAnimationFrame : function ( ) {
var me = this ;
if ( me . request === null ) {
// Skip animation frame requests until the active one is executed.
// This can happen when processing mouse events, e.g. 'mousemove'
// and 'mouseout' events will trigger multiple renders.
me . request = helpers . requestAnimFrame . call ( window , function ( ) {
me . request = null ;
me . startDigest ( ) ;
} ) ;
}
} ,
startDigest : function ( ) {
var startTime = Date . now ( ) ;
var framesToDrop = 0 ;
if ( this . dropFrames > 1 ) {
framesToDrop = Math . floor ( this . dropFrames ) ;
this . dropFrames = this . dropFrames % 1 ;
}
var i = 0 ;
while ( i < this . animations . length ) {
if ( this . animations [ i ] . animationObject . currentStep === null ) {
this . animations [ i ] . animationObject . currentStep = 0 ;
}
this . animations [ i ] . animationObject . currentStep += 1 + framesToDrop ;
if ( this . animations [ i ] . animationObject . currentStep > this . animations [ i ] . animationObject . numSteps ) {
this . animations [ i ] . animationObject . currentStep = this . animations [ i ] . animationObject . numSteps ;
}
this . animations [ i ] . animationObject . render ( this . animations [ i ] . chartInstance , this . animations [ i ] . animationObject ) ;
if ( this . animations [ i ] . animationObject . onAnimationProgress && this . animations [ i ] . animationObject . onAnimationProgress . call ) {
this . animations [ i ] . animationObject . onAnimationProgress . call ( this . animations [ i ] . chartInstance , this . animations [ i ] ) ;
}
if ( this . animations [ i ] . animationObject . currentStep === this . animations [ i ] . animationObject . numSteps ) {
if ( this . animations [ i ] . animationObject . onAnimationComplete && this . animations [ i ] . animationObject . onAnimationComplete . call ) {
this . animations [ i ] . animationObject . onAnimationComplete . call ( this . animations [ i ] . chartInstance , this . animations [ i ] ) ;
}
// executed the last frame. Remove the animation.
this . animations [ i ] . chartInstance . animating = false ;
this . animations . splice ( i , 1 ) ;
} else {
++ i ;
}
}
var endTime = Date . now ( ) ;
var dropFrames = ( endTime - startTime ) / this . frameDuration ;
this . dropFrames += dropFrames ;
// Do we have more stuff to animate?
if ( this . animations . length > 0 ) {
this . requestAnimationFrame ( ) ;
}
}
} ;
} ;
} , { } ] , 22 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
//Create a dictionary of chart types, to allow for extension of existing types
Chart . types = { } ;
//Store a reference to each instance - allowing us to globally resize chart instances on window resize.
//Destroy method on the chart will remove the instance of the chart from this reference.
Chart . instances = { } ;
// Controllers available for dataset visualization eg. bar, line, slice, etc.
Chart . controllers = { } ;
// The main controller of a chart
Chart . Controller = function ( instance ) {
this . chart = instance ;
this . config = instance . config ;
this . options = this . config . options = helpers . configMerge ( Chart . defaults . global , Chart . defaults [ this . config . type ] , this . config . options || { } ) ;
this . id = helpers . uid ( ) ;
Object . defineProperty ( this , 'data' , {
get : function ( ) {
return this . config . data ;
}
} ) ;
//Add the chart instance to the global namespace
Chart . instances [ this . id ] = this ;
if ( this . options . responsive ) {
// Silent resize before chart draws
this . resize ( true ) ;
}
this . initialize ( ) ;
return this ;
} ;
helpers . extend ( Chart . Controller . prototype , {
initialize : function initialize ( ) {
// TODO
// If BeforeInit(this) doesn't return false, proceed
this . bindEvents ( ) ;
// Make sure controllers are built first so that each dataset is bound to an axis before the scales
// are built
this . ensureScalesHaveIDs ( ) ;
this . buildOrUpdateControllers ( ) ;
this . buildScales ( ) ;
this . buildSurroundingItems ( ) ;
this . updateLayout ( ) ;
this . resetElements ( ) ;
this . initToolTip ( ) ;
this . update ( ) ;
// TODO
// If AfterInit(this) doesn't return false, proceed
return this ;
} ,
clear : function clear ( ) {
helpers . clear ( this . chart ) ;
return this ;
} ,
stop : function stop ( ) {
// Stops any current animation loop occuring
Chart . animationService . cancelAnimation ( this ) ;
return this ;
} ,
resize : function resize ( silent ) {
var canvas = this . chart . canvas ;
var newWidth = helpers . getMaximumWidth ( this . chart . canvas ) ;
var newHeight = ( this . options . maintainAspectRatio && isNaN ( this . chart . aspectRatio ) === false && isFinite ( this . chart . aspectRatio ) && this . chart . aspectRatio !== 0 ) ? newWidth / this . chart . aspectRatio : helpers . getMaximumHeight ( this . chart . canvas ) ;
var sizeChanged = this . chart . width !== newWidth || this . chart . height !== newHeight ;
if ( ! sizeChanged )
return this ;
canvas . width = this . chart . width = newWidth ;
canvas . height = this . chart . height = newHeight ;
helpers . retinaScale ( this . chart ) ;
if ( ! silent ) {
this . stop ( ) ;
this . update ( this . options . responsiveAnimationDuration ) ;
}
return this ;
} ,
ensureScalesHaveIDs : function ensureScalesHaveIDs ( ) {
var defaultXAxisID = 'x-axis-' ;
var defaultYAxisID = 'y-axis-' ;
if ( this . options . scales ) {
if ( this . options . scales . xAxes && this . options . scales . xAxes . length ) {
helpers . each ( this . options . scales . xAxes , function ( xAxisOptions , index ) {
xAxisOptions . id = xAxisOptions . id || ( defaultXAxisID + index ) ;
} ) ;
}
if ( this . options . scales . yAxes && this . options . scales . yAxes . length ) {
// Build the y axes
helpers . each ( this . options . scales . yAxes , function ( yAxisOptions , index ) {
yAxisOptions . id = yAxisOptions . id || ( defaultYAxisID + index ) ;
} ) ;
}
}
} ,
buildScales : function buildScales ( ) {
// Map of scale ID to scale object so we can lookup later
this . scales = { } ;
// Build the x axes
if ( this . options . scales ) {
if ( this . options . scales . xAxes && this . options . scales . xAxes . length ) {
helpers . each ( this . options . scales . xAxes , function ( xAxisOptions , index ) {
var xType = helpers . getValueOrDefault ( xAxisOptions . type , 'category' ) ;
var ScaleClass = Chart . scaleService . getScaleConstructor ( xType ) ;
if ( ScaleClass ) {
var scale = new ScaleClass ( {
ctx : this . chart . ctx ,
options : xAxisOptions ,
chart : this ,
id : xAxisOptions . id
} ) ;
this . scales [ scale . id ] = scale ;
}
} , this ) ;
}
if ( this . options . scales . yAxes && this . options . scales . yAxes . length ) {
// Build the y axes
helpers . each ( this . options . scales . yAxes , function ( yAxisOptions , index ) {
var yType = helpers . getValueOrDefault ( yAxisOptions . type , 'linear' ) ;
var ScaleClass = Chart . scaleService . getScaleConstructor ( yType ) ;
if ( ScaleClass ) {
var scale = new ScaleClass ( {
ctx : this . chart . ctx ,
options : yAxisOptions ,
chart : this ,
id : yAxisOptions . id
} ) ;
this . scales [ scale . id ] = scale ;
}
} , this ) ;
}
}
if ( this . options . scale ) {
// Build radial axes
var ScaleClass = Chart . scaleService . getScaleConstructor ( this . options . scale . type ) ;
if ( ScaleClass ) {
var scale = new ScaleClass ( {
ctx : this . chart . ctx ,
options : this . options . scale ,
chart : this
} ) ;
this . scale = scale ;
this . scales . radialScale = scale ;
}
}
Chart . scaleService . addScalesToLayout ( this ) ;
} ,
buildSurroundingItems : function ( ) {
if ( this . options . title ) {
this . titleBlock = new Chart . Title ( {
ctx : this . chart . ctx ,
options : this . options . title ,
chart : this
} ) ;
Chart . layoutService . addBox ( this , this . titleBlock ) ;
}
if ( this . options . legend ) {
this . legend = new Chart . Legend ( {
ctx : this . chart . ctx ,
options : this . options . legend ,
chart : this
} ) ;
Chart . layoutService . addBox ( this , this . legend ) ;
}
} ,
updateLayout : function ( ) {
Chart . layoutService . update ( this , this . chart . width , this . chart . height ) ;
} ,
buildOrUpdateControllers : function buildOrUpdateControllers ( ) {
var types = [ ] ;
var newControllers = [ ] ;
helpers . each ( this . data . datasets , function ( dataset , datasetIndex ) {
if ( ! dataset . type ) {
dataset . type = this . config . type ;
}
var type = dataset . type ;
types . push ( type ) ;
if ( dataset . controller ) {
dataset . controller . updateIndex ( datasetIndex ) ;
} else {
dataset . controller = new Chart . controllers [ type ] ( this , datasetIndex ) ;
newControllers . push ( dataset . controller ) ;
}
} , this ) ;
if ( types . length > 1 ) {
for ( var i = 1 ; i < types . length ; i ++ ) {
if ( types [ i ] !== types [ i - 1 ] ) {
this . isCombo = true ;
break ;
}
}
}
return newControllers ;
} ,
resetElements : function resetElements ( ) {
helpers . each ( this . data . datasets , function ( dataset , datasetIndex ) {
dataset . controller . reset ( ) ;
} ) ;
} ,
update : function update ( animationDuration , lazy ) {
// In case the entire data object changed
this . tooltip . _data = this . data ;
// Make sure dataset controllers are updated and new controllers are reset
var newControllers = this . buildOrUpdateControllers ( ) ;
Chart . layoutService . update ( this , this . chart . width , this . chart . height ) ;
// Can only reset the new controllers after the scales have been updated
helpers . each ( newControllers , function ( controller ) {
controller . reset ( ) ;
} ) ;
// Make sure all dataset controllers have correct meta data counts
helpers . each ( this . data . datasets , function ( dataset , datasetIndex ) {
dataset . controller . buildOrUpdateElements ( ) ;
} ) ;
// This will loop through any data and do the appropriate element update for the type
helpers . each ( this . data . datasets , function ( dataset , datasetIndex ) {
dataset . controller . update ( ) ;
} ) ;
this . render ( animationDuration , lazy ) ;
} ,
render : function render ( duration , lazy ) {
if ( this . options . animation && ( ( typeof duration !== 'undefined' && duration !== 0 ) || ( typeof duration === 'undefined' && this . options . animation . duration !== 0 ) ) ) {
var animation = new Chart . Animation ( ) ;
animation . numSteps = ( duration || this . options . animation . duration ) / 16.66 ; //60 fps
animation . easing = this . options . animation . easing ;
// render function
animation . render = function ( chartInstance , animationObject ) {
var easingFunction = helpers . easingEffects [ animationObject . easing ] ;
var stepDecimal = animationObject . currentStep / animationObject . numSteps ;
var easeDecimal = easingFunction ( stepDecimal ) ;
chartInstance . draw ( easeDecimal , stepDecimal , animationObject . currentStep ) ;
} ;
// user events
animation . onAnimationProgress = this . options . animation . onProgress ;
animation . onAnimationComplete = this . options . animation . onComplete ;
Chart . animationService . addAnimation ( this , animation , duration , lazy ) ;
} else {
this . draw ( ) ;
if ( this . options . animation && this . options . animation . onComplete && this . options . animation . onComplete . call ) {
this . options . animation . onComplete . call ( this ) ;
}
}
return this ;
} ,
draw : function ( ease ) {
var easingDecimal = ease || 1 ;
this . clear ( ) ;
// Draw all the scales
helpers . each ( this . boxes , function ( box ) {
box . draw ( this . chartArea ) ;
} , this ) ;
if ( this . scale ) {
this . scale . draw ( ) ;
}
// Clip out the chart area so that anything outside does not draw. This is necessary for zoom and pan to function
this . chart . ctx . save ( ) ;
this . chart . ctx . beginPath ( ) ;
this . chart . ctx . rect ( this . chartArea . left , this . chartArea . top , this . chartArea . right - this . chartArea . left , this . chartArea . bottom - this . chartArea . top ) ;
this . chart . ctx . clip ( ) ;
// Draw each dataset via its respective controller (reversed to support proper line stacking)
helpers . each ( this . data . datasets , function ( dataset , datasetIndex ) {
if ( helpers . isDatasetVisible ( dataset ) ) {
dataset . controller . draw ( ease ) ;
}
} , null , true ) ;
// Restore from the clipping operation
this . chart . ctx . restore ( ) ;
// Finally draw the tooltip
this . tooltip . transition ( easingDecimal ) . draw ( ) ;
} ,
// Get the single element that was clicked on
// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
getElementAtEvent : function ( e ) {
var eventPosition = helpers . getRelativePosition ( e , this . chart ) ;
var elementsArray = [ ] ;
helpers . each ( this . data . datasets , function ( dataset , datasetIndex ) {
if ( helpers . isDatasetVisible ( dataset ) ) {
helpers . each ( dataset . metaData , function ( element , index ) {
if ( element . inRange ( eventPosition . x , eventPosition . y ) ) {
elementsArray . push ( element ) ;
return elementsArray ;
}
} ) ;
}
} ) ;
return elementsArray ;
} ,
getElementsAtEvent : function ( e ) {
var eventPosition = helpers . getRelativePosition ( e , this . chart ) ;
var elementsArray = [ ] ;
var found = ( function ( ) {
if ( this . data . datasets ) {
for ( var i = 0 ; i < this . data . datasets . length ; i ++ ) {
if ( helpers . isDatasetVisible ( this . data . datasets [ i ] ) ) {
for ( var j = 0 ; j < this . data . datasets [ i ] . metaData . length ; j ++ ) {
if ( this . data . datasets [ i ] . metaData [ j ] . inRange ( eventPosition . x , eventPosition . y ) ) {
return this . data . datasets [ i ] . metaData [ j ] ;
}
}
}
}
}
} ) . call ( this ) ;
if ( ! found ) {
return elementsArray ;
}
helpers . each ( this . data . datasets , function ( dataset , dsIndex ) {
if ( helpers . isDatasetVisible ( dataset ) ) {
elementsArray . push ( dataset . metaData [ found . _index ] ) ;
}
} ) ;
return elementsArray ;
} ,
getDatasetAtEvent : function ( e ) {
var elementsArray = this . getElementAtEvent ( e ) ;
if ( elementsArray . length > 0 ) {
elementsArray = this . data . datasets [ elementsArray [ 0 ] . _datasetIndex ] . metaData ;
}
return elementsArray ;
} ,
generateLegend : function generateLegend ( ) {
return this . options . legendCallback ( this ) ;
} ,
destroy : function destroy ( ) {
this . clear ( ) ;
helpers . unbindEvents ( this , this . events ) ;
helpers . removeResizeListener ( this . chart . canvas . parentNode ) ;
// Reset canvas height/width attributes
var canvas = this . chart . canvas ;
canvas . width = this . chart . width ;
canvas . height = this . chart . height ;
// if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here
if ( this . chart . originalDevicePixelRatio !== undefined ) {
this . chart . ctx . scale ( 1 / this . chart . originalDevicePixelRatio , 1 / this . chart . originalDevicePixelRatio ) ;
}
// Reset to the old style since it may have been changed by the device pixel ratio changes
canvas . style . width = this . chart . originalCanvasStyleWidth ;
canvas . style . height = this . chart . originalCanvasStyleHeight ;
delete Chart . instances [ this . id ] ;
} ,
toBase64Image : function toBase64Image ( ) {
return this . chart . canvas . toDataURL . apply ( this . chart . canvas , arguments ) ;
} ,
initToolTip : function initToolTip ( ) {
this . tooltip = new Chart . Tooltip ( {
_chart : this . chart ,
_chartInstance : this ,
_data : this . data ,
_options : this . options
} , this ) ;
} ,
bindEvents : function bindEvents ( ) {
helpers . bindEvents ( this , this . options . events , function ( evt ) {
this . eventHandler ( evt ) ;
} ) ;
} ,
eventHandler : function eventHandler ( e ) {
this . lastActive = this . lastActive || [ ] ;
this . lastTooltipActive = this . lastTooltipActive || [ ] ;
// Find Active Elements for hover and tooltips
if ( e . type === 'mouseout' ) {
this . active = [ ] ;
this . tooltipActive = [ ] ;
} else {
var _this = this ;
var getItemsForMode = function ( mode ) {
switch ( mode ) {
case 'single' :
return _this . getElementAtEvent ( e ) ;
case 'label' :
return _this . getElementsAtEvent ( e ) ;
case 'dataset' :
return _this . getDatasetAtEvent ( e ) ;
default :
return e ;
}
} ;
this . active = getItemsForMode ( this . options . hover . mode ) ;
this . tooltipActive = getItemsForMode ( this . options . tooltips . mode ) ;
}
// 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 ) ;
}
if ( this . legend && this . legend . handleEvent ) {
this . legend . handleEvent ( e ) ;
}
}
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' :
this . data . datasets [ this . lastActive [ 0 ] . _datasetIndex ] . controller . removeHoverStyle ( this . lastActive [ 0 ] , this . lastActive [ 0 ] . _datasetIndex , this . lastActive [ 0 ] . _index ) ;
break ;
case 'label' :
case 'dataset' :
for ( var i = 0 ; i < this . lastActive . length ; i ++ ) {
if ( this . lastActive [ i ] )
this . data . datasets [ this . lastActive [ i ] . _datasetIndex ] . controller . removeHoverStyle ( this . lastActive [ i ] , this . lastActive [ i ] . _datasetIndex , this . lastActive [ i ] . _index ) ;
}
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' :
this . data . datasets [ this . active [ 0 ] . _datasetIndex ] . controller . setHoverStyle ( this . active [ 0 ] ) ;
break ;
case 'label' :
case 'dataset' :
for ( var j = 0 ; j < this . active . length ; j ++ ) {
if ( this . active [ j ] )
this . data . datasets [ this . active [ j ] . _datasetIndex ] . controller . setHoverStyle ( this . active [ j ] ) ;
}
break ;
default :
// Don't change anything
}
}
// Built in Tooltips
if ( this . options . tooltips . enabled || this . options . tooltips . custom ) {
// The usual updates
this . tooltip . initialize ( ) ;
this . tooltip . _active = this . tooltipActive ;
this . tooltip . update ( ) ;
}
// 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 ) ;
helpers . each ( this . tooltipActive , function ( element , index ) {
if ( element !== this . lastTooltipActive [ index ] ) {
changed = true ;
}
} , this ) ;
// If entering, leaving, or changing elements, animate the change via pivot
if ( ( this . lastActive . length !== this . active . length ) ||
( this . lastTooltipActive . length !== this . tooltipActive . length ) ||
changed ) {
this . stop ( ) ;
if ( this . options . tooltips . enabled || this . options . tooltips . custom ) {
this . tooltip . update ( true ) ;
}
// We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more
// memory than necessary.
this . render ( this . options . hover . animationDuration , true ) ;
}
}
// Remember Last Actives
this . lastActive = this . active ;
this . lastTooltipActive = this . tooltipActive ;
return this ;
}
} ) ;
} ;
} , { } ] , 23 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
// Base class for all dataset controllers (line, bar, etc)
Chart . DatasetController = function ( chart , datasetIndex ) {
this . initialize . call ( this , chart , datasetIndex ) ;
} ;
helpers . extend ( Chart . DatasetController . prototype , {
initialize : function ( chart , datasetIndex ) {
this . chart = chart ;
this . index = datasetIndex ;
this . linkScales ( ) ;
this . addElements ( ) ;
} ,
updateIndex : function ( datasetIndex ) {
this . index = datasetIndex ;
} ,
linkScales : function ( ) {
if ( ! this . getDataset ( ) . xAxisID ) {
this . getDataset ( ) . xAxisID = this . chart . options . scales . xAxes [ 0 ] . id ;
}
if ( ! this . getDataset ( ) . yAxisID ) {
this . getDataset ( ) . yAxisID = this . chart . options . scales . yAxes [ 0 ] . id ;
}
} ,
getDataset : function ( ) {
return this . chart . data . datasets [ this . index ] ;
} ,
getScaleForId : function ( scaleID ) {
return this . chart . scales [ scaleID ] ;
} ,
reset : function ( ) {
this . update ( true ) ;
} ,
buildOrUpdateElements : function buildOrUpdateElements ( ) {
// Handle the number of data points changing
var numData = this . getDataset ( ) . data . length ;
var numMetaData = this . getDataset ( ) . metaData . length ;
// Make sure that we handle number of datapoints changing
if ( numData < numMetaData ) {
// Remove excess bars for data points that have been removed
this . getDataset ( ) . metaData . splice ( numData , numMetaData - numData ) ;
} else if ( numData > numMetaData ) {
// Add new elements
for ( var index = numMetaData ; index < numData ; ++ index ) {
this . addElementAndReset ( index ) ;
}
}
} ,
// Controllers should implement the following
addElements : helpers . noop ,
addElementAndReset : helpers . noop ,
draw : helpers . noop ,
removeHoverStyle : helpers . noop ,
setHoverStyle : helpers . noop ,
update : helpers . noop
} ) ;
Chart . DatasetController . extend = helpers . inherits ;
} ;
} , { } ] , 24 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . elements = { } ;
Chart . Element = function ( configuration ) {
helpers . extend ( this , configuration ) ;
this . initialize . apply ( this , arguments ) ;
} ;
helpers . extend ( Chart . Element . prototype , {
initialize : function ( ) { } ,
pivot : function ( ) {
if ( ! this . _view ) {
this . _view = helpers . clone ( this . _model ) ;
}
this . _start = helpers . clone ( this . _view ) ;
return this ;
} ,
transition : function ( ease ) {
if ( ! this . _view ) {
this . _view = helpers . clone ( this . _model ) ;
}
// No animation -> No Transition
if ( ease === 1 ) {
this . _view = this . _model ;
this . _start = null ;
return this ;
}
if ( ! this . _start ) {
this . pivot ( ) ;
}
helpers . each ( this . _model , function ( value , key ) {
if ( key [ 0 ] === '_' || ! this . _model . hasOwnProperty ( key ) ) {
// Only non-underscored properties
}
// Init if doesn't exist
else if ( ! this . _view . hasOwnProperty ( key ) ) {
if ( typeof value === 'number' && ! isNaN ( this . _view [ key ] ) ) {
this . _view [ key ] = value * ease ;
} else {
this . _view [ key ] = value ;
}
}
// No unnecessary computations
else if ( value === this . _view [ key ] ) {
// It's the same! Woohoo!
}
// Color transitions if possible
else if ( typeof value === 'string' ) {
try {
var color = helpers . color ( this . _start [ key ] ) . mix ( helpers . color ( this . _model [ key ] ) , ease ) ;
this . _view [ key ] = color . rgbString ( ) ;
} catch ( err ) {
this . _view [ key ] = value ;
}
}
// Number transitions
else if ( typeof value === 'number' ) {
var startVal = this . _start [ key ] !== undefined && isNaN ( this . _start [ key ] ) === false ? this . _start [ key ] : 0 ;
this . _view [ key ] = ( ( this . _model [ key ] - startVal ) * ease ) + startVal ;
}
// Everything else
else {
this . _view [ key ] = value ;
}
} , this ) ;
return this ;
} ,
tooltipPosition : function ( ) {
return {
x : this . _model . x ,
y : this . _model . y
} ;
} ,
hasValue : function ( ) {
return helpers . isNumber ( this . _model . x ) && helpers . isNumber ( this . _model . y ) ;
}
} ) ;
Chart . Element . extend = helpers . inherits ;
} ;
} , { } ] , 25 : [ function ( require , module , exports ) {
/*global window: false */
/*global document: false */
"use strict" ;
var color = require ( 'chartjs-color' ) ;
module . exports = function ( Chart ) {
//Global Chart helpers object for utility methods and classes
var helpers = Chart . helpers = { } ;
//-- Basic js utility methods
helpers . each = function ( loopable , callback , self , reverse ) {
// Check to see if null or undefined firstly.
var i , len ;
if ( helpers . isArray ( loopable ) ) {
len = loopable . length ;
if ( reverse ) {
for ( i = len - 1 ; i >= 0 ; i -- ) {
callback . call ( self , loopable [ i ] , i ) ;
}
} else {
for ( i = 0 ; i < len ; i ++ ) {
callback . call ( self , loopable [ i ] , i ) ;
}
}
} else if ( typeof loopable === 'object' ) {
var keys = Object . keys ( loopable ) ;
len = keys . length ;
for ( i = 0 ; i < len ; i ++ ) {
callback . call ( self , loopable [ keys [ i ] ] , keys [ i ] ) ;
}
}
} ;
helpers . clone = function ( obj ) {
var objClone = { } ;
helpers . each ( obj , function ( value , key ) {
if ( obj . hasOwnProperty ( key ) ) {
if ( helpers . isArray ( value ) ) {
objClone [ key ] = value . slice ( 0 ) ;
} else if ( typeof value === 'object' && value !== null ) {
objClone [ key ] = helpers . clone ( value ) ;
} else {
objClone [ key ] = value ;
}
}
} ) ;
return objClone ;
} ;
helpers . extend = function ( base ) {
var len = arguments . length ;
var additionalArgs = [ ] ;
for ( var i = 1 ; i < len ; i ++ ) {
additionalArgs . push ( arguments [ i ] ) ;
}
helpers . each ( additionalArgs , function ( extensionObject ) {
helpers . each ( extensionObject , function ( value , key ) {
if ( extensionObject . hasOwnProperty ( key ) ) {
base [ key ] = value ;
}
} ) ;
} ) ;
return base ;
} ;
// Need a special merge function to chart configs since they are now grouped
helpers . configMerge = function ( _base ) {
var base = helpers . clone ( _base ) ;
helpers . each ( Array . prototype . slice . call ( arguments , 1 ) , function ( extension ) {
helpers . each ( extension , function ( value , key ) {
if ( extension . hasOwnProperty ( key ) ) {
if ( key === 'scales' ) {
// Scale config merging is complex. Add out own function here for that
base [ key ] = helpers . scaleMerge ( base . hasOwnProperty ( key ) ? base [ key ] : { } , value ) ;
} else if ( key === 'scale' ) {
// Used in polar area & radar charts since there is only one scale
base [ key ] = helpers . configMerge ( base . hasOwnProperty ( key ) ? base [ key ] : { } , Chart . scaleService . getScaleDefaults ( value . type ) , value ) ;
} else if ( base . hasOwnProperty ( key ) && helpers . isArray ( base [ key ] ) && helpers . isArray ( value ) ) {
// In this case we have an array of objects replacing another array. Rather than doing a strict replace,
// merge. This allows easy scale option merging
var baseArray = base [ key ] ;
helpers . each ( value , function ( valueObj , index ) {
if ( index < baseArray . length ) {
if ( typeof baseArray [ index ] === 'object' && baseArray [ index ] !== null && typeof valueObj === 'object' && valueObj !== null ) {
// Two objects are coming together. Do a merge of them.
baseArray [ index ] = helpers . configMerge ( baseArray [ index ] , valueObj ) ;
} else {
// Just overwrite in this case since there is nothing to merge
baseArray [ index ] = valueObj ;
}
} else {
baseArray . push ( valueObj ) ; // nothing to merge
}
} ) ;
} else if ( base . hasOwnProperty ( key ) && typeof base [ key ] === "object" && base [ key ] !== null && typeof value === "object" ) {
// If we are overwriting an object with an object, do a merge of the properties.
base [ key ] = helpers . configMerge ( base [ key ] , value ) ;
} else {
// can just overwrite the value in this case
base [ key ] = value ;
}
}
} ) ;
} ) ;
return base ;
} ;
helpers . extendDeep = function ( _base ) {
return _extendDeep . apply ( this , arguments ) ;
function _extendDeep ( dst ) {
helpers . each ( arguments , function ( obj ) {
if ( obj !== dst ) {
helpers . each ( obj , function ( value , key ) {
if ( dst [ key ] && dst [ key ] . constructor && dst [ key ] . constructor === Object ) {
_extendDeep ( dst [ key ] , value ) ;
} else {
dst [ key ] = value ;
}
} ) ;
}
} ) ;
return dst ;
}
} ;
helpers . scaleMerge = function ( _base , extension ) {
var base = helpers . clone ( _base ) ;
helpers . each ( extension , function ( value , key ) {
if ( extension . hasOwnProperty ( key ) ) {
if ( key === 'xAxes' || key === 'yAxes' ) {
// These properties are arrays of items
if ( base . hasOwnProperty ( key ) ) {
helpers . each ( value , function ( valueObj , index ) {
var axisType = helpers . getValueOrDefault ( valueObj . type , key === 'xAxes' ? 'category' : 'linear' ) ;
var axisDefaults = Chart . scaleService . getScaleDefaults ( axisType ) ;
if ( index >= base [ key ] . length || ! base [ key ] [ index ] . type ) {
base [ key ] . push ( helpers . configMerge ( axisDefaults , valueObj ) ) ;
} else if ( valueObj . type && valueObj . type !== base [ key ] [ index ] . type ) {
// Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
base [ key ] [ index ] = helpers . configMerge ( base [ key ] [ index ] , axisDefaults , valueObj ) ;
} else {
// Type is the same
base [ key ] [ index ] = helpers . configMerge ( base [ key ] [ index ] , valueObj ) ;
}
} ) ;
} else {
base [ key ] = [ ] ;
helpers . each ( value , function ( valueObj ) {
var axisType = helpers . getValueOrDefault ( valueObj . type , key === 'xAxes' ? 'category' : 'linear' ) ;
base [ key ] . push ( helpers . configMerge ( Chart . scaleService . getScaleDefaults ( axisType ) , valueObj ) ) ;
} ) ;
}
} else if ( base . hasOwnProperty ( key ) && typeof base [ key ] === "object" && base [ key ] !== null && typeof value === "object" ) {
// If we are overwriting an object with an object, do a merge of the properties.
base [ key ] = helpers . configMerge ( base [ key ] , value ) ;
} else {
// can just overwrite the value in this case
base [ key ] = value ;
}
}
} ) ;
return base ;
} ;
helpers . getValueAtIndexOrDefault = function ( value , index , defaultValue ) {
if ( value === undefined || value === null ) {
return defaultValue ;
}
if ( helpers . isArray ( value ) ) {
return index < value . length ? value [ index ] : defaultValue ;
}
return value ;
} ;
helpers . getValueOrDefault = function ( value , defaultValue ) {
return value === undefined ? defaultValue : value ;
} ;
helpers . indexOf = function ( arrayToSearch , item ) {
if ( Array . prototype . indexOf ) {
return arrayToSearch . indexOf ( item ) ;
} else {
for ( var i = 0 ; i < arrayToSearch . length ; i ++ ) {
if ( arrayToSearch [ i ] === item )
return i ;
}
return - 1 ;
}
} ;
helpers . where = function ( collection , filterCallback ) {
var filtered = [ ] ;
helpers . each ( collection , function ( item ) {
if ( filterCallback ( item ) ) {
filtered . push ( item ) ;
}
} ) ;
return filtered ;
} ;
helpers . findIndex = function ( arrayToSearch , callback , thisArg ) {
var index = - 1 ;
if ( Array . prototype . findIndex ) {
index = arrayToSearch . findIndex ( callback , thisArg ) ;
} else {
for ( var i = 0 ; i < arrayToSearch . length ; ++ i ) {
thisArg = thisArg !== undefined ? thisArg : arrayToSearch ;
if ( callback . call ( thisArg , arrayToSearch [ i ] , i , arrayToSearch ) ) {
index = i ;
break ;
}
}
}
return index ;
} ;
helpers . findNextWhere = function ( arrayToSearch , filterCallback , startIndex ) {
// Default to start of the array
if ( startIndex === undefined || startIndex === null ) {
startIndex = - 1 ;
}
for ( var i = startIndex + 1 ; i < arrayToSearch . length ; i ++ ) {
var currentItem = arrayToSearch [ i ] ;
if ( filterCallback ( currentItem ) ) {
return currentItem ;
}
}
} ;
helpers . findPreviousWhere = function ( arrayToSearch , filterCallback , startIndex ) {
// Default to end of the array
if ( startIndex === undefined || startIndex === null ) {
startIndex = arrayToSearch . length ;
}
for ( var i = startIndex - 1 ; i >= 0 ; i -- ) {
var currentItem = arrayToSearch [ i ] ;
if ( filterCallback ( currentItem ) ) {
return currentItem ;
}
}
} ;
helpers . inherits = function ( extensions ) {
//Basic javascript inheritance based on the model created in Backbone.js
var parent = this ;
var ChartElement = ( extensions && extensions . hasOwnProperty ( "constructor" ) ) ? extensions . constructor : function ( ) {
return parent . apply ( this , arguments ) ;
} ;
var Surrogate = function ( ) {
this . constructor = ChartElement ;
} ;
Surrogate . prototype = parent . prototype ;
ChartElement . prototype = new Surrogate ( ) ;
ChartElement . extend = helpers . inherits ;
if ( extensions ) {
helpers . extend ( ChartElement . prototype , extensions ) ;
}
ChartElement . _ _super _ _ = parent . prototype ;
return ChartElement ;
} ;
helpers . noop = function ( ) { } ;
helpers . uid = ( function ( ) {
var id = 0 ;
return function ( ) {
return "chart-" + id ++ ;
} ;
} ) ( ) ;
helpers . warn = function ( str ) {
//Method for warning of errors
if ( console && typeof console . warn === "function" ) {
console . warn ( str ) ;
}
} ;
//-- Math methods
helpers . isNumber = function ( n ) {
return ! isNaN ( parseFloat ( n ) ) && isFinite ( n ) ;
} ;
helpers . almostEquals = function ( x , y , epsilon ) {
return Math . abs ( x - y ) < epsilon ;
} ;
helpers . max = function ( array ) {
return array . reduce ( function ( max , value ) {
if ( ! isNaN ( value ) ) {
return Math . max ( max , value ) ;
} else {
return max ;
}
} , Number . NEGATIVE _INFINITY ) ;
} ;
helpers . min = function ( array ) {
return array . reduce ( function ( min , value ) {
if ( ! isNaN ( value ) ) {
return Math . min ( min , value ) ;
} else {
return min ;
}
} , Number . POSITIVE _INFINITY ) ;
} ;
helpers . sign = function ( x ) {
if ( Math . sign ) {
return Math . sign ( x ) ;
} else {
x = + x ; // convert to a number
if ( x === 0 || isNaN ( x ) ) {
return x ;
}
return x > 0 ? 1 : - 1 ;
}
} ;
helpers . log10 = function ( x ) {
if ( Math . log10 ) {
return Math . log10 ( x ) ;
} else {
return Math . log ( x ) / Math . LN10 ;
}
} ;
helpers . toRadians = function ( degrees ) {
return degrees * ( Math . PI / 180 ) ;
} ;
helpers . toDegrees = function ( radians ) {
return radians * ( 180 / Math . PI ) ;
} ;
// Gets the angle from vertical upright to the point about a centre.
helpers . getAngleFromPoint = function ( centrePoint , anglePoint ) {
var distanceFromXCenter = anglePoint . x - centrePoint . x ,
distanceFromYCenter = anglePoint . y - centrePoint . y ,
radialDistanceFromCenter = Math . sqrt ( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter ) ;
var angle = Math . atan2 ( distanceFromYCenter , distanceFromXCenter ) ;
if ( angle < ( - 0.5 * Math . PI ) ) {
angle += 2.0 * Math . PI ; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
}
return {
angle : angle ,
distance : radialDistanceFromCenter
} ;
} ;
helpers . aliasPixel = function ( pixelWidth ) {
return ( pixelWidth % 2 === 0 ) ? 0 : 0.5 ;
} ;
helpers . splineCurve = function ( firstPoint , middlePoint , afterPoint , t ) {
//Props to Rob Spencer at scaled innovation for his post on splining between points
//http://scaledinnovation.com/analytics/splines/aboutSplines.html
// This function must also respect "skipped" points
var previous = firstPoint . skip ? middlePoint : firstPoint ,
current = middlePoint ,
next = afterPoint . skip ? middlePoint : afterPoint ;
var d01 = Math . sqrt ( Math . pow ( current . x - previous . x , 2 ) + Math . pow ( current . y - previous . y , 2 ) ) ;
var d12 = Math . sqrt ( Math . pow ( next . x - current . x , 2 ) + Math . pow ( next . y - current . y , 2 ) ) ;
var s01 = d01 / ( d01 + d12 ) ;
var s12 = d12 / ( d01 + d12 ) ;
// If all points are the same, s01 & s02 will be inf
s01 = isNaN ( s01 ) ? 0 : s01 ;
s12 = isNaN ( s12 ) ? 0 : s12 ;
var fa = t * s01 ; // scaling factor for triangle Ta
var fb = t * s12 ;
return {
previous : {
x : current . x - fa * ( next . x - previous . x ) ,
y : current . y - fa * ( next . y - previous . y )
} ,
next : {
x : current . x + fb * ( next . x - previous . x ) ,
y : current . y + fb * ( next . y - previous . y )
}
} ;
} ;
helpers . nextItem = function ( collection , index , loop ) {
if ( loop ) {
return index >= collection . length - 1 ? collection [ 0 ] : collection [ index + 1 ] ;
}
return index >= collection . length - 1 ? collection [ collection . length - 1 ] : collection [ index + 1 ] ;
} ;
helpers . previousItem = function ( collection , index , loop ) {
if ( loop ) {
return index <= 0 ? collection [ collection . length - 1 ] : collection [ index - 1 ] ;
}
return index <= 0 ? collection [ 0 ] : collection [ index - 1 ] ;
} ;
// Implementation of the nice number algorithm used in determining where axis labels will go
helpers . niceNum = function ( range , round ) {
var exponent = Math . floor ( helpers . log10 ( range ) ) ;
var fraction = range / Math . pow ( 10 , exponent ) ;
var niceFraction ;
if ( round ) {
if ( fraction < 1.5 ) {
niceFraction = 1 ;
} else if ( fraction < 3 ) {
niceFraction = 2 ;
} else if ( fraction < 7 ) {
niceFraction = 5 ;
} else {
niceFraction = 10 ;
}
} else {
if ( fraction <= 1.0 ) {
niceFraction = 1 ;
} else if ( fraction <= 2 ) {
niceFraction = 2 ;
} else if ( fraction <= 5 ) {
niceFraction = 5 ;
} else {
niceFraction = 10 ;
}
}
return niceFraction * Math . pow ( 10 , exponent ) ;
} ;
//Easing functions adapted from Robert Penner's easing equations
//http://www.robertpenner.com/easing/
var easingEffects = helpers . easingEffects = {
linear : function ( t ) {
return t ;
} ,
easeInQuad : function ( t ) {
return t * t ;
} ,
easeOutQuad : function ( t ) {
return - 1 * t * ( t - 2 ) ;
} ,
easeInOutQuad : function ( t ) {
if ( ( t /= 1 / 2 ) < 1 ) {
return 1 / 2 * t * t ;
}
return - 1 / 2 * ( ( -- t ) * ( t - 2 ) - 1 ) ;
} ,
easeInCubic : function ( t ) {
return t * t * t ;
} ,
easeOutCubic : function ( t ) {
return 1 * ( ( t = t / 1 - 1 ) * t * t + 1 ) ;
} ,
easeInOutCubic : function ( t ) {
if ( ( t /= 1 / 2 ) < 1 ) {
return 1 / 2 * t * t * t ;
}
return 1 / 2 * ( ( t -= 2 ) * t * t + 2 ) ;
} ,
easeInQuart : function ( t ) {
return t * t * t * t ;
} ,
easeOutQuart : function ( t ) {
return - 1 * ( ( t = t / 1 - 1 ) * t * t * t - 1 ) ;
} ,
easeInOutQuart : function ( t ) {
if ( ( t /= 1 / 2 ) < 1 ) {
return 1 / 2 * t * t * t * t ;
}
return - 1 / 2 * ( ( t -= 2 ) * t * t * t - 2 ) ;
} ,
easeInQuint : function ( t ) {
return 1 * ( t /= 1 ) * t * t * t * t ;
} ,
easeOutQuint : function ( t ) {
return 1 * ( ( t = t / 1 - 1 ) * t * t * t * t + 1 ) ;
} ,
easeInOutQuint : function ( t ) {
if ( ( t /= 1 / 2 ) < 1 ) {
return 1 / 2 * t * t * t * t * t ;
}
return 1 / 2 * ( ( t -= 2 ) * t * t * t * t + 2 ) ;
} ,
easeInSine : function ( t ) {
return - 1 * Math . cos ( t / 1 * ( Math . PI / 2 ) ) + 1 ;
} ,
easeOutSine : function ( t ) {
return 1 * Math . sin ( t / 1 * ( Math . PI / 2 ) ) ;
} ,
easeInOutSine : function ( t ) {
return - 1 / 2 * ( Math . cos ( Math . PI * t / 1 ) - 1 ) ;
} ,
easeInExpo : function ( t ) {
return ( t === 0 ) ? 1 : 1 * Math . pow ( 2 , 10 * ( t / 1 - 1 ) ) ;
} ,
easeOutExpo : function ( t ) {
return ( t === 1 ) ? 1 : 1 * ( - Math . pow ( 2 , - 10 * t / 1 ) + 1 ) ;
} ,
easeInOutExpo : function ( t ) {
if ( t === 0 ) {
return 0 ;
}
if ( t === 1 ) {
return 1 ;
}
if ( ( t /= 1 / 2 ) < 1 ) {
return 1 / 2 * Math . pow ( 2 , 10 * ( t - 1 ) ) ;
}
return 1 / 2 * ( - Math . pow ( 2 , - 10 * -- t ) + 2 ) ;
} ,
easeInCirc : function ( t ) {
if ( t >= 1 ) {
return t ;
}
return - 1 * ( Math . sqrt ( 1 - ( t /= 1 ) * t ) - 1 ) ;
} ,
easeOutCirc : function ( t ) {
return 1 * Math . sqrt ( 1 - ( t = t / 1 - 1 ) * t ) ;
} ,
easeInOutCirc : function ( t ) {
if ( ( t /= 1 / 2 ) < 1 ) {
return - 1 / 2 * ( Math . sqrt ( 1 - t * t ) - 1 ) ;
}
return 1 / 2 * ( Math . sqrt ( 1 - ( t -= 2 ) * t ) + 1 ) ;
} ,
easeInElastic : function ( t ) {
var s = 1.70158 ;
var p = 0 ;
var a = 1 ;
if ( t === 0 ) {
return 0 ;
}
if ( ( t /= 1 ) === 1 ) {
return 1 ;
}
if ( ! p ) {
p = 1 * 0.3 ;
}
if ( a < Math . abs ( 1 ) ) {
a = 1 ;
s = p / 4 ;
} else {
s = p / ( 2 * Math . PI ) * Math . asin ( 1 / a ) ;
}
return - ( a * Math . pow ( 2 , 10 * ( t -= 1 ) ) * Math . sin ( ( t * 1 - s ) * ( 2 * Math . PI ) / p ) ) ;
} ,
easeOutElastic : function ( t ) {
var s = 1.70158 ;
var p = 0 ;
var a = 1 ;
if ( t === 0 ) {
return 0 ;
}
if ( ( t /= 1 ) === 1 ) {
return 1 ;
}
if ( ! p ) {
p = 1 * 0.3 ;
}
if ( a < Math . abs ( 1 ) ) {
a = 1 ;
s = p / 4 ;
} else {
s = p / ( 2 * Math . PI ) * Math . asin ( 1 / a ) ;
}
return a * Math . pow ( 2 , - 10 * t ) * Math . sin ( ( t * 1 - s ) * ( 2 * Math . PI ) / p ) + 1 ;
} ,
easeInOutElastic : function ( t ) {
var s = 1.70158 ;
var p = 0 ;
var a = 1 ;
if ( t === 0 ) {
return 0 ;
}
if ( ( t /= 1 / 2 ) === 2 ) {
return 1 ;
}
if ( ! p ) {
p = 1 * ( 0.3 * 1.5 ) ;
}
if ( a < Math . abs ( 1 ) ) {
a = 1 ;
s = p / 4 ;
} else {
s = p / ( 2 * Math . PI ) * Math . asin ( 1 / a ) ;
}
if ( t < 1 ) {
return - 0.5 * ( a * Math . pow ( 2 , 10 * ( t -= 1 ) ) * Math . sin ( ( t * 1 - s ) * ( 2 * Math . PI ) / p ) ) ;
}
return a * Math . pow ( 2 , - 10 * ( t -= 1 ) ) * Math . sin ( ( t * 1 - s ) * ( 2 * Math . PI ) / p ) * 0.5 + 1 ;
} ,
easeInBack : function ( t ) {
var s = 1.70158 ;
return 1 * ( t /= 1 ) * t * ( ( s + 1 ) * t - s ) ;
} ,
easeOutBack : function ( t ) {
var s = 1.70158 ;
return 1 * ( ( t = t / 1 - 1 ) * t * ( ( s + 1 ) * t + s ) + 1 ) ;
} ,
easeInOutBack : function ( t ) {
var s = 1.70158 ;
if ( ( t /= 1 / 2 ) < 1 ) {
return 1 / 2 * ( t * t * ( ( ( s *= ( 1.525 ) ) + 1 ) * t - s ) ) ;
}
return 1 / 2 * ( ( t -= 2 ) * t * ( ( ( s *= ( 1.525 ) ) + 1 ) * t + s ) + 2 ) ;
} ,
easeInBounce : function ( t ) {
return 1 - easingEffects . easeOutBounce ( 1 - t ) ;
} ,
easeOutBounce : function ( t ) {
if ( ( t /= 1 ) < ( 1 / 2.75 ) ) {
return 1 * ( 7.5625 * t * t ) ;
} else if ( t < ( 2 / 2.75 ) ) {
return 1 * ( 7.5625 * ( t -= ( 1.5 / 2.75 ) ) * t + 0.75 ) ;
} else if ( t < ( 2.5 / 2.75 ) ) {
return 1 * ( 7.5625 * ( t -= ( 2.25 / 2.75 ) ) * t + 0.9375 ) ;
} else {
return 1 * ( 7.5625 * ( t -= ( 2.625 / 2.75 ) ) * t + 0.984375 ) ;
}
} ,
easeInOutBounce : function ( t ) {
if ( t < 1 / 2 ) {
return easingEffects . easeInBounce ( t * 2 ) * 0.5 ;
}
return easingEffects . easeOutBounce ( t * 2 - 1 ) * 0.5 + 1 * 0.5 ;
}
} ;
//Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
helpers . requestAnimFrame = ( function ( ) {
return window . requestAnimationFrame ||
window . webkitRequestAnimationFrame ||
window . mozRequestAnimationFrame ||
window . oRequestAnimationFrame ||
window . msRequestAnimationFrame ||
function ( callback ) {
return window . setTimeout ( callback , 1000 / 60 ) ;
} ;
} ) ( ) ;
helpers . cancelAnimFrame = ( function ( ) {
return window . cancelAnimationFrame ||
window . webkitCancelAnimationFrame ||
window . mozCancelAnimationFrame ||
window . oCancelAnimationFrame ||
window . msCancelAnimationFrame ||
function ( callback ) {
return window . clearTimeout ( callback , 1000 / 60 ) ;
} ;
} ) ( ) ;
//-- DOM methods
helpers . getRelativePosition = function ( evt , chart ) {
var mouseX , mouseY ;
var e = evt . originalEvent || evt ,
canvas = evt . currentTarget || evt . srcElement ,
boundingRect = canvas . getBoundingClientRect ( ) ;
if ( e . touches && e . touches . length > 0 ) {
mouseX = e . touches [ 0 ] . clientX ;
mouseY = e . touches [ 0 ] . clientY ;
} else {
mouseX = e . clientX ;
mouseY = e . clientY ;
}
// Scale mouse coordinates into canvas coordinates
// by following the pattern laid out by 'jerryj' in the comments of
// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
var paddingLeft = parseFloat ( helpers . getStyle ( canvas , 'padding-left' ) ) ;
var paddingTop = parseFloat ( helpers . getStyle ( canvas , 'padding-top' ) ) ;
var paddingRight = parseFloat ( helpers . getStyle ( canvas , 'padding-right' ) ) ;
var paddingBottom = parseFloat ( helpers . getStyle ( canvas , 'padding-bottom' ) ) ;
var width = boundingRect . right - boundingRect . left - paddingLeft - paddingRight ;
var height = boundingRect . bottom - boundingRect . top - paddingTop - paddingBottom ;
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
mouseX = Math . round ( ( mouseX - boundingRect . left - paddingLeft ) / ( width ) * canvas . width / chart . currentDevicePixelRatio ) ;
mouseY = Math . round ( ( mouseY - boundingRect . top - paddingTop ) / ( height ) * canvas . height / chart . currentDevicePixelRatio ) ;
return {
x : mouseX ,
y : mouseY
} ;
} ;
helpers . addEvent = function ( node , eventType , method ) {
if ( node . addEventListener ) {
node . addEventListener ( eventType , method ) ;
} else if ( node . attachEvent ) {
node . attachEvent ( "on" + eventType , method ) ;
} else {
node [ "on" + eventType ] = method ;
}
} ;
helpers . removeEvent = function ( node , eventType , handler ) {
if ( node . removeEventListener ) {
node . removeEventListener ( eventType , handler , false ) ;
} else if ( node . detachEvent ) {
node . detachEvent ( "on" + eventType , handler ) ;
} else {
node [ "on" + eventType ] = helpers . noop ;
}
} ;
helpers . bindEvents = function ( chartInstance , arrayOfEvents , handler ) {
// Create the events object if it's not already present
if ( ! chartInstance . events )
chartInstance . events = { } ;
helpers . each ( arrayOfEvents , function ( eventName ) {
chartInstance . events [ eventName ] = function ( ) {
handler . apply ( chartInstance , arguments ) ;
} ;
helpers . addEvent ( chartInstance . chart . canvas , eventName , chartInstance . events [ eventName ] ) ;
} ) ;
} ;
helpers . unbindEvents = function ( chartInstance , arrayOfEvents ) {
helpers . each ( arrayOfEvents , function ( handler , eventName ) {
helpers . removeEvent ( chartInstance . chart . canvas , eventName , handler ) ;
} ) ;
} ;
// Private helper function to convert max-width/max-height values that may be percentages into a number
function parseMaxStyle ( styleValue , node , parentProperty ) {
var valueInPixels ;
if ( typeof ( styleValue ) === 'string' ) {
valueInPixels = parseInt ( styleValue , 10 ) ;
if ( styleValue . indexOf ( '%' ) != - 1 ) {
// percentage * size in dimension
valueInPixels = valueInPixels / 100 * node . parentNode [ parentProperty ] ;
}
} else {
valueInPixels = styleValue ;
}
return valueInPixels ;
}
// Private helper to get a constraint dimension
// @param domNode : the node to check the constraint on
// @param maxStyle : the style that defines the maximum for the direction we are using (max-width / max-height)
// @param percentageProperty : property of parent to use when calculating width as a percentage
function getConstraintDimension ( domNode , maxStyle , percentageProperty ) {
var constrainedDimension ;
var constrainedNode = document . defaultView . getComputedStyle ( domNode ) [ maxStyle ] ;
var constrainedContainer = document . defaultView . getComputedStyle ( domNode . parentNode ) [ maxStyle ] ;
var hasCNode = constrainedNode !== null && constrainedNode !== "none" ;
var hasCContainer = constrainedContainer !== null && constrainedContainer !== "none" ;
if ( hasCNode || hasCContainer ) {
constrainedDimension = Math . min ( ( hasCNode ? parseMaxStyle ( constrainedNode , domNode , percentageProperty ) : Number . POSITIVE _INFINITY ) , ( hasCContainer ? parseMaxStyle ( constrainedContainer , domNode . parentNode , percentageProperty ) : Number . POSITIVE _INFINITY ) ) ;
}
return constrainedDimension ;
}
// returns Number or undefined if no constraint
helpers . getConstraintWidth = function ( domNode ) {
return getConstraintDimension ( domNode , 'max-width' , 'clientWidth' ) ;
} ;
// returns Number or undefined if no constraint
helpers . getConstraintHeight = function ( domNode ) {
return getConstraintDimension ( domNode , 'max-height' , 'clientHeight' ) ;
} ;
helpers . getMaximumWidth = function ( domNode ) {
var container = domNode . parentNode ;
var padding = parseInt ( helpers . getStyle ( container , 'padding-left' ) ) + parseInt ( helpers . getStyle ( container , 'padding-right' ) ) ;
var w = container . clientWidth - padding ;
var cw = helpers . getConstraintWidth ( domNode ) ;
if ( cw !== undefined ) {
w = Math . min ( w , cw ) ;
}
return w ;
} ;
helpers . getMaximumHeight = function ( domNode ) {
var container = domNode . parentNode ;
var padding = parseInt ( helpers . getStyle ( container , 'padding-top' ) ) + parseInt ( helpers . getStyle ( container , 'padding-bottom' ) ) ;
var h = container . clientHeight - padding ;
var ch = helpers . getConstraintHeight ( domNode ) ;
if ( ch !== undefined ) {
h = Math . min ( h , ch ) ;
}
return h ;
} ;
helpers . getStyle = function ( el , property ) {
return el . currentStyle ?
el . currentStyle [ property ] :
document . defaultView . getComputedStyle ( el , null ) . getPropertyValue ( property ) ;
} ;
helpers . retinaScale = function ( chart ) {
var ctx = chart . ctx ;
var width = chart . canvas . width ;
var height = chart . canvas . height ;
var pixelRatio = chart . currentDevicePixelRatio = window . devicePixelRatio || 1 ;
if ( pixelRatio !== 1 ) {
ctx . canvas . height = height * pixelRatio ;
ctx . canvas . width = width * pixelRatio ;
ctx . scale ( pixelRatio , pixelRatio ) ;
// Store the device pixel ratio so that we can go backwards in `destroy`.
// The devicePixelRatio changes with zoom, so there are no guarantees that it is the same
// when destroy is called
chart . originalDevicePixelRatio = chart . originalDevicePixelRatio || pixelRatio ;
}
ctx . canvas . style . width = width + 'px' ;
ctx . canvas . style . height = height + 'px' ;
} ;
//-- Canvas methods
helpers . clear = function ( chart ) {
chart . ctx . clearRect ( 0 , 0 , chart . width , chart . height ) ;
} ;
helpers . fontString = function ( pixelSize , fontStyle , fontFamily ) {
return fontStyle + " " + pixelSize + "px " + fontFamily ;
} ;
helpers . longestText = function ( ctx , font , arrayOfStrings , cache ) {
cache = cache || { } ;
cache . data = cache . data || { } ;
cache . garbageCollect = cache . garbageCollect || [ ] ;
if ( cache . font !== font ) {
cache . data = { } ;
cache . garbageCollect = [ ] ;
cache . font = font ;
}
ctx . font = font ;
var longest = 0 ;
helpers . each ( arrayOfStrings , function ( string ) {
// Undefined strings should not be measured
if ( string !== undefined && string !== null ) {
var textWidth = cache . data [ string ] ;
if ( ! textWidth ) {
textWidth = cache . data [ string ] = ctx . measureText ( string ) . width ;
cache . garbageCollect . push ( string ) ;
}
if ( textWidth > longest ) {
longest = textWidth ;
}
}
} ) ;
var gcLen = cache . garbageCollect . length / 2 ;
if ( gcLen > arrayOfStrings . length ) {
for ( var i = 0 ; i < gcLen ; i ++ ) {
delete cache . data [ cache . garbageCollect [ i ] ] ;
}
cache . garbageCollect . splice ( 0 , gcLen ) ;
}
return longest ;
} ;
helpers . drawRoundedRectangle = function ( ctx , x , y , width , height , radius ) {
ctx . beginPath ( ) ;
ctx . moveTo ( x + radius , y ) ;
ctx . lineTo ( x + width - radius , y ) ;
ctx . quadraticCurveTo ( x + width , y , x + width , y + radius ) ;
ctx . lineTo ( x + width , y + height - radius ) ;
ctx . quadraticCurveTo ( x + width , y + height , x + width - radius , y + height ) ;
ctx . lineTo ( x + radius , y + height ) ;
ctx . quadraticCurveTo ( x , y + height , x , y + height - radius ) ;
ctx . lineTo ( x , y + radius ) ;
ctx . quadraticCurveTo ( x , y , x + radius , y ) ;
ctx . closePath ( ) ;
} ;
helpers . color = function ( c ) {
if ( ! color ) {
console . log ( 'Color.js not found!' ) ;
return c ;
}
/* global CanvasGradient */
if ( c instanceof CanvasGradient ) {
return color ( Chart . defaults . global . defaultColor ) ;
}
return color ( c ) ;
} ;
helpers . addResizeListener = function ( node , callback ) {
// Hide an iframe before the node
var hiddenIframe = document . createElement ( 'iframe' ) ;
var hiddenIframeClass = 'chartjs-hidden-iframe' ;
if ( hiddenIframe . classlist ) {
// can use classlist
hiddenIframe . classlist . add ( hiddenIframeClass ) ;
} else {
hiddenIframe . setAttribute ( 'class' , hiddenIframeClass ) ;
}
// Set the style
hiddenIframe . style . width = '100%' ;
hiddenIframe . style . display = 'block' ;
hiddenIframe . style . border = 0 ;
hiddenIframe . style . height = 0 ;
hiddenIframe . style . margin = 0 ;
hiddenIframe . style . position = 'absolute' ;
hiddenIframe . style . left = 0 ;
hiddenIframe . style . right = 0 ;
hiddenIframe . style . top = 0 ;
hiddenIframe . style . bottom = 0 ;
// Insert the iframe so that contentWindow is available
node . insertBefore ( hiddenIframe , node . firstChild ) ;
( hiddenIframe . contentWindow || hiddenIframe ) . onresize = function ( ) {
if ( callback ) {
callback ( ) ;
}
} ;
} ;
helpers . removeResizeListener = function ( node ) {
var hiddenIframe = node . querySelector ( '.chartjs-hidden-iframe' ) ;
// Remove the resize detect iframe
if ( hiddenIframe ) {
hiddenIframe . parentNode . removeChild ( hiddenIframe ) ;
}
} ;
helpers . isArray = function ( obj ) {
if ( ! Array . isArray ) {
return Object . prototype . toString . call ( obj ) === '[object Array]' ;
}
return Array . isArray ( obj ) ;
} ;
helpers . pushAllIfDefined = function ( element , array ) {
if ( typeof element === "undefined" ) {
return ;
}
if ( helpers . isArray ( element ) ) {
array . push . apply ( array , element ) ;
} else {
array . push ( element ) ;
}
} ;
helpers . isDatasetVisible = function ( dataset ) {
return ! dataset . hidden ;
} ;
helpers . callCallback = function ( fn , args , _tArg ) {
if ( fn && typeof fn . call === 'function' ) {
fn . apply ( _tArg , args ) ;
}
} ;
} ;
} , { "chartjs-color" : 6 } ] , 26 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( ) {
//Occupy the global variable of Chart, and create a simple base class
var Chart = function ( context , config ) {
this . config = config ;
// Support a jQuery'd canvas element
if ( context . length && context [ 0 ] . getContext ) {
context = context [ 0 ] ;
}
// Support a canvas domnode
if ( context . getContext ) {
context = context . getContext ( "2d" ) ;
}
this . ctx = context ;
this . canvas = context . canvas ;
// Figure out what the size of the chart will be.
// If the canvas has a specified width and height, we use those else
// we look to see if the canvas node has a CSS width and height.
// If there is still no height, fill the parent container
this . width = context . canvas . width || parseInt ( Chart . helpers . getStyle ( context . canvas , 'width' ) ) || Chart . helpers . getMaximumWidth ( context . canvas ) ;
this . height = context . canvas . height || parseInt ( Chart . helpers . getStyle ( context . canvas , 'height' ) ) || Chart . helpers . getMaximumHeight ( context . canvas ) ;
this . aspectRatio = this . width / this . height ;
if ( isNaN ( this . aspectRatio ) || isFinite ( this . aspectRatio ) === false ) {
// If the canvas has no size, try and figure out what the aspect ratio will be.
// Some charts prefer square canvases (pie, radar, etc). If that is specified, use that
// else use the canvas default ratio of 2
this . aspectRatio = config . aspectRatio !== undefined ? config . aspectRatio : 2 ;
}
// Store the original style of the element so we can set it back
this . originalCanvasStyleWidth = context . canvas . style . width ;
this . originalCanvasStyleHeight = context . canvas . style . height ;
// High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
Chart . helpers . retinaScale ( this ) ;
if ( config ) {
this . controller = new Chart . Controller ( this ) ;
}
// Always bind this so that if the responsive state changes we still work
var _this = this ;
Chart . helpers . addResizeListener ( context . canvas . parentNode , function ( ) {
if ( _this . controller && _this . controller . config . options . responsive ) {
_this . controller . resize ( ) ;
}
} ) ;
return this . controller ? this . controller : this ;
} ;
//Globally expose the defaults to allow for user updating/changing
Chart . defaults = {
global : {
responsive : true ,
responsiveAnimationDuration : 0 ,
maintainAspectRatio : true ,
events : [ "mousemove" , "mouseout" , "click" , "touchstart" , "touchmove" ] ,
hover : {
onHover : null ,
mode : 'single' ,
animationDuration : 400
} ,
onClick : null ,
defaultColor : 'rgba(0,0,0,0.1)' ,
defaultFontColor : '#666' ,
defaultFontFamily : "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif" ,
defaultFontSize : 12 ,
defaultFontStyle : 'normal' ,
showLines : true ,
// Element defaults defined in element extensions
elements : { } ,
// Legend callback string
legendCallback : function ( chart ) {
var text = [ ] ;
text . push ( '<ul class="' + chart . id + '-legend">' ) ;
for ( var i = 0 ; i < chart . data . datasets . length ; i ++ ) {
text . push ( '<li><span style="background-color:' + chart . data . datasets [ i ] . backgroundColor + '"></span>' ) ;
if ( chart . data . datasets [ i ] . label ) {
text . push ( chart . data . datasets [ i ] . label ) ;
}
text . push ( '</li>' ) ;
}
text . push ( '</ul>' ) ;
return text . join ( "" ) ;
}
}
} ;
return Chart ;
} ;
} , { } ] , 27 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
// The layout service is very self explanatory. It's responsible for the layout within a chart.
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
// It is this service's responsibility of carrying out that layout.
Chart . layoutService = {
defaults : { } ,
// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
addBox : function ( chartInstance , box ) {
if ( ! chartInstance . boxes ) {
chartInstance . boxes = [ ] ;
}
chartInstance . boxes . push ( box ) ;
} ,
removeBox : function ( chartInstance , box ) {
if ( ! chartInstance . boxes ) {
return ;
}
chartInstance . boxes . splice ( chartInstance . boxes . indexOf ( box ) , 1 ) ;
} ,
// The most important function
update : function ( chartInstance , width , height ) {
if ( ! chartInstance ) {
return ;
}
var xPadding = 0 ;
var yPadding = 0 ;
var leftBoxes = helpers . where ( chartInstance . boxes , function ( box ) {
return box . options . position === "left" ;
} ) ;
var rightBoxes = helpers . where ( chartInstance . boxes , function ( box ) {
return box . options . position === "right" ;
} ) ;
var topBoxes = helpers . where ( chartInstance . boxes , function ( box ) {
return box . options . position === "top" ;
} ) ;
var bottomBoxes = helpers . where ( chartInstance . boxes , function ( box ) {
return box . options . position === "bottom" ;
} ) ;
// Boxes that overlay the chartarea such as the radialLinear scale
var chartAreaBoxes = helpers . where ( chartInstance . boxes , function ( box ) {
return box . options . position === "chartArea" ;
} ) ;
function fullWidthSorter ( a , b ) {
}
// Ensure that full width boxes are at the very top / bottom
topBoxes . sort ( function ( a , b ) {
return ( b . options . fullWidth ? 1 : 0 ) - ( a . options . fullWidth ? 1 : 0 ) ;
} ) ;
bottomBoxes . sort ( function ( a , b ) {
return ( a . options . fullWidth ? 1 : 0 ) - ( b . options . fullWidth ? 1 : 0 ) ;
} ) ;
// Essentially we now have any number of boxes on each of the 4 sides.
// Our canvas looks like the following.
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
// B1 is the bottom axis
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
// an error will be thrown.
//
// |----------------------------------------------------|
// | T1 (Full Width) |
// |----------------------------------------------------|
// | | | T2 | |
// | |----|-------------------------------------|----|
// | | | C1 | | C2 | |
// | | |----| |----| |
// | | | | |
// | L1 | L2 | ChartArea (C0) | R1 |
// | | | | |
// | | |----| |----| |
// | | | C3 | | C4 | |
// | |----|-------------------------------------|----|
// | | | B1 | |
// |----------------------------------------------------|
// | B2 (Full Width) |
// |----------------------------------------------------|
//
// What we do to find the best sizing, we do the following
// 1. Determine the minimum size of the chart area.
// 2. Split the remaining width equally between each vertical axis
// 3. Split the remaining height equally between each horizontal axis
// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
// 5. Adjust the sizes of each axis based on it's minimum reported size.
// 6. Refit each axis
// 7. Position each axis in the final location
// 8. Tell the chart the final location of the chart area
// 9. Tell any axes that overlay the chart area the positions of the chart area
// Step 1
var chartWidth = width - ( 2 * xPadding ) ;
var chartHeight = height - ( 2 * yPadding ) ;
var chartAreaWidth = chartWidth / 2 ; // min 50%
var chartAreaHeight = chartHeight / 2 ; // min 50%
// Step 2
var verticalBoxWidth = ( width - chartAreaWidth ) / ( leftBoxes . length + rightBoxes . length ) ;
// Step 3
var horizontalBoxHeight = ( height - chartAreaHeight ) / ( topBoxes . length + bottomBoxes . length ) ;
// Step 4
var maxChartAreaWidth = chartWidth ;
var maxChartAreaHeight = chartHeight ;
var minBoxSizes = [ ] ;
helpers . each ( leftBoxes . concat ( rightBoxes , topBoxes , bottomBoxes ) , getMinimumBoxSize ) ;
function getMinimumBoxSize ( box ) {
var minSize ;
var isHorizontal = box . isHorizontal ( ) ;
if ( isHorizontal ) {
minSize = box . update ( box . options . fullWidth ? chartWidth : maxChartAreaWidth , horizontalBoxHeight ) ;
maxChartAreaHeight -= minSize . height ;
} else {
minSize = box . update ( verticalBoxWidth , chartAreaHeight ) ;
maxChartAreaWidth -= minSize . width ;
}
minBoxSizes . push ( {
horizontal : isHorizontal ,
minSize : minSize ,
box : box
} ) ;
}
// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
// be if the axes are drawn at their minimum sizes.
// Steps 5 & 6
var totalLeftBoxesWidth = xPadding ;
var totalRightBoxesWidth = xPadding ;
var totalTopBoxesHeight = yPadding ;
var totalBottomBoxesHeight = yPadding ;
// Update, and calculate the left and right margins for the horizontal boxes
helpers . each ( leftBoxes . concat ( rightBoxes ) , fitBox ) ;
helpers . each ( leftBoxes , function ( box ) {
totalLeftBoxesWidth += box . width ;
} ) ;
helpers . each ( rightBoxes , function ( box ) {
totalRightBoxesWidth += box . width ;
} ) ;
// Set the Left and Right margins for the horizontal boxes
helpers . each ( topBoxes . concat ( bottomBoxes ) , fitBox ) ;
// Function to fit a box
function fitBox ( box ) {
var minBoxSize = helpers . findNextWhere ( minBoxSizes , function ( minBoxSize ) {
return minBoxSize . box === box ;
} ) ;
if ( minBoxSize ) {
if ( box . isHorizontal ( ) ) {
var scaleMargin = {
left : totalLeftBoxesWidth ,
right : totalRightBoxesWidth ,
top : 0 ,
bottom : 0
} ;
// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
// on the margin. Sometimes they need to increase in size slightly
box . update ( box . options . fullWidth ? chartWidth : maxChartAreaWidth , chartHeight / 2 , scaleMargin ) ;
} else {
box . update ( minBoxSize . minSize . width , maxChartAreaHeight ) ;
}
}
}
// Figure out how much margin is on the top and bottom of the vertical boxes
helpers . each ( topBoxes , function ( box ) {
totalTopBoxesHeight += box . height ;
} ) ;
helpers . each ( bottomBoxes , function ( box ) {
totalBottomBoxesHeight += box . height ;
} ) ;
// Let the left layout know the final margin
helpers . each ( leftBoxes . concat ( rightBoxes ) , finalFitVerticalBox ) ;
function finalFitVerticalBox ( box ) {
var minBoxSize = helpers . findNextWhere ( minBoxSizes , function ( minBoxSize ) {
return minBoxSize . box === box ;
} ) ;
var scaleMargin = {
left : 0 ,
right : 0 ,
top : totalTopBoxesHeight ,
bottom : totalBottomBoxesHeight
} ;
if ( minBoxSize ) {
box . update ( minBoxSize . minSize . width , maxChartAreaHeight , scaleMargin ) ;
}
}
// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
totalLeftBoxesWidth = xPadding ;
totalRightBoxesWidth = xPadding ;
totalTopBoxesHeight = yPadding ;
totalBottomBoxesHeight = yPadding ;
helpers . each ( leftBoxes , function ( box ) {
totalLeftBoxesWidth += box . width ;
} ) ;
helpers . each ( rightBoxes , function ( box ) {
totalRightBoxesWidth += box . width ;
} ) ;
helpers . each ( topBoxes , function ( box ) {
totalTopBoxesHeight += box . height ;
} ) ;
helpers . each ( bottomBoxes , function ( box ) {
totalBottomBoxesHeight += box . height ;
} ) ;
// Figure out if our chart area changed. This would occur if the dataset layout label rotation
// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
// without calling `fit` again
var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight ;
var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth ;
if ( newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight ) {
helpers . each ( leftBoxes , function ( box ) {
box . height = newMaxChartAreaHeight ;
} ) ;
helpers . each ( rightBoxes , function ( box ) {
box . height = newMaxChartAreaHeight ;
} ) ;
helpers . each ( topBoxes , function ( box ) {
box . width = newMaxChartAreaWidth ;
} ) ;
helpers . each ( bottomBoxes , function ( box ) {
box . width = newMaxChartAreaWidth ;
} ) ;
maxChartAreaHeight = newMaxChartAreaHeight ;
maxChartAreaWidth = newMaxChartAreaWidth ;
}
// Step 7 - Position the boxes
var left = xPadding ;
var top = yPadding ;
var right = 0 ;
var bottom = 0 ;
helpers . each ( leftBoxes . concat ( topBoxes ) , placeBox ) ;
// Account for chart width and height
left += maxChartAreaWidth ;
top += maxChartAreaHeight ;
helpers . each ( rightBoxes , placeBox ) ;
helpers . each ( bottomBoxes , placeBox ) ;
function placeBox ( box ) {
if ( box . isHorizontal ( ) ) {
box . left = box . options . fullWidth ? xPadding : totalLeftBoxesWidth ;
box . right = box . options . fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth ;
box . top = top ;
box . bottom = top + box . height ;
// Move to next point
top = box . bottom ;
} else {
box . left = left ;
box . right = left + box . width ;
box . top = totalTopBoxesHeight ;
box . bottom = totalTopBoxesHeight + maxChartAreaHeight ;
// Move to next point
left = box . right ;
}
}
// Step 8
chartInstance . chartArea = {
left : totalLeftBoxesWidth ,
top : totalTopBoxesHeight ,
right : totalLeftBoxesWidth + maxChartAreaWidth ,
bottom : totalTopBoxesHeight + maxChartAreaHeight
} ;
// Step 9
helpers . each ( chartAreaBoxes , function ( box ) {
box . left = chartInstance . chartArea . left ;
box . top = chartInstance . chartArea . top ;
box . right = chartInstance . chartArea . right ;
box . bottom = chartInstance . chartArea . bottom ;
box . update ( maxChartAreaWidth , maxChartAreaHeight ) ;
} ) ;
}
} ;
} ;
} , { } ] , 28 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . legend = {
display : true ,
position : 'top' ,
fullWidth : true , // marks that this box should take the full width of the canvas (pushing down other boxes)
reverse : false ,
// a callback that will handle
onClick : function ( e , legendItem ) {
var dataset = this . chart . data . datasets [ legendItem . datasetIndex ] ;
dataset . hidden = ! dataset . hidden ;
// We hid a dataset ... rerender the chart
this . chart . update ( ) ;
} ,
labels : {
boxWidth : 40 ,
padding : 10 ,
// Generates labels shown in the legend
// Valid properties to return:
// text : text to display
// fillStyle : fill of coloured box
// strokeStyle: stroke of coloured box
// hidden : if this legend item refers to a hidden item
// lineCap : cap style for line
// lineDash
// lineDashOffset :
// lineJoin :
// lineWidth :
generateLabels : function ( data ) {
return helpers . isArray ( data . datasets ) ? data . datasets . map ( function ( dataset , i ) {
return {
text : dataset . label ,
fillStyle : dataset . backgroundColor ,
hidden : dataset . hidden ,
lineCap : dataset . borderCapStyle ,
lineDash : dataset . borderDash ,
lineDashOffset : dataset . borderDashOffset ,
lineJoin : dataset . borderJoinStyle ,
lineWidth : dataset . borderWidth ,
strokeStyle : dataset . borderColor ,
// Below is extra data used for toggling the datasets
datasetIndex : i
} ;
} , this ) : [ ] ;
}
}
} ;
Chart . Legend = Chart . Element . extend ( {
initialize : function ( config ) {
helpers . extend ( this , config ) ;
// Contains hit boxes for each dataset (in dataset order)
this . legendHitBoxes = [ ] ;
// Are we in doughnut mode which has a different data type
this . doughnutMode = false ;
} ,
// These methods are ordered by lifecyle. Utilities then follow.
// Any function defined here is inherited by all legend types.
// Any function can be extended by the legend type
beforeUpdate : helpers . noop ,
update : function ( maxWidth , maxHeight , margins ) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this . beforeUpdate ( ) ;
// Absorb the master measurements
this . maxWidth = maxWidth ;
this . maxHeight = maxHeight ;
this . margins = margins ;
// Dimensions
this . beforeSetDimensions ( ) ;
this . setDimensions ( ) ;
this . afterSetDimensions ( ) ;
// Labels
this . beforeBuildLabels ( ) ;
this . buildLabels ( ) ;
this . afterBuildLabels ( ) ;
// Fit
this . beforeFit ( ) ;
this . fit ( ) ;
this . afterFit ( ) ;
//
this . afterUpdate ( ) ;
return this . minSize ;
} ,
afterUpdate : helpers . noop ,
//
beforeSetDimensions : helpers . noop ,
setDimensions : function ( ) {
// Set the unconstrained dimension before label rotation
if ( this . isHorizontal ( ) ) {
// Reset position before calculating rotation
this . width = this . maxWidth ;
this . left = 0 ;
this . right = this . width ;
} else {
this . height = this . maxHeight ;
// Reset position before calculating rotation
this . top = 0 ;
this . bottom = this . height ;
}
// Reset padding
this . paddingLeft = 0 ;
this . paddingTop = 0 ;
this . paddingRight = 0 ;
this . paddingBottom = 0 ;
// Reset minSize
this . minSize = {
width : 0 ,
height : 0
} ;
} ,
afterSetDimensions : helpers . noop ,
//
beforeBuildLabels : helpers . noop ,
buildLabels : function ( ) {
this . legendItems = this . options . labels . generateLabels . call ( this , this . chart . data ) ;
if ( this . options . reverse ) {
this . legendItems . reverse ( ) ;
}
} ,
afterBuildLabels : helpers . noop ,
//
beforeFit : helpers . noop ,
fit : function ( ) {
var ctx = this . ctx ;
var fontSize = helpers . getValueOrDefault ( this . options . labels . fontSize , Chart . defaults . global . defaultFontSize ) ;
var fontStyle = helpers . getValueOrDefault ( this . options . labels . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var fontFamily = helpers . getValueOrDefault ( this . options . labels . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var labelFont = helpers . fontString ( fontSize , fontStyle , fontFamily ) ;
// Reset hit boxes
this . legendHitBoxes = [ ] ;
// Width
if ( this . isHorizontal ( ) ) {
this . minSize . width = this . maxWidth ; // fill all the width
} else {
this . minSize . width = this . options . display ? 10 : 0 ;
}
// height
if ( this . isHorizontal ( ) ) {
this . minSize . height = this . options . display ? 10 : 0 ;
} else {
this . minSize . height = this . maxHeight ; // fill all the height
}
// Increase sizes here
if ( this . options . display ) {
if ( this . isHorizontal ( ) ) {
// Labels
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
this . lineWidths = [ 0 ] ;
var totalHeight = this . legendItems . length ? fontSize + ( this . options . labels . padding ) : 0 ;
ctx . textAlign = "left" ;
ctx . textBaseline = 'top' ;
ctx . font = labelFont ;
helpers . each ( this . legendItems , function ( legendItem , i ) {
var width = this . options . labels . boxWidth + ( fontSize / 2 ) + ctx . measureText ( legendItem . text ) . width ;
if ( this . lineWidths [ this . lineWidths . length - 1 ] + width + this . options . labels . padding >= this . width ) {
totalHeight += fontSize + ( this . options . labels . padding ) ;
this . lineWidths [ this . lineWidths . length ] = this . left ;
}
// Store the hitbox width and height here. Final position will be updated in `draw`
this . legendHitBoxes [ i ] = {
left : 0 ,
top : 0 ,
width : width ,
height : fontSize
} ;
this . lineWidths [ this . lineWidths . length - 1 ] += width + this . options . labels . padding ;
} , this ) ;
this . minSize . height += totalHeight ;
} else {
// TODO vertical
}
}
this . width = this . minSize . width ;
this . height = this . minSize . height ;
} ,
afterFit : helpers . noop ,
// Shared Methods
isHorizontal : function ( ) {
return this . options . position === "top" || this . options . position === "bottom" ;
} ,
// Actualy draw the legend on the canvas
draw : function ( ) {
if ( this . options . display ) {
var ctx = this . ctx ;
var cursor = {
x : this . left + ( ( this . width - this . lineWidths [ 0 ] ) / 2 ) ,
y : this . top + this . options . labels . padding ,
line : 0
} ;
var fontColor = helpers . getValueOrDefault ( this . options . labels . fontColor , Chart . defaults . global . defaultFontColor ) ;
var fontSize = helpers . getValueOrDefault ( this . options . labels . fontSize , Chart . defaults . global . defaultFontSize ) ;
var fontStyle = helpers . getValueOrDefault ( this . options . labels . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var fontFamily = helpers . getValueOrDefault ( this . options . labels . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var labelFont = helpers . fontString ( fontSize , fontStyle , fontFamily ) ;
// Horizontal
if ( this . isHorizontal ( ) ) {
// Labels
ctx . textAlign = "left" ;
ctx . textBaseline = 'top' ;
ctx . lineWidth = 0.5 ;
ctx . strokeStyle = fontColor ; // for strikethrough effect
ctx . fillStyle = fontColor ; // render in correct colour
ctx . font = labelFont ;
helpers . each ( this . legendItems , function ( legendItem , i ) {
var textWidth = ctx . measureText ( legendItem . text ) . width ;
var width = this . options . labels . boxWidth + ( fontSize / 2 ) + textWidth ;
if ( cursor . x + width >= this . width ) {
cursor . y += fontSize + ( this . options . labels . padding ) ;
cursor . line ++ ;
cursor . x = this . left + ( ( this . width - this . lineWidths [ cursor . line ] ) / 2 ) ;
}
// Set the ctx for the box
ctx . save ( ) ;
var itemOrDefault = function ( item , defaulVal ) {
return item !== undefined ? item : defaulVal ;
} ;
ctx . fillStyle = itemOrDefault ( legendItem . fillStyle , Chart . defaults . global . defaultColor ) ;
ctx . lineCap = itemOrDefault ( legendItem . lineCap , Chart . defaults . global . elements . line . borderCapStyle ) ;
ctx . lineDashOffset = itemOrDefault ( legendItem . lineDashOffset , Chart . defaults . global . elements . line . borderDashOffset ) ;
ctx . lineJoin = itemOrDefault ( legendItem . lineJoin , Chart . defaults . global . elements . line . borderJoinStyle ) ;
ctx . lineWidth = itemOrDefault ( legendItem . lineWidth , Chart . defaults . global . elements . line . borderWidth ) ;
ctx . strokeStyle = itemOrDefault ( legendItem . strokeStyle , Chart . defaults . global . defaultColor ) ;
if ( ctx . setLineDash ) {
// IE 9 and 10 do not support line dash
ctx . setLineDash ( itemOrDefault ( legendItem . lineDash , Chart . defaults . global . elements . line . borderDash ) ) ;
}
// Draw the box
ctx . strokeRect ( cursor . x , cursor . y , this . options . labels . boxWidth , fontSize ) ;
ctx . fillRect ( cursor . x , cursor . y , this . options . labels . boxWidth , fontSize ) ;
ctx . restore ( ) ;
this . legendHitBoxes [ i ] . left = cursor . x ;
this . legendHitBoxes [ i ] . top = cursor . y ;
// Fill the actual label
ctx . fillText ( legendItem . text , this . options . labels . boxWidth + ( fontSize / 2 ) + cursor . x , cursor . y ) ;
if ( legendItem . hidden ) {
// Strikethrough the text if hidden
ctx . beginPath ( ) ;
ctx . lineWidth = 2 ;
ctx . moveTo ( this . options . labels . boxWidth + ( fontSize / 2 ) + cursor . x , cursor . y + ( fontSize / 2 ) ) ;
ctx . lineTo ( this . options . labels . boxWidth + ( fontSize / 2 ) + cursor . x + textWidth , cursor . y + ( fontSize / 2 ) ) ;
ctx . stroke ( ) ;
}
cursor . x += width + ( this . options . labels . padding ) ;
} , this ) ;
} else {
}
}
} ,
// Handle an event
handleEvent : function ( e ) {
var position = helpers . getRelativePosition ( e , this . chart . chart ) ;
if ( position . x >= this . left && position . x <= this . right && position . y >= this . top && position . y <= this . bottom ) {
// See if we are touching one of the dataset boxes
for ( var i = 0 ; i < this . legendHitBoxes . length ; ++ i ) {
var hitBox = this . legendHitBoxes [ i ] ;
if ( position . x >= hitBox . left && position . x <= hitBox . left + hitBox . width && position . y >= hitBox . top && position . y <= hitBox . top + hitBox . height ) {
// Touching an element
if ( this . options . onClick ) {
this . options . onClick . call ( this , e , this . legendItems [ i ] ) ;
}
break ;
}
}
}
}
} ) ;
} ;
} , { } ] , 29 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . scale = {
display : true ,
// grid line settings
gridLines : {
display : true ,
color : "rgba(0, 0, 0, 0.1)" ,
lineWidth : 1 ,
drawOnChartArea : true ,
drawTicks : true ,
zeroLineWidth : 1 ,
zeroLineColor : "rgba(0,0,0,0.25)" ,
offsetGridLines : false
} ,
// scale label
scaleLabel : {
// actual label
labelString : '' ,
// display property
display : false
} ,
// label settings
ticks : {
beginAtZero : false ,
maxRotation : 50 ,
mirror : false ,
padding : 10 ,
reverse : false ,
display : true ,
autoSkip : true ,
autoSkipPadding : 0 ,
callback : function ( value ) {
return '' + value ;
}
}
} ;
Chart . Scale = Chart . Element . extend ( {
// These methods are ordered by lifecyle. Utilities then follow.
// Any function defined here is inherited by all scale types.
// Any function can be extended by the scale type
beforeUpdate : function ( ) {
helpers . callCallback ( this . options . beforeUpdate , [ this ] ) ;
} ,
update : function ( maxWidth , maxHeight , margins ) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this . beforeUpdate ( ) ;
// Absorb the master measurements
this . maxWidth = maxWidth ;
this . maxHeight = maxHeight ;
this . margins = helpers . extend ( {
left : 0 ,
right : 0 ,
top : 0 ,
bottom : 0
} , margins ) ;
// Dimensions
this . beforeSetDimensions ( ) ;
this . setDimensions ( ) ;
this . afterSetDimensions ( ) ;
// Data min/max
this . beforeDataLimits ( ) ;
this . determineDataLimits ( ) ;
this . afterDataLimits ( ) ;
// Ticks
this . beforeBuildTicks ( ) ;
this . buildTicks ( ) ;
this . afterBuildTicks ( ) ;
this . beforeTickToLabelConversion ( ) ;
this . convertTicksToLabels ( ) ;
this . afterTickToLabelConversion ( ) ;
// Tick Rotation
this . beforeCalculateTickRotation ( ) ;
this . calculateTickRotation ( ) ;
this . afterCalculateTickRotation ( ) ;
// Fit
this . beforeFit ( ) ;
this . fit ( ) ;
this . afterFit ( ) ;
//
this . afterUpdate ( ) ;
return this . minSize ;
} ,
afterUpdate : function ( ) {
helpers . callCallback ( this . options . afterUpdate , [ this ] ) ;
} ,
//
beforeSetDimensions : function ( ) {
helpers . callCallback ( this . options . beforeSetDimensions , [ this ] ) ;
} ,
setDimensions : function ( ) {
// Set the unconstrained dimension before label rotation
if ( this . isHorizontal ( ) ) {
// Reset position before calculating rotation
this . width = this . maxWidth ;
this . left = 0 ;
this . right = this . width ;
} else {
this . height = this . maxHeight ;
// Reset position before calculating rotation
this . top = 0 ;
this . bottom = this . height ;
}
// Reset padding
this . paddingLeft = 0 ;
this . paddingTop = 0 ;
this . paddingRight = 0 ;
this . paddingBottom = 0 ;
} ,
afterSetDimensions : function ( ) {
helpers . callCallback ( this . options . afterSetDimensions , [ this ] ) ;
} ,
// Data limits
beforeDataLimits : function ( ) {
helpers . callCallback ( this . options . beforeDataLimits , [ this ] ) ;
} ,
determineDataLimits : helpers . noop ,
afterDataLimits : function ( ) {
helpers . callCallback ( this . options . afterDataLimits , [ this ] ) ;
} ,
//
beforeBuildTicks : function ( ) {
helpers . callCallback ( this . options . beforeBuildTicks , [ this ] ) ;
} ,
buildTicks : helpers . noop ,
afterBuildTicks : function ( ) {
helpers . callCallback ( this . options . afterBuildTicks , [ this ] ) ;
} ,
beforeTickToLabelConversion : function ( ) {
helpers . callCallback ( this . options . beforeTickToLabelConversion , [ this ] ) ;
} ,
convertTicksToLabels : function ( ) {
// Convert ticks to strings
this . ticks = this . ticks . map ( function ( numericalTick , index , ticks ) {
if ( this . options . ticks . userCallback ) {
return this . options . ticks . userCallback ( numericalTick , index , ticks ) ;
}
return this . options . ticks . callback ( numericalTick , index , ticks ) ;
} ,
this ) ;
} ,
afterTickToLabelConversion : function ( ) {
helpers . callCallback ( this . options . afterTickToLabelConversion , [ this ] ) ;
} ,
//
beforeCalculateTickRotation : function ( ) {
helpers . callCallback ( this . options . beforeCalculateTickRotation , [ this ] ) ;
} ,
calculateTickRotation : function ( ) {
//Get the width of each grid by calculating the difference
//between x offsets between 0 and 1.
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
var tickFontStyle = helpers . getValueOrDefault ( this . options . ticks . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var tickFontFamily = helpers . getValueOrDefault ( this . options . ticks . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var tickLabelFont = helpers . fontString ( tickFontSize , tickFontStyle , tickFontFamily ) ;
this . ctx . font = tickLabelFont ;
var firstWidth = this . ctx . measureText ( this . ticks [ 0 ] ) . width ;
var lastWidth = this . ctx . measureText ( this . ticks [ this . ticks . length - 1 ] ) . width ;
var firstRotated ;
this . labelRotation = 0 ;
this . paddingRight = 0 ;
this . paddingLeft = 0 ;
if ( this . options . display ) {
if ( this . isHorizontal ( ) ) {
this . paddingRight = lastWidth / 2 + 3 ;
this . paddingLeft = firstWidth / 2 + 3 ;
if ( ! this . longestTextCache ) {
this . longestTextCache = { } ;
}
var originalLabelWidth = helpers . longestText ( this . ctx , tickLabelFont , this . ticks , this . longestTextCache ) ;
var labelWidth = originalLabelWidth ;
var cosRotation ;
var sinRotation ;
// Allow 3 pixels x2 padding either side for label readability
// only the index matters for a dataset scale, but we want a consistent interface between scales
var tickWidth = this . getPixelForTick ( 1 ) - this . getPixelForTick ( 0 ) - 6 ;
//Max label rotation can be set or default to 90 - also act as a loop counter
while ( labelWidth > tickWidth && this . labelRotation < this . options . ticks . maxRotation ) {
cosRotation = Math . cos ( helpers . toRadians ( this . labelRotation ) ) ;
sinRotation = Math . sin ( helpers . toRadians ( this . labelRotation ) ) ;
firstRotated = cosRotation * firstWidth ;
// We're right aligning the text now.
if ( firstRotated + tickFontSize / 2 > this . yLabelWidth ) {
this . paddingLeft = firstRotated + tickFontSize / 2 ;
}
this . paddingRight = tickFontSize / 2 ;
if ( sinRotation * originalLabelWidth > this . maxHeight ) {
// go back one step
this . labelRotation -- ;
break ;
}
this . labelRotation ++ ;
labelWidth = cosRotation * originalLabelWidth ;
}
}
}
if ( this . margins ) {
this . paddingLeft = Math . max ( this . paddingLeft - this . margins . left , 0 ) ;
this . paddingRight = Math . max ( this . paddingRight - this . margins . right , 0 ) ;
}
} ,
afterCalculateTickRotation : function ( ) {
helpers . callCallback ( this . options . afterCalculateTickRotation , [ this ] ) ;
} ,
//
beforeFit : function ( ) {
helpers . callCallback ( this . options . beforeFit , [ this ] ) ;
} ,
fit : function ( ) {
this . minSize = {
width : 0 ,
height : 0
} ;
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
var tickFontStyle = helpers . getValueOrDefault ( this . options . ticks . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var tickFontFamily = helpers . getValueOrDefault ( this . options . ticks . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var tickLabelFont = helpers . fontString ( tickFontSize , tickFontStyle , tickFontFamily ) ;
var scaleLabelFontSize = helpers . getValueOrDefault ( this . options . scaleLabel . fontSize , Chart . defaults . global . defaultFontSize ) ;
var scaleLabelFontStyle = helpers . getValueOrDefault ( this . options . scaleLabel . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var scaleLabelFontFamily = helpers . getValueOrDefault ( this . options . scaleLabel . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var scaleLabelFont = helpers . fontString ( scaleLabelFontSize , scaleLabelFontStyle , scaleLabelFontFamily ) ;
// Width
if ( this . isHorizontal ( ) ) {
// subtract the margins to line up with the chartArea if we are a full width scale
this . minSize . width = this . isFullWidth ( ) ? this . maxWidth - this . margins . left - this . margins . right : this . maxWidth ;
} else {
this . minSize . width = this . options . gridLines . display && this . options . display ? 10 : 0 ;
}
// height
if ( this . isHorizontal ( ) ) {
this . minSize . height = this . options . gridLines . display && this . options . display ? 10 : 0 ;
} else {
this . minSize . height = this . maxHeight ; // fill all the height
}
// Are we showing a title for the scale?
if ( this . options . scaleLabel . display ) {
if ( this . isHorizontal ( ) ) {
this . minSize . height += ( scaleLabelFontSize * 1.5 ) ;
} else {
this . minSize . width += ( scaleLabelFontSize * 1.5 ) ;
}
}
if ( this . options . ticks . display && this . options . display ) {
// Don't bother fitting the ticks if we are not showing them
if ( ! this . longestTextCache ) {
this . longestTextCache = { } ;
}
var largestTextWidth = helpers . longestText ( this . ctx , tickLabelFont , this . ticks , this . longestTextCache ) ;
if ( this . isHorizontal ( ) ) {
// A horizontal axis is more constrained by the height.
this . longestLabelWidth = largestTextWidth ;
// TODO - improve this calculation
var labelHeight = ( Math . sin ( helpers . toRadians ( this . labelRotation ) ) * this . longestLabelWidth ) + 1.5 * tickFontSize ;
this . minSize . height = Math . min ( this . maxHeight , this . minSize . height + labelHeight ) ;
this . ctx . font = tickLabelFont ;
var firstLabelWidth = this . ctx . measureText ( this . ticks [ 0 ] ) . width ;
var lastLabelWidth = this . ctx . measureText ( this . ticks [ this . ticks . length - 1 ] ) . width ;
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
// by the font height
var cosRotation = Math . cos ( helpers . toRadians ( this . labelRotation ) ) ;
var sinRotation = Math . sin ( helpers . toRadians ( this . labelRotation ) ) ;
this . paddingLeft = this . labelRotation !== 0 ? ( cosRotation * firstLabelWidth ) + 3 : firstLabelWidth / 2 + 3 ; // add 3 px to move away from canvas edges
this . paddingRight = this . labelRotation !== 0 ? ( sinRotation * ( tickFontSize / 2 ) ) + 3 : lastLabelWidth / 2 + 3 ; // when rotated
} else {
// A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
var maxLabelWidth = this . maxWidth - this . minSize . width ;
// Account for padding
if ( ! this . options . ticks . mirror ) {
largestTextWidth += this . options . ticks . padding ;
}
if ( largestTextWidth < maxLabelWidth ) {
// We don't need all the room
this . minSize . width += largestTextWidth ;
} else {
// Expand to max size
this . minSize . width = this . maxWidth ;
}
this . paddingTop = tickFontSize / 2 ;
this . paddingBottom = tickFontSize / 2 ;
}
}
if ( this . margins ) {
this . paddingLeft = Math . max ( this . paddingLeft - this . margins . left , 0 ) ;
this . paddingTop = Math . max ( this . paddingTop - this . margins . top , 0 ) ;
this . paddingRight = Math . max ( this . paddingRight - this . margins . right , 0 ) ;
this . paddingBottom = Math . max ( this . paddingBottom - this . margins . bottom , 0 ) ;
}
this . width = this . minSize . width ;
this . height = this . minSize . height ;
} ,
afterFit : function ( ) {
helpers . callCallback ( this . options . afterFit , [ this ] ) ;
} ,
// Shared Methods
isHorizontal : function ( ) {
return this . options . position === "top" || this . options . position === "bottom" ;
} ,
isFullWidth : function ( ) {
return ( this . options . fullWidth ) ;
} ,
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
getRightValue : function getRightValue ( rawValue ) {
// Null and undefined values first
if ( rawValue === null || typeof ( rawValue ) === 'undefined' ) {
return NaN ;
}
// isNaN(object) returns true, so make sure NaN is checking for a number
if ( typeof ( rawValue ) === 'number' && isNaN ( rawValue ) ) {
return NaN ;
}
// If it is in fact an object, dive in one more level
if ( typeof ( rawValue ) === "object" ) {
if ( rawValue instanceof Date ) {
return rawValue ;
} else {
return getRightValue ( this . isHorizontal ( ) ? rawValue . x : rawValue . y ) ;
}
}
// Value is good, return it
return rawValue ;
} ,
// Used to get the value to display in the tooltip for the data at the given index
// function getLabelForIndex(index, datasetIndex)
getLabelForIndex : helpers . noop ,
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue : helpers . noop ,
// Used for tick location, should
getPixelForTick : function ( index , includeOffset ) {
if ( this . isHorizontal ( ) ) {
var innerWidth = this . width - ( this . paddingLeft + this . paddingRight ) ;
var tickWidth = innerWidth / Math . max ( ( this . ticks . length - ( ( this . options . gridLines . offsetGridLines ) ? 0 : 1 ) ) , 1 ) ;
var pixel = ( tickWidth * index ) + this . paddingLeft ;
if ( includeOffset ) {
pixel += tickWidth / 2 ;
}
var finalVal = this . left + Math . round ( pixel ) ;
finalVal += this . isFullWidth ( ) ? this . margins . left : 0 ;
return finalVal ;
} else {
var innerHeight = this . height - ( this . paddingTop + this . paddingBottom ) ;
return this . top + ( index * ( innerHeight / ( this . ticks . length - 1 ) ) ) ;
}
} ,
// Utility for getting the pixel location of a percentage of scale
getPixelForDecimal : function ( decimal /*, includeOffset*/ ) {
if ( this . isHorizontal ( ) ) {
var innerWidth = this . width - ( this . paddingLeft + this . paddingRight ) ;
var valueOffset = ( innerWidth * decimal ) + this . paddingLeft ;
var finalVal = this . left + Math . round ( valueOffset ) ;
finalVal += this . isFullWidth ( ) ? this . margins . left : 0 ;
return finalVal ;
} else {
return this . top + ( decimal * this . height ) ;
}
} ,
// Actualy draw the scale on the canvas
// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
draw : function ( chartArea ) {
if ( this . options . display ) {
var setContextLineSettings ;
var isRotated = this . labelRotation !== 0 ;
var skipRatio ;
var scaleLabelX ;
var scaleLabelY ;
var useAutoskipper = this . options . ticks . autoSkip ;
// figure out the maximum number of gridlines to show
var maxTicks ;
if ( this . options . ticks . maxTicksLimit ) {
maxTicks = this . options . ticks . maxTicksLimit ;
}
var tickFontColor = helpers . getValueOrDefault ( this . options . ticks . fontColor , Chart . defaults . global . defaultFontColor ) ;
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
var tickFontStyle = helpers . getValueOrDefault ( this . options . ticks . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var tickFontFamily = helpers . getValueOrDefault ( this . options . ticks . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var tickLabelFont = helpers . fontString ( tickFontSize , tickFontStyle , tickFontFamily ) ;
var scaleLabelFontColor = helpers . getValueOrDefault ( this . options . scaleLabel . fontColor , Chart . defaults . global . defaultFontColor ) ;
var scaleLabelFontSize = helpers . getValueOrDefault ( this . options . scaleLabel . fontSize , Chart . defaults . global . defaultFontSize ) ;
var scaleLabelFontStyle = helpers . getValueOrDefault ( this . options . scaleLabel . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var scaleLabelFontFamily = helpers . getValueOrDefault ( this . options . scaleLabel . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var scaleLabelFont = helpers . fontString ( scaleLabelFontSize , scaleLabelFontStyle , scaleLabelFontFamily ) ;
var cosRotation = Math . cos ( helpers . toRadians ( this . labelRotation ) ) ;
var sinRotation = Math . sin ( helpers . toRadians ( this . labelRotation ) ) ;
var longestRotatedLabel = this . longestLabelWidth * cosRotation ;
var rotatedLabelHeight = tickFontSize * sinRotation ;
// Make sure we draw text in the correct color and font
this . ctx . fillStyle = tickFontColor ;
if ( this . isHorizontal ( ) ) {
setContextLineSettings = true ;
var yTickStart = this . options . position === "bottom" ? this . top : this . bottom - 10 ;
var yTickEnd = this . options . position === "bottom" ? this . top + 10 : this . bottom ;
skipRatio = false ;
if ( ( ( longestRotatedLabel / 2 ) + this . options . ticks . autoSkipPadding ) * this . ticks . length > ( this . width - ( this . paddingLeft + this . paddingRight ) ) ) {
skipRatio = 1 + Math . floor ( ( ( ( longestRotatedLabel / 2 ) + this . options . ticks . autoSkipPadding ) * this . ticks . length ) / ( this . width - ( this . paddingLeft + this . paddingRight ) ) ) ;
}
// if they defined a max number of ticks,
// increase skipRatio until that number is met
if ( maxTicks && this . ticks . length > maxTicks ) {
while ( ! skipRatio || this . ticks . length / ( skipRatio || 1 ) > maxTicks ) {
if ( ! skipRatio ) {
skipRatio = 1 ;
}
skipRatio += 1 ;
}
}
if ( ! useAutoskipper ) {
skipRatio = false ;
}
helpers . each ( this . ticks , function ( label , index ) {
// Blank ticks
var isLastTick = this . ticks . length === index + 1 ;
// Since we always show the last tick,we need may need to hide the last shown one before
var shouldSkip = ( skipRatio > 1 && index % skipRatio > 0 ) || ( index % skipRatio === 0 && index + skipRatio > this . ticks . length ) ;
if ( shouldSkip && ! isLastTick || ( label === undefined || label === null ) ) {
return ;
}
var xLineValue = this . getPixelForTick ( index ) ; // xvalues for grid lines
var xLabelValue = this . getPixelForTick ( index , this . options . gridLines . offsetGridLines ) ; // x values for ticks (need to consider offsetLabel option)
if ( this . options . gridLines . display ) {
if ( index === ( typeof this . zeroLineIndex !== 'undefined' ? this . zeroLineIndex : 0 ) ) {
// Draw the first index specially
this . ctx . lineWidth = this . options . gridLines . zeroLineWidth ;
this . ctx . strokeStyle = this . options . gridLines . zeroLineColor ;
setContextLineSettings = true ; // reset next time
} else if ( setContextLineSettings ) {
this . ctx . lineWidth = this . options . gridLines . lineWidth ;
this . ctx . strokeStyle = this . options . gridLines . color ;
setContextLineSettings = false ;
}
xLineValue += helpers . aliasPixel ( this . ctx . lineWidth ) ;
// Draw the label area
this . ctx . beginPath ( ) ;
if ( this . options . gridLines . drawTicks ) {
this . ctx . moveTo ( xLineValue , yTickStart ) ;
this . ctx . lineTo ( xLineValue , yTickEnd ) ;
}
// Draw the chart area
if ( this . options . gridLines . drawOnChartArea ) {
this . ctx . moveTo ( xLineValue , chartArea . top ) ;
this . ctx . lineTo ( xLineValue , chartArea . bottom ) ;
}
// Need to stroke in the loop because we are potentially changing line widths & colours
this . ctx . stroke ( ) ;
}
if ( this . options . ticks . display ) {
this . ctx . save ( ) ;
this . ctx . translate ( xLabelValue , ( isRotated ) ? this . top + 12 : this . options . position === "top" ? this . bottom - 10 : this . top + 10 ) ;
this . ctx . rotate ( helpers . toRadians ( this . labelRotation ) * - 1 ) ;
this . ctx . font = tickLabelFont ;
this . ctx . textAlign = ( isRotated ) ? "right" : "center" ;
this . ctx . textBaseline = ( isRotated ) ? "middle" : this . options . position === "top" ? "bottom" : "top" ;
this . ctx . fillText ( label , 0 , 0 ) ;
this . ctx . restore ( ) ;
}
} , this ) ;
if ( this . options . scaleLabel . display ) {
// Draw the scale label
this . ctx . textAlign = "center" ;
this . ctx . textBaseline = 'middle' ;
this . ctx . fillStyle = scaleLabelFontColor ; // render in correct colour
this . ctx . font = scaleLabelFont ;
scaleLabelX = this . left + ( ( this . right - this . left ) / 2 ) ; // midpoint of the width
scaleLabelY = this . options . position === 'bottom' ? this . bottom - ( scaleLabelFontSize / 2 ) : this . top + ( scaleLabelFontSize / 2 ) ;
this . ctx . fillText ( this . options . scaleLabel . labelString , scaleLabelX , scaleLabelY ) ;
}
} else {
setContextLineSettings = true ;
var xTickStart = this . options . position === "right" ? this . left : this . right - 5 ;
var xTickEnd = this . options . position === "right" ? this . left + 5 : this . right ;
helpers . each ( this . ticks , function ( label , index ) {
// If the callback returned a null or undefined value, do not draw this line
if ( label === undefined || label === null ) {
return ;
}
var yLineValue = this . getPixelForTick ( index ) ; // xvalues for grid lines
if ( this . options . gridLines . display ) {
if ( index === ( typeof this . zeroLineIndex !== 'undefined' ? this . zeroLineIndex : 0 ) ) {
// Draw the first index specially
this . ctx . lineWidth = this . options . gridLines . zeroLineWidth ;
this . ctx . strokeStyle = this . options . gridLines . zeroLineColor ;
setContextLineSettings = true ; // reset next time
} else if ( setContextLineSettings ) {
this . ctx . lineWidth = this . options . gridLines . lineWidth ;
this . ctx . strokeStyle = this . options . gridLines . color ;
setContextLineSettings = false ;
}
yLineValue += helpers . aliasPixel ( this . ctx . lineWidth ) ;
// Draw the label area
this . ctx . beginPath ( ) ;
if ( this . options . gridLines . drawTicks ) {
this . ctx . moveTo ( xTickStart , yLineValue ) ;
this . ctx . lineTo ( xTickEnd , yLineValue ) ;
}
// Draw the chart area
if ( this . options . gridLines . drawOnChartArea ) {
this . ctx . moveTo ( chartArea . left , yLineValue ) ;
this . ctx . lineTo ( chartArea . right , yLineValue ) ;
}
// Need to stroke in the loop because we are potentially changing line widths & colours
this . ctx . stroke ( ) ;
}
if ( this . options . ticks . display ) {
var xLabelValue ;
var yLabelValue = this . getPixelForTick ( index , this . options . gridLines . offsetGridLines ) ; // x values for ticks (need to consider offsetLabel option)
this . ctx . save ( ) ;
if ( this . options . position === "left" ) {
if ( this . options . ticks . mirror ) {
xLabelValue = this . right + this . options . ticks . padding ;
this . ctx . textAlign = "left" ;
} else {
xLabelValue = this . right - this . options . ticks . padding ;
this . ctx . textAlign = "right" ;
}
} else {
// right side
if ( this . options . ticks . mirror ) {
xLabelValue = this . left - this . options . ticks . padding ;
this . ctx . textAlign = "right" ;
} else {
xLabelValue = this . left + this . options . ticks . padding ;
this . ctx . textAlign = "left" ;
}
}
this . ctx . translate ( xLabelValue , yLabelValue ) ;
this . ctx . rotate ( helpers . toRadians ( this . labelRotation ) * - 1 ) ;
this . ctx . font = tickLabelFont ;
this . ctx . textBaseline = "middle" ;
this . ctx . fillText ( label , 0 , 0 ) ;
this . ctx . restore ( ) ;
}
} , this ) ;
if ( this . options . scaleLabel . display ) {
// Draw the scale label
scaleLabelX = this . options . position === 'left' ? this . left + ( scaleLabelFontSize / 2 ) : this . right - ( scaleLabelFontSize / 2 ) ;
scaleLabelY = this . top + ( ( this . bottom - this . top ) / 2 ) ;
var rotation = this . options . position === 'left' ? - 0.5 * Math . PI : 0.5 * Math . PI ;
this . ctx . save ( ) ;
this . ctx . translate ( scaleLabelX , scaleLabelY ) ;
this . ctx . rotate ( rotation ) ;
this . ctx . textAlign = "center" ;
this . ctx . fillStyle = scaleLabelFontColor ; // render in correct colour
this . ctx . font = scaleLabelFont ;
this . ctx . textBaseline = 'middle' ;
this . ctx . fillText ( this . options . scaleLabel . labelString , 0 , 0 ) ;
this . ctx . restore ( ) ;
}
}
// Draw the line at the edge of the axis
this . ctx . lineWidth = this . options . gridLines . lineWidth ;
this . ctx . strokeStyle = this . options . gridLines . color ;
var x1 = this . left ,
x2 = this . right ,
y1 = this . top ,
y2 = this . bottom ;
if ( this . isHorizontal ( ) ) {
y1 = y2 = this . options . position === 'top' ? this . bottom : this . top ;
y1 += helpers . aliasPixel ( this . ctx . lineWidth ) ;
y2 += helpers . aliasPixel ( this . ctx . lineWidth ) ;
} else {
x1 = x2 = this . options . position === 'left' ? this . right : this . left ;
x1 += helpers . aliasPixel ( this . ctx . lineWidth ) ;
x2 += helpers . aliasPixel ( this . ctx . lineWidth ) ;
}
this . ctx . beginPath ( ) ;
this . ctx . moveTo ( x1 , y1 ) ;
this . ctx . lineTo ( x2 , y2 ) ;
this . ctx . stroke ( ) ;
}
}
} ) ;
} ;
} , { } ] , 30 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . scaleService = {
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
// use the new chart options to grab the correct scale
constructors : { } ,
// Use a registration function so that we can move to an ES6 map when we no longer need to support
// old browsers
// Scale config defaults
defaults : { } ,
registerScaleType : function ( type , scaleConstructor , defaults ) {
this . constructors [ type ] = scaleConstructor ;
this . defaults [ type ] = helpers . clone ( defaults ) ;
} ,
getScaleConstructor : function ( type ) {
return this . constructors . hasOwnProperty ( type ) ? this . constructors [ type ] : undefined ;
} ,
getScaleDefaults : function ( type ) {
// Return the scale defaults merged with the global settings so that we always use the latest ones
return this . defaults . hasOwnProperty ( type ) ? helpers . scaleMerge ( Chart . defaults . scale , this . defaults [ type ] ) : { } ;
} ,
addScalesToLayout : function ( chartInstance ) {
// Adds each scale to the chart.boxes array to be sized accordingly
helpers . each ( chartInstance . scales , function ( scale ) {
Chart . layoutService . addBox ( chartInstance , scale ) ;
} ) ;
}
} ;
} ;
} , { } ] , 31 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . title = {
display : false ,
position : 'top' ,
fullWidth : true , // marks that this box should take the full width of the canvas (pushing down other boxes)
fontStyle : 'bold' ,
padding : 10 ,
// actual title
text : ''
} ;
Chart . Title = Chart . Element . extend ( {
initialize : function ( config ) {
helpers . extend ( this , config ) ;
this . options = helpers . configMerge ( Chart . defaults . global . title , config . options ) ;
// Contains hit boxes for each dataset (in dataset order)
this . legendHitBoxes = [ ] ;
} ,
// These methods are ordered by lifecyle. Utilities then follow.
beforeUpdate : helpers . noop ,
update : function ( maxWidth , maxHeight , margins ) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this . beforeUpdate ( ) ;
// Absorb the master measurements
this . maxWidth = maxWidth ;
this . maxHeight = maxHeight ;
this . margins = margins ;
// Dimensions
this . beforeSetDimensions ( ) ;
this . setDimensions ( ) ;
this . afterSetDimensions ( ) ;
// Labels
this . beforeBuildLabels ( ) ;
this . buildLabels ( ) ;
this . afterBuildLabels ( ) ;
// Fit
this . beforeFit ( ) ;
this . fit ( ) ;
this . afterFit ( ) ;
//
this . afterUpdate ( ) ;
return this . minSize ;
} ,
afterUpdate : helpers . noop ,
//
beforeSetDimensions : helpers . noop ,
setDimensions : function ( ) {
// Set the unconstrained dimension before label rotation
if ( this . isHorizontal ( ) ) {
// Reset position before calculating rotation
this . width = this . maxWidth ;
this . left = 0 ;
this . right = this . width ;
} else {
this . height = this . maxHeight ;
// Reset position before calculating rotation
this . top = 0 ;
this . bottom = this . height ;
}
// Reset padding
this . paddingLeft = 0 ;
this . paddingTop = 0 ;
this . paddingRight = 0 ;
this . paddingBottom = 0 ;
// Reset minSize
this . minSize = {
width : 0 ,
height : 0
} ;
} ,
afterSetDimensions : helpers . noop ,
//
beforeBuildLabels : helpers . noop ,
buildLabels : helpers . noop ,
afterBuildLabels : helpers . noop ,
//
beforeFit : helpers . noop ,
fit : function ( ) {
var ctx = this . ctx ;
var fontSize = helpers . getValueOrDefault ( this . options . fontSize , Chart . defaults . global . defaultFontSize ) ;
var fontStyle = helpers . getValueOrDefault ( this . options . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var fontFamily = helpers . getValueOrDefault ( this . options . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var titleFont = helpers . fontString ( fontSize , fontStyle , fontFamily ) ;
// Width
if ( this . isHorizontal ( ) ) {
this . minSize . width = this . maxWidth ; // fill all the width
} else {
this . minSize . width = 0 ;
}
// height
if ( this . isHorizontal ( ) ) {
this . minSize . height = 0 ;
} else {
this . minSize . height = this . maxHeight ; // fill all the height
}
// Increase sizes here
if ( this . isHorizontal ( ) ) {
// Title
if ( this . options . display ) {
this . minSize . height += fontSize + ( this . options . padding * 2 ) ;
}
} else {
if ( this . options . display ) {
this . minSize . width += fontSize + ( this . options . padding * 2 ) ;
}
}
this . width = this . minSize . width ;
this . height = this . minSize . height ;
} ,
afterFit : helpers . noop ,
// Shared Methods
isHorizontal : function ( ) {
return this . options . position === "top" || this . options . position === "bottom" ;
} ,
// Actualy draw the title block on the canvas
draw : function ( ) {
if ( this . options . display ) {
var ctx = this . ctx ;
var titleX , titleY ;
var fontColor = helpers . getValueOrDefault ( this . options . fontColor , Chart . defaults . global . defaultFontColor ) ;
var fontSize = helpers . getValueOrDefault ( this . options . fontSize , Chart . defaults . global . defaultFontSize ) ;
var fontStyle = helpers . getValueOrDefault ( this . options . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var fontFamily = helpers . getValueOrDefault ( this . options . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var titleFont = helpers . fontString ( fontSize , fontStyle , fontFamily ) ;
ctx . fillStyle = fontColor ; // render in correct colour
ctx . font = titleFont ;
// Horizontal
if ( this . isHorizontal ( ) ) {
// Title
ctx . textAlign = "center" ;
ctx . textBaseline = 'middle' ;
titleX = this . left + ( ( this . right - this . left ) / 2 ) ; // midpoint of the width
titleY = this . top + ( ( this . bottom - this . top ) / 2 ) ; // midpoint of the height
ctx . fillText ( this . options . text , titleX , titleY ) ;
} else {
// Title
titleX = this . options . position === 'left' ? this . left + ( fontSize / 2 ) : this . right - ( fontSize / 2 ) ;
titleY = this . top + ( ( this . bottom - this . top ) / 2 ) ;
var rotation = this . options . position === 'left' ? - 0.5 * Math . PI : 0.5 * Math . PI ;
ctx . save ( ) ;
ctx . translate ( titleX , titleY ) ;
ctx . rotate ( rotation ) ;
ctx . textAlign = "center" ;
ctx . textBaseline = 'middle' ;
ctx . fillText ( this . options . text , 0 , 0 ) ;
ctx . restore ( ) ;
}
}
}
} ) ;
} ;
} , { } ] , 32 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . tooltips = {
enabled : true ,
custom : null ,
mode : 'single' ,
backgroundColor : "rgba(0,0,0,0.8)" ,
titleFontStyle : "bold" ,
titleSpacing : 2 ,
titleMarginBottom : 6 ,
titleColor : "#fff" ,
titleAlign : "left" ,
bodySpacing : 2 ,
bodyColor : "#fff" ,
bodyAlign : "left" ,
footerFontStyle : "bold" ,
footerSpacing : 2 ,
footerMarginTop : 6 ,
footerColor : "#fff" ,
footerAlign : "left" ,
yPadding : 6 ,
xPadding : 6 ,
yAlign : 'center' ,
xAlign : 'center' ,
caretSize : 5 ,
cornerRadius : 6 ,
multiKeyBackground : '#fff' ,
callbacks : {
// Args are: (tooltipItems, data)
beforeTitle : helpers . noop ,
title : function ( tooltipItems , data ) {
// Pick first xLabel for now
var title = '' ;
if ( tooltipItems . length > 0 ) {
if ( tooltipItems [ 0 ] . xLabel ) {
title = tooltipItems [ 0 ] . xLabel ;
} else if ( data . labels . length > 0 && tooltipItems [ 0 ] . index < data . labels . length ) {
title = data . labels [ tooltipItems [ 0 ] . index ] ;
}
}
return title ;
} ,
afterTitle : helpers . noop ,
// Args are: (tooltipItems, data)
beforeBody : helpers . noop ,
// Args are: (tooltipItem, data)
beforeLabel : helpers . noop ,
label : function ( tooltipItem , data ) {
var datasetLabel = data . datasets [ tooltipItem . datasetIndex ] . label || '' ;
return datasetLabel + ': ' + tooltipItem . yLabel ;
} ,
afterLabel : helpers . noop ,
// Args are: (tooltipItems, data)
afterBody : helpers . noop ,
// Args are: (tooltipItems, data)
beforeFooter : helpers . noop ,
footer : helpers . noop ,
afterFooter : helpers . noop
}
} ;
// Helper to push or concat based on if the 2nd parameter is an array or not
function pushOrConcat ( base , toPush ) {
if ( toPush ) {
if ( helpers . isArray ( toPush ) ) {
base = base . concat ( toPush ) ;
} else {
base . push ( toPush ) ;
}
}
return base ;
}
Chart . Tooltip = Chart . Element . extend ( {
initialize : function ( ) {
var options = this . _options ;
helpers . extend ( this , {
_model : {
// Positioning
xPadding : options . tooltips . xPadding ,
yPadding : options . tooltips . yPadding ,
xAlign : options . tooltips . yAlign ,
yAlign : options . tooltips . xAlign ,
// Body
bodyColor : options . tooltips . bodyColor ,
_bodyFontFamily : helpers . getValueOrDefault ( options . tooltips . bodyFontFamily , Chart . defaults . global . defaultFontFamily ) ,
_bodyFontStyle : helpers . getValueOrDefault ( options . tooltips . bodyFontStyle , Chart . defaults . global . defaultFontStyle ) ,
_bodyAlign : options . tooltips . bodyAlign ,
bodyFontSize : helpers . getValueOrDefault ( options . tooltips . bodyFontSize , Chart . defaults . global . defaultFontSize ) ,
bodySpacing : options . tooltips . bodySpacing ,
// Title
titleColor : options . tooltips . titleColor ,
_titleFontFamily : helpers . getValueOrDefault ( options . tooltips . titleFontFamily , Chart . defaults . global . defaultFontFamily ) ,
_titleFontStyle : helpers . getValueOrDefault ( options . tooltips . titleFontStyle , Chart . defaults . global . defaultFontStyle ) ,
titleFontSize : helpers . getValueOrDefault ( options . tooltips . titleFontSize , Chart . defaults . global . defaultFontSize ) ,
_titleAlign : options . tooltips . titleAlign ,
titleSpacing : options . tooltips . titleSpacing ,
titleMarginBottom : options . tooltips . titleMarginBottom ,
// Footer
footerColor : options . tooltips . footerColor ,
_footerFontFamily : helpers . getValueOrDefault ( options . tooltips . footerFontFamily , Chart . defaults . global . defaultFontFamily ) ,
_footerFontStyle : helpers . getValueOrDefault ( options . tooltips . footerFontStyle , Chart . defaults . global . defaultFontStyle ) ,
footerFontSize : helpers . getValueOrDefault ( options . tooltips . footerFontSize , Chart . defaults . global . defaultFontSize ) ,
_footerAlign : options . tooltips . footerAlign ,
footerSpacing : options . tooltips . footerSpacing ,
footerMarginTop : options . tooltips . footerMarginTop ,
// Appearance
caretSize : options . tooltips . caretSize ,
cornerRadius : options . tooltips . cornerRadius ,
backgroundColor : options . tooltips . backgroundColor ,
opacity : 0 ,
legendColorBackground : options . tooltips . multiKeyBackground
}
} ) ;
} ,
// Get the title
// Args are: (tooltipItem, data)
getTitle : function ( ) {
var beforeTitle = this . _options . tooltips . callbacks . beforeTitle . apply ( this , arguments ) ,
title = this . _options . tooltips . callbacks . title . apply ( this , arguments ) ,
afterTitle = this . _options . tooltips . callbacks . afterTitle . apply ( this , arguments ) ;
var lines = [ ] ;
lines = pushOrConcat ( lines , beforeTitle ) ;
lines = pushOrConcat ( lines , title ) ;
lines = pushOrConcat ( lines , afterTitle ) ;
return lines ;
} ,
// Args are: (tooltipItem, data)
getBeforeBody : function ( ) {
var lines = this . _options . tooltips . callbacks . beforeBody . apply ( this , arguments ) ;
return helpers . isArray ( lines ) ? lines : lines !== undefined ? [ lines ] : [ ] ;
} ,
// Args are: (tooltipItem, data)
getBody : function ( tooltipItems , data ) {
var lines = [ ] ;
helpers . each ( tooltipItems , function ( bodyItem ) {
helpers . pushAllIfDefined ( this . _options . tooltips . callbacks . beforeLabel . call ( this , bodyItem , data ) , lines ) ;
helpers . pushAllIfDefined ( this . _options . tooltips . callbacks . label . call ( this , bodyItem , data ) , lines ) ;
helpers . pushAllIfDefined ( this . _options . tooltips . callbacks . afterLabel . call ( this , bodyItem , data ) , lines ) ;
} , this ) ;
return lines ;
} ,
// Args are: (tooltipItem, data)
getAfterBody : function ( ) {
var lines = this . _options . tooltips . callbacks . afterBody . apply ( this , arguments ) ;
return helpers . isArray ( lines ) ? lines : lines !== undefined ? [ lines ] : [ ] ;
} ,
// Get the footer and beforeFooter and afterFooter lines
// Args are: (tooltipItem, data)
getFooter : function ( ) {
var beforeFooter = this . _options . tooltips . callbacks . beforeFooter . apply ( this , arguments ) ;
var footer = this . _options . tooltips . callbacks . footer . apply ( this , arguments ) ;
var afterFooter = this . _options . tooltips . callbacks . afterFooter . apply ( this , arguments ) ;
var lines = [ ] ;
lines = pushOrConcat ( lines , beforeFooter ) ;
lines = pushOrConcat ( lines , footer ) ;
lines = pushOrConcat ( lines , afterFooter ) ;
return lines ;
} ,
getAveragePosition : function ( elements ) {
if ( ! elements . length ) {
return false ;
}
var xPositions = [ ] ;
var yPositions = [ ] ;
helpers . each ( elements , function ( el ) {
if ( el ) {
var pos = el . tooltipPosition ( ) ;
xPositions . push ( pos . x ) ;
yPositions . push ( pos . y ) ;
}
} ) ;
var x = 0 ,
y = 0 ;
for ( var i = 0 ; i < xPositions . length ; i ++ ) {
x += xPositions [ i ] ;
y += yPositions [ i ] ;
}
return {
x : Math . round ( x / xPositions . length ) ,
y : Math . round ( y / xPositions . length )
} ;
} ,
update : function ( changed ) {
if ( this . _active . length ) {
this . _model . opacity = 1 ;
var element = this . _active [ 0 ] ,
labelColors = [ ] ,
tooltipPosition ;
var tooltipItems = [ ] ;
if ( this . _options . tooltips . mode === 'single' ) {
var yScale = element . _yScale || element . _scale ; // handle radar || polarArea charts
tooltipItems . push ( {
xLabel : element . _xScale ? element . _xScale . getLabelForIndex ( element . _index , element . _datasetIndex ) : '' ,
yLabel : yScale ? yScale . getLabelForIndex ( element . _index , element . _datasetIndex ) : '' ,
index : element . _index ,
datasetIndex : element . _datasetIndex
} ) ;
tooltipPosition = this . getAveragePosition ( this . _active ) ;
} else {
helpers . each ( this . _data . datasets , function ( dataset , datasetIndex ) {
if ( ! helpers . isDatasetVisible ( dataset ) ) {
return ;
}
var currentElement = dataset . metaData [ element . _index ] ;
if ( currentElement ) {
var yScale = element . _yScale || element . _scale ; // handle radar || polarArea charts
tooltipItems . push ( {
xLabel : currentElement . _xScale ? currentElement . _xScale . getLabelForIndex ( currentElement . _index , currentElement . _datasetIndex ) : '' ,
yLabel : yScale ? yScale . getLabelForIndex ( currentElement . _index , currentElement . _datasetIndex ) : '' ,
index : element . _index ,
datasetIndex : datasetIndex
} ) ;
}
} , null , element . _yScale . options . stacked ) ;
helpers . each ( this . _active , function ( active ) {
if ( active ) {
labelColors . push ( {
borderColor : active . _view . borderColor ,
backgroundColor : active . _view . backgroundColor
} ) ;
}
} , null , element . _yScale . options . stacked ) ;
tooltipPosition = this . getAveragePosition ( this . _active ) ;
tooltipPosition . y = this . _active [ 0 ] . _yScale . getPixelForDecimal ( 0.5 ) ;
}
// Build the Text Lines
helpers . extend ( this . _model , {
title : this . getTitle ( tooltipItems , this . _data ) ,
beforeBody : this . getBeforeBody ( tooltipItems , this . _data ) ,
body : this . getBody ( tooltipItems , this . _data ) ,
afterBody : this . getAfterBody ( tooltipItems , this . _data ) ,
footer : this . getFooter ( tooltipItems , this . _data )
} ) ;
helpers . extend ( this . _model , {
x : Math . round ( tooltipPosition . x ) ,
y : Math . round ( tooltipPosition . y ) ,
caretPadding : helpers . getValueOrDefault ( tooltipPosition . padding , 2 ) ,
labelColors : labelColors
} ) ;
// We need to determine alignment of
var tooltipSize = this . getTooltipSize ( this . _model ) ;
this . determineAlignment ( tooltipSize ) ; // Smart Tooltip placement to stay on the canvas
helpers . extend ( this . _model , this . getBackgroundPoint ( this . _model , tooltipSize ) ) ;
} else {
this . _model . opacity = 0 ;
}
if ( changed && this . _options . tooltips . custom ) {
this . _options . tooltips . custom . call ( this , this . _model ) ;
}
return this ;
} ,
getTooltipSize : function getTooltipSize ( vm ) {
var ctx = this . _chart . ctx ;
var size = {
height : vm . yPadding * 2 , // Tooltip Padding
width : 0
} ;
var combinedBodyLength = vm . body . length + vm . beforeBody . length + vm . afterBody . length ;
size . height += vm . title . length * vm . titleFontSize ; // Title Lines
size . height += ( vm . title . length - 1 ) * vm . titleSpacing ; // Title Line Spacing
size . height += vm . title . length ? vm . titleMarginBottom : 0 ; // Title's bottom Margin
size . height += combinedBodyLength * vm . bodyFontSize ; // Body Lines
size . height += combinedBodyLength ? ( combinedBodyLength - 1 ) * vm . bodySpacing : 0 ; // Body Line Spacing
size . height += vm . footer . length ? vm . footerMarginTop : 0 ; // Footer Margin
size . height += vm . footer . length * ( vm . footerFontSize ) ; // Footer Lines
size . height += vm . footer . length ? ( vm . footer . length - 1 ) * vm . footerSpacing : 0 ; // Footer Line Spacing
// Width
ctx . font = helpers . fontString ( vm . titleFontSize , vm . _titleFontStyle , vm . _titleFontFamily ) ;
helpers . each ( vm . title , function ( line ) {
size . width = Math . max ( size . width , ctx . measureText ( line ) . width ) ;
} ) ;
ctx . font = helpers . fontString ( vm . bodyFontSize , vm . _bodyFontStyle , vm . _bodyFontFamily ) ;
helpers . each ( vm . beforeBody . concat ( vm . afterBody ) , function ( line ) {
size . width = Math . max ( size . width , ctx . measureText ( line ) . width ) ;
} ) ;
helpers . each ( vm . body , function ( line ) {
size . width = Math . max ( size . width , ctx . measureText ( line ) . width + ( this . _options . tooltips . mode !== 'single' ? ( vm . bodyFontSize + 2 ) : 0 ) ) ;
} , this ) ;
ctx . font = helpers . fontString ( vm . footerFontSize , vm . _footerFontStyle , vm . _footerFontFamily ) ;
helpers . each ( vm . footer , function ( line ) {
size . width = Math . max ( size . width , ctx . measureText ( line ) . width ) ;
} ) ;
size . width += 2 * vm . xPadding ;
return size ;
} ,
determineAlignment : function determineAlignment ( size ) {
if ( this . _model . y < size . height ) {
this . _model . yAlign = 'top' ;
} else if ( this . _model . y > ( this . _chart . height - size . height ) ) {
this . _model . yAlign = 'bottom' ;
}
var lf , rf ; // functions to determine left, right alignment
var olf , orf ; // functions to determine if left/right alignment causes tooltip to go outside chart
var yf ; // function to get the y alignment if the tooltip goes outside of the left or right edges
var _this = this ;
var midX = ( this . _chartInstance . chartArea . left + this . _chartInstance . chartArea . right ) / 2 ;
var midY = ( this . _chartInstance . chartArea . top + this . _chartInstance . chartArea . bottom ) / 2 ;
if ( this . _model . yAlign === 'center' ) {
lf = function ( x ) {
return x <= midX ;
} ;
rf = function ( x ) {
return x > midX ;
} ;
} else {
lf = function ( x ) {
return x <= ( size . width / 2 ) ;
} ;
rf = function ( x ) {
return x >= ( _this . _chart . width - ( size . width / 2 ) ) ;
} ;
}
olf = function ( x ) {
return x + size . width > _this . _chart . width ;
} ;
orf = function ( x ) {
return x - size . width < 0 ;
} ;
yf = function ( y ) {
return y <= midY ? 'top' : 'bottom' ;
} ;
if ( lf ( this . _model . x ) ) {
this . _model . xAlign = 'left' ;
// Is tooltip too wide and goes over the right side of the chart.?
if ( olf ( this . _model . x ) ) {
this . _model . xAlign = 'center' ;
this . _model . yAlign = yf ( this . _model . y ) ;
}
} else if ( rf ( this . _model . x ) ) {
this . _model . xAlign = 'right' ;
// Is tooltip too wide and goes outside left edge of canvas?
if ( orf ( this . _model . x ) ) {
this . _model . xAlign = 'center' ;
this . _model . yAlign = yf ( this . _model . y ) ;
}
}
} ,
getBackgroundPoint : function getBackgroundPoint ( vm , size ) {
// Background Position
var pt = {
x : vm . x ,
y : vm . y
} ;
if ( vm . xAlign === 'right' ) {
pt . x -= size . width ;
} else if ( vm . xAlign === 'center' ) {
pt . x -= ( size . width / 2 ) ;
}
if ( vm . yAlign === 'top' ) {
pt . y += vm . caretPadding + vm . caretSize ;
} else if ( vm . yAlign === 'bottom' ) {
pt . y -= size . height + vm . caretPadding + vm . caretSize ;
} else {
pt . y -= ( size . height / 2 ) ;
}
if ( vm . yAlign === 'center' ) {
if ( vm . xAlign === 'left' ) {
pt . x += vm . caretPadding + vm . caretSize ;
} else if ( vm . xAlign === 'right' ) {
pt . x -= vm . caretPadding + vm . caretSize ;
}
} else {
if ( vm . xAlign === 'left' ) {
pt . x -= vm . cornerRadius + vm . caretPadding ;
} else if ( vm . xAlign === 'right' ) {
pt . x += vm . cornerRadius + vm . caretPadding ;
}
}
return pt ;
} ,
drawCaret : function drawCaret ( tooltipPoint , size , opacity , caretPadding ) {
var vm = this . _view ;
var ctx = this . _chart . ctx ;
var x1 , x2 , x3 ;
var y1 , y2 , y3 ;
if ( vm . yAlign === 'center' ) {
// Left or right side
if ( vm . xAlign === 'left' ) {
x1 = tooltipPoint . x ;
x2 = x1 - vm . caretSize ;
x3 = x1 ;
} else {
x1 = tooltipPoint . x + size . width ;
x2 = x1 + vm . caretSize ;
x3 = x1 ;
}
y2 = tooltipPoint . y + ( size . height / 2 ) ;
y1 = y2 - vm . caretSize ;
y3 = y2 + vm . caretSize ;
} else {
if ( vm . xAlign === 'left' ) {
x1 = tooltipPoint . x + vm . cornerRadius ;
x2 = x1 + vm . caretSize ;
x3 = x2 + vm . caretSize ;
} else if ( vm . xAlign === 'right' ) {
x1 = tooltipPoint . x + size . width - vm . cornerRadius ;
x2 = x1 - vm . caretSize ;
x3 = x2 - vm . caretSize ;
} else {
x2 = tooltipPoint . x + ( size . width / 2 ) ;
x1 = x2 - vm . caretSize ;
x3 = x2 + vm . caretSize ;
}
if ( vm . yAlign === 'top' ) {
y1 = tooltipPoint . y ;
y2 = y1 - vm . caretSize ;
y3 = y1 ;
} else {
y1 = tooltipPoint . y + size . height ;
y2 = y1 + vm . caretSize ;
y3 = y1 ;
}
}
var bgColor = helpers . color ( vm . backgroundColor ) ;
ctx . fillStyle = bgColor . alpha ( opacity * bgColor . alpha ( ) ) . rgbString ( ) ;
ctx . beginPath ( ) ;
ctx . moveTo ( x1 , y1 ) ;
ctx . lineTo ( x2 , y2 ) ;
ctx . lineTo ( x3 , y3 ) ;
ctx . closePath ( ) ;
ctx . fill ( ) ;
} ,
drawTitle : function drawTitle ( pt , vm , ctx , opacity ) {
if ( vm . title . length ) {
ctx . textAlign = vm . _titleAlign ;
ctx . textBaseline = "top" ;
var titleColor = helpers . color ( vm . titleColor ) ;
ctx . fillStyle = titleColor . alpha ( opacity * titleColor . alpha ( ) ) . rgbString ( ) ;
ctx . font = helpers . fontString ( vm . titleFontSize , vm . _titleFontStyle , vm . _titleFontFamily ) ;
helpers . each ( vm . title , function ( title , i ) {
ctx . fillText ( title , pt . x , pt . y ) ;
pt . y += vm . titleFontSize + vm . titleSpacing ; // Line Height and spacing
if ( i + 1 === vm . title . length ) {
pt . y += vm . titleMarginBottom - vm . titleSpacing ; // If Last, add margin, remove spacing
}
} ) ;
}
} ,
drawBody : function drawBody ( pt , vm , ctx , opacity ) {
ctx . textAlign = vm . _bodyAlign ;
ctx . textBaseline = "top" ;
var bodyColor = helpers . color ( vm . bodyColor ) ;
ctx . fillStyle = bodyColor . alpha ( opacity * bodyColor . alpha ( ) ) . rgbString ( ) ;
ctx . font = helpers . fontString ( vm . bodyFontSize , vm . _bodyFontStyle , vm . _bodyFontFamily ) ;
// Before Body
helpers . each ( vm . beforeBody , function ( beforeBody ) {
ctx . fillText ( beforeBody , pt . x , pt . y ) ;
pt . y += vm . bodyFontSize + vm . bodySpacing ;
} ) ;
helpers . each ( vm . body , function ( body , i ) {
// Draw Legend-like boxes if needed
if ( this . _options . tooltips . mode !== 'single' ) {
// Fill a white rect so that colours merge nicely if the opacity is < 1
ctx . fillStyle = helpers . color ( vm . legendColorBackground ) . alpha ( opacity ) . rgbaString ( ) ;
ctx . fillRect ( pt . x , pt . y , vm . bodyFontSize , vm . bodyFontSize ) ;
// Border
ctx . strokeStyle = helpers . color ( vm . labelColors [ i ] . borderColor ) . alpha ( opacity ) . rgbaString ( ) ;
ctx . strokeRect ( pt . x , pt . y , vm . bodyFontSize , vm . bodyFontSize ) ;
// Inner square
ctx . fillStyle = helpers . color ( vm . labelColors [ i ] . backgroundColor ) . alpha ( opacity ) . rgbaString ( ) ;
ctx . fillRect ( pt . x + 1 , pt . y + 1 , vm . bodyFontSize - 2 , vm . bodyFontSize - 2 ) ;
ctx . fillStyle = helpers . color ( vm . bodyColor ) . alpha ( opacity ) . rgbaString ( ) ; // Return fill style for text
}
// Body Line
ctx . fillText ( body , pt . x + ( this . _options . tooltips . mode !== 'single' ? ( vm . bodyFontSize + 2 ) : 0 ) , pt . y ) ;
pt . y += vm . bodyFontSize + vm . bodySpacing ;
} , this ) ;
// After Body
helpers . each ( vm . afterBody , function ( afterBody ) {
ctx . fillText ( afterBody , pt . x , pt . y ) ;
pt . y += vm . bodyFontSize ;
} ) ;
pt . y -= vm . bodySpacing ; // Remove last body spacing
} ,
drawFooter : function drawFooter ( pt , vm , ctx , opacity ) {
if ( vm . footer . length ) {
pt . y += vm . footerMarginTop ;
ctx . textAlign = vm . _footerAlign ;
ctx . textBaseline = "top" ;
var footerColor = helpers . color ( vm . footerColor ) ;
ctx . fillStyle = footerColor . alpha ( opacity * footerColor . alpha ( ) ) . rgbString ( ) ;
ctx . font = helpers . fontString ( vm . footerFontSize , vm . _footerFontStyle , vm . _footerFontFamily ) ;
helpers . each ( vm . footer , function ( footer ) {
ctx . fillText ( footer , pt . x , pt . y ) ;
pt . y += vm . footerFontSize + vm . footerSpacing ;
} ) ;
}
} ,
draw : function draw ( ) {
var ctx = this . _chart . ctx ;
var vm = this . _view ;
if ( vm . opacity === 0 ) {
return ;
}
var caretPadding = vm . caretPadding ;
var tooltipSize = this . getTooltipSize ( vm ) ;
var pt = {
x : vm . x ,
y : vm . y
} ;
// IE11/Edge does not like very small opacities, so snap to 0
var opacity = Math . abs ( vm . opacity < 1e-3 ) ? 0 : vm . opacity ;
if ( this . _options . tooltips . enabled ) {
// Draw Background
var bgColor = helpers . color ( vm . backgroundColor ) ;
ctx . fillStyle = bgColor . alpha ( opacity * bgColor . alpha ( ) ) . rgbString ( ) ;
helpers . drawRoundedRectangle ( ctx , pt . x , pt . y , tooltipSize . width , tooltipSize . height , vm . cornerRadius ) ;
ctx . fill ( ) ;
// Draw Caret
this . drawCaret ( pt , tooltipSize , opacity , caretPadding ) ;
// Draw Title, Body, and Footer
pt . x += vm . xPadding ;
pt . y += vm . yPadding ;
// Titles
this . drawTitle ( pt , vm , ctx , opacity ) ;
// Body
this . drawBody ( pt , vm , ctx , opacity ) ;
// Footer
this . drawFooter ( pt , vm , ctx , opacity ) ;
}
}
} ) ;
} ;
} , { } ] , 33 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart , moment ) {
var helpers = Chart . helpers ;
Chart . defaults . global . elements . arc = {
backgroundColor : Chart . defaults . global . defaultColor ,
borderColor : "#fff" ,
borderWidth : 2
} ;
Chart . elements . Arc = Chart . Element . extend ( {
inLabelRange : function ( mouseX ) {
var vm = this . _view ;
if ( vm ) {
return ( Math . pow ( mouseX - vm . x , 2 ) < Math . pow ( vm . radius + vm . hoverRadius , 2 ) ) ;
} else {
return false ;
}
} ,
inRange : function ( chartX , chartY ) {
var vm = this . _view ;
if ( vm ) {
var pointRelativePosition = helpers . getAngleFromPoint ( vm , {
x : chartX ,
y : chartY
} ) ;
//Sanitise angle range
var startAngle = vm . startAngle ;
var endAngle = vm . endAngle ;
while ( endAngle < startAngle ) {
endAngle += 2.0 * Math . PI ;
}
while ( pointRelativePosition . angle > endAngle ) {
pointRelativePosition . angle -= 2.0 * Math . PI ;
}
while ( pointRelativePosition . angle < startAngle ) {
pointRelativePosition . angle += 2.0 * Math . PI ;
}
//Check if within the range of the open/close angle
var betweenAngles = ( pointRelativePosition . angle >= startAngle && pointRelativePosition . angle <= endAngle ) ,
withinRadius = ( pointRelativePosition . distance >= vm . innerRadius && pointRelativePosition . distance <= vm . outerRadius ) ;
return ( betweenAngles && withinRadius ) ;
} else {
return false ;
}
} ,
tooltipPosition : function ( ) {
var vm = this . _view ;
var centreAngle = vm . startAngle + ( ( vm . endAngle - vm . startAngle ) / 2 ) ,
rangeFromCentre = ( vm . outerRadius - vm . innerRadius ) / 2 + vm . innerRadius ;
return {
x : vm . x + ( Math . cos ( centreAngle ) * rangeFromCentre ) ,
y : vm . y + ( Math . sin ( centreAngle ) * rangeFromCentre )
} ;
} ,
draw : function ( ) {
var ctx = this . _chart . ctx ;
var vm = this . _view ;
ctx . beginPath ( ) ;
ctx . arc ( vm . x , vm . y , vm . outerRadius , vm . startAngle , vm . endAngle ) ;
ctx . arc ( vm . x , vm . y , vm . innerRadius , vm . endAngle , vm . startAngle , true ) ;
ctx . closePath ( ) ;
ctx . strokeStyle = vm . borderColor ;
ctx . lineWidth = vm . borderWidth ;
ctx . fillStyle = vm . backgroundColor ;
ctx . fill ( ) ;
ctx . lineJoin = 'bevel' ;
if ( vm . borderWidth ) {
ctx . stroke ( ) ;
}
}
} ) ;
} ;
} , { } ] , 34 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . elements . line = {
tension : 0.4 ,
backgroundColor : Chart . defaults . global . defaultColor ,
borderWidth : 3 ,
borderColor : Chart . defaults . global . defaultColor ,
borderCapStyle : 'butt' ,
borderDash : [ ] ,
borderDashOffset : 0.0 ,
borderJoinStyle : 'miter' ,
fill : true // do we fill in the area between the line and its base axis
} ;
Chart . elements . Line = Chart . Element . extend ( {
lineToNextPoint : function ( previousPoint , point , nextPoint , skipHandler , previousSkipHandler ) {
var ctx = this . _chart . ctx ;
if ( point . _view . skip ) {
skipHandler . call ( this , previousPoint , point , nextPoint ) ;
} else if ( previousPoint . _view . skip ) {
previousSkipHandler . call ( this , previousPoint , point , nextPoint ) ;
} else if ( point . _view . tension === 0 ) {
ctx . lineTo ( point . _view . x , point . _view . y ) ;
} else {
// Line between points
ctx . bezierCurveTo (
previousPoint . _view . controlPointNextX ,
previousPoint . _view . controlPointNextY ,
point . _view . controlPointPreviousX ,
point . _view . controlPointPreviousY ,
point . _view . x ,
point . _view . y
) ;
}
} ,
draw : function ( ) {
var _this = this ;
var vm = this . _view ;
var ctx = this . _chart . ctx ;
var first = this . _children [ 0 ] ;
var last = this . _children [ this . _children . length - 1 ] ;
function loopBackToStart ( drawLineToCenter ) {
if ( ! first . _view . skip && ! last . _view . skip ) {
// Draw a bezier line from last to first
ctx . bezierCurveTo (
last . _view . controlPointNextX ,
last . _view . controlPointNextY ,
first . _view . controlPointPreviousX ,
first . _view . controlPointPreviousY ,
first . _view . x ,
first . _view . y
) ;
} else if ( drawLineToCenter ) {
// Go to center
ctx . lineTo ( _this . _view . scaleZero . x , _this . _view . scaleZero . y ) ;
}
}
ctx . save ( ) ;
// If we had points and want to fill this line, do so.
if ( this . _children . length > 0 && vm . fill ) {
// Draw the background first (so the border is always on top)
ctx . beginPath ( ) ;
helpers . each ( this . _children , function ( point , index ) {
var previous = helpers . previousItem ( this . _children , index ) ;
var next = helpers . nextItem ( this . _children , index ) ;
// First point moves to it's starting position no matter what
if ( index === 0 ) {
if ( this . _loop ) {
ctx . moveTo ( vm . scaleZero . x , vm . scaleZero . y ) ;
} else {
ctx . moveTo ( point . _view . x , vm . scaleZero ) ;
}
if ( point . _view . skip ) {
if ( ! this . _loop ) {
ctx . moveTo ( next . _view . x , this . _view . scaleZero ) ;
}
} else {
ctx . lineTo ( point . _view . x , point . _view . y ) ;
}
} else {
this . lineToNextPoint ( previous , point , next , function ( previousPoint , point , nextPoint ) {
if ( this . _loop ) {
// Go to center
ctx . lineTo ( this . _view . scaleZero . x , this . _view . scaleZero . y ) ;
} else {
ctx . lineTo ( previousPoint . _view . x , this . _view . scaleZero ) ;
ctx . moveTo ( nextPoint . _view . x , this . _view . scaleZero ) ;
}
} , function ( previousPoint , point ) {
// If we skipped the last point, draw a line to ourselves so that the fill is nice
ctx . lineTo ( point . _view . x , point . _view . y ) ;
} ) ;
}
} , this ) ;
// For radial scales, loop back around to the first point
if ( this . _loop ) {
loopBackToStart ( true ) ;
} else {
//Round off the line by going to the base of the chart, back to the start, then fill.
ctx . lineTo ( this . _children [ this . _children . length - 1 ] . _view . x , vm . scaleZero ) ;
ctx . lineTo ( this . _children [ 0 ] . _view . x , vm . scaleZero ) ;
}
ctx . fillStyle = vm . backgroundColor || Chart . defaults . global . defaultColor ;
ctx . closePath ( ) ;
ctx . fill ( ) ;
}
// Now draw the line between all the points with any borders
ctx . lineCap = vm . borderCapStyle || Chart . defaults . global . elements . line . borderCapStyle ;
// IE 9 and 10 do not support line dash
if ( ctx . setLineDash ) {
ctx . setLineDash ( vm . borderDash || Chart . defaults . global . elements . line . borderDash ) ;
}
ctx . lineDashOffset = vm . borderDashOffset || Chart . defaults . global . elements . line . borderDashOffset ;
ctx . lineJoin = vm . borderJoinStyle || Chart . defaults . global . elements . line . borderJoinStyle ;
ctx . lineWidth = vm . borderWidth || Chart . defaults . global . elements . line . borderWidth ;
ctx . strokeStyle = vm . borderColor || Chart . defaults . global . defaultColor ;
ctx . beginPath ( ) ;
helpers . each ( this . _children , function ( point , index ) {
var previous = helpers . previousItem ( this . _children , index ) ;
var next = helpers . nextItem ( this . _children , index ) ;
if ( index === 0 ) {
ctx . moveTo ( point . _view . x , point . _view . y ) ;
} else {
this . lineToNextPoint ( previous , point , next , function ( previousPoint , point , nextPoint ) {
ctx . moveTo ( nextPoint . _view . x , nextPoint . _view . y ) ;
} , function ( previousPoint , point ) {
// If we skipped the last point, move up to our point preventing a line from being drawn
ctx . moveTo ( point . _view . x , point . _view . y ) ;
} ) ;
}
} , this ) ;
if ( this . _loop && this . _children . length > 0 ) {
loopBackToStart ( ) ;
}
ctx . stroke ( ) ;
ctx . restore ( ) ;
}
} ) ;
} ;
} , { } ] , 35 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . elements . point = {
radius : 3 ,
pointStyle : 'circle' ,
backgroundColor : Chart . defaults . global . defaultColor ,
borderWidth : 1 ,
borderColor : Chart . defaults . global . defaultColor ,
// Hover
hitRadius : 1 ,
hoverRadius : 4 ,
hoverBorderWidth : 1
} ;
Chart . elements . Point = Chart . Element . extend ( {
inRange : function ( mouseX , mouseY ) {
var vm = this . _view ;
if ( vm ) {
var hoverRange = vm . hitRadius + vm . radius ;
return ( ( Math . pow ( mouseX - vm . x , 2 ) + Math . pow ( mouseY - vm . y , 2 ) ) < Math . pow ( hoverRange , 2 ) ) ;
} else {
return false ;
}
} ,
inLabelRange : function ( mouseX ) {
var vm = this . _view ;
if ( vm ) {
return ( Math . pow ( mouseX - vm . x , 2 ) < Math . pow ( vm . radius + vm . hitRadius , 2 ) ) ;
} else {
return false ;
}
} ,
tooltipPosition : function ( ) {
var vm = this . _view ;
return {
x : vm . x ,
y : vm . y ,
padding : vm . radius + vm . borderWidth
} ;
} ,
draw : function ( ) {
var vm = this . _view ;
var ctx = this . _chart . ctx ;
if ( vm . skip ) {
return ;
}
if ( typeof vm . pointStyle === 'object' && ( ( vm . pointStyle . toString ( ) === '[object HTMLImageElement]' ) || ( vm . pointStyle . toString ( ) === '[object HTMLCanvasElement]' ) ) ) {
ctx . drawImage ( vm . pointStyle , vm . x - vm . pointStyle . width / 2 , vm . y - vm . pointStyle . height / 2 ) ;
return ;
}
if ( ! isNaN ( vm . radius ) && vm . radius > 0 ) {
ctx . strokeStyle = vm . borderColor || Chart . defaults . global . defaultColor ;
ctx . lineWidth = helpers . getValueOrDefault ( vm . borderWidth , Chart . defaults . global . elements . point . borderWidth ) ;
ctx . fillStyle = vm . backgroundColor || Chart . defaults . global . defaultColor ;
var radius = vm . radius ;
var xOffset ;
var yOffset ;
switch ( vm . pointStyle ) {
// Default includes circle
default : ctx . beginPath ( ) ;
ctx . arc ( vm . x , vm . y , radius , 0 , Math . PI * 2 ) ;
ctx . closePath ( ) ;
ctx . fill ( ) ;
break ;
case 'triangle' :
ctx . beginPath ( ) ;
var edgeLength = 3 * radius / Math . sqrt ( 3 ) ;
var height = edgeLength * Math . sqrt ( 3 ) / 2 ;
ctx . moveTo ( vm . x - edgeLength / 2 , vm . y + height / 3 ) ;
ctx . lineTo ( vm . x + edgeLength / 2 , vm . y + height / 3 ) ;
ctx . lineTo ( vm . x , vm . y - 2 * height / 3 ) ;
ctx . closePath ( ) ;
ctx . fill ( ) ;
break ;
case 'rect' :
ctx . fillRect ( vm . x - 1 / Math . SQRT2 * radius , vm . y - 1 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius ) ;
ctx . strokeRect ( vm . x - 1 / Math . SQRT2 * radius , vm . y - 1 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius ) ;
break ;
case 'rectRot' :
ctx . translate ( vm . x , vm . y ) ;
ctx . rotate ( Math . PI / 4 ) ;
ctx . fillRect ( - 1 / Math . SQRT2 * radius , - 1 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius ) ;
ctx . strokeRect ( - 1 / Math . SQRT2 * radius , - 1 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius , 2 / Math . SQRT2 * radius ) ;
ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
break ;
case 'cross' :
ctx . beginPath ( ) ;
ctx . moveTo ( vm . x , vm . y + radius ) ;
ctx . lineTo ( vm . x , vm . y - radius ) ;
ctx . moveTo ( vm . x - radius , vm . y ) ;
ctx . lineTo ( vm . x + radius , vm . y ) ;
ctx . closePath ( ) ;
break ;
case 'crossRot' :
ctx . beginPath ( ) ;
xOffset = Math . cos ( Math . PI / 4 ) * radius ;
yOffset = Math . sin ( Math . PI / 4 ) * radius ;
ctx . moveTo ( vm . x - xOffset , vm . y - yOffset ) ;
ctx . lineTo ( vm . x + xOffset , vm . y + yOffset ) ;
ctx . moveTo ( vm . x - xOffset , vm . y + yOffset ) ;
ctx . lineTo ( vm . x + xOffset , vm . y - yOffset ) ;
ctx . closePath ( ) ;
break ;
case 'star' :
ctx . beginPath ( ) ;
ctx . moveTo ( vm . x , vm . y + radius ) ;
ctx . lineTo ( vm . x , vm . y - radius ) ;
ctx . moveTo ( vm . x - radius , vm . y ) ;
ctx . lineTo ( vm . x + radius , vm . y ) ;
xOffset = Math . cos ( Math . PI / 4 ) * radius ;
yOffset = Math . sin ( Math . PI / 4 ) * radius ;
ctx . moveTo ( vm . x - xOffset , vm . y - yOffset ) ;
ctx . lineTo ( vm . x + xOffset , vm . y + yOffset ) ;
ctx . moveTo ( vm . x - xOffset , vm . y + yOffset ) ;
ctx . lineTo ( vm . x + xOffset , vm . y - yOffset ) ;
ctx . closePath ( ) ;
break ;
case 'line' :
ctx . beginPath ( ) ;
ctx . moveTo ( vm . x - radius , vm . y ) ;
ctx . lineTo ( vm . x + radius , vm . y ) ;
ctx . closePath ( ) ;
break ;
case 'dash' :
ctx . beginPath ( ) ;
ctx . moveTo ( vm . x , vm . y ) ;
ctx . lineTo ( vm . x + radius , vm . y ) ;
ctx . closePath ( ) ;
break ;
}
ctx . stroke ( ) ;
}
}
} ) ;
} ;
} , { } ] , 36 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
Chart . defaults . global . elements . rectangle = {
backgroundColor : Chart . defaults . global . defaultColor ,
borderWidth : 0 ,
borderColor : Chart . defaults . global . defaultColor ,
borderSkipped : 'bottom'
} ;
Chart . elements . Rectangle = Chart . Element . extend ( {
draw : function ( ) {
var ctx = this . _chart . ctx ;
var vm = this . _view ;
var halfWidth = vm . width / 2 ,
leftX = vm . x - halfWidth ,
rightX = vm . x + halfWidth ,
top = vm . base - ( vm . base - vm . y ) ,
halfStroke = vm . borderWidth / 2 ;
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if ( vm . borderWidth ) {
leftX += halfStroke ;
rightX -= halfStroke ;
top += halfStroke ;
}
ctx . beginPath ( ) ;
ctx . fillStyle = vm . backgroundColor ;
ctx . strokeStyle = vm . borderColor ;
ctx . lineWidth = vm . borderWidth ;
// Corner points, from bottom-left to bottom-right clockwise
// | 1 2 |
// | 0 3 |
var corners = [
[ leftX , vm . base ] ,
[ leftX , top ] ,
[ rightX , top ] ,
[ rightX , vm . base ]
] ;
// Find first (starting) corner with fallback to 'bottom'
var borders = [ 'bottom' , 'left' , 'top' , 'right' ] ;
var startCorner = borders . indexOf ( vm . borderSkipped , 0 ) ;
if ( startCorner === - 1 )
startCorner = 0 ;
function cornerAt ( index ) {
return corners [ ( startCorner + index ) % 4 ] ;
}
// Draw rectangle from 'startCorner'
ctx . moveTo . apply ( ctx , cornerAt ( 0 ) ) ;
for ( var i = 1 ; i < 4 ; i ++ )
ctx . lineTo . apply ( ctx , cornerAt ( i ) ) ;
ctx . fill ( ) ;
if ( vm . borderWidth ) {
ctx . stroke ( ) ;
}
} ,
height : function ( ) {
var vm = this . _view ;
return vm . base - vm . y ;
} ,
inRange : function ( mouseX , mouseY ) {
var vm = this . _view ;
var inRange = false ;
if ( vm ) {
if ( vm . y < vm . base ) {
inRange = ( mouseX >= vm . x - vm . width / 2 && mouseX <= vm . x + vm . width / 2 ) && ( mouseY >= vm . y && mouseY <= vm . base ) ;
} else {
inRange = ( mouseX >= vm . x - vm . width / 2 && mouseX <= vm . x + vm . width / 2 ) && ( mouseY >= vm . base && mouseY <= vm . y ) ;
}
}
return inRange ;
} ,
inLabelRange : function ( mouseX ) {
var vm = this . _view ;
if ( vm ) {
return ( mouseX >= vm . x - vm . width / 2 && mouseX <= vm . x + vm . width / 2 ) ;
} else {
return false ;
}
} ,
tooltipPosition : function ( ) {
var vm = this . _view ;
return {
x : vm . x ,
y : vm . y
} ;
}
} ) ;
} ;
} , { } ] , 37 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
// Default config for a category scale
var defaultConfig = {
position : "bottom"
} ;
var DatasetScale = Chart . Scale . extend ( {
buildTicks : function ( index ) {
this . startIndex = 0 ;
this . endIndex = this . chart . data . labels . length ;
var findIndex ;
if ( this . options . ticks . min !== undefined ) {
// user specified min value
findIndex = helpers . indexOf ( this . chart . data . labels , this . options . ticks . min ) ;
this . startIndex = findIndex !== - 1 ? findIndex : this . startIndex ;
}
if ( this . options . ticks . max !== undefined ) {
// user specified max value
findIndex = helpers . indexOf ( this . chart . data . labels , this . options . ticks . max ) ;
this . endIndex = findIndex !== - 1 ? findIndex : this . endIndex ;
}
// If we are viewing some subset of labels, slice the original array
this . ticks = ( this . startIndex === 0 && this . endIndex === this . chart . data . labels . length ) ? this . chart . data . labels : this . chart . data . labels . slice ( this . startIndex , this . endIndex + 1 ) ;
} ,
getLabelForIndex : function ( index , datasetIndex ) {
return this . ticks [ index ] ;
} ,
// Used to get data value locations. Value can either be an index or a numerical value
getPixelForValue : function ( value , index , datasetIndex , includeOffset ) {
// 1 is added because we need the length but we have the indexes
var offsetAmt = Math . max ( ( this . ticks . length - ( ( this . options . gridLines . offsetGridLines ) ? 0 : 1 ) ) , 1 ) ;
if ( this . isHorizontal ( ) ) {
var innerWidth = this . width - ( this . paddingLeft + this . paddingRight ) ;
var valueWidth = innerWidth / offsetAmt ;
var widthOffset = ( valueWidth * ( index - this . startIndex ) ) + this . paddingLeft ;
if ( this . options . gridLines . offsetGridLines && includeOffset ) {
widthOffset += ( valueWidth / 2 ) ;
}
return this . left + Math . round ( widthOffset ) ;
} else {
var innerHeight = this . height - ( this . paddingTop + this . paddingBottom ) ;
var valueHeight = innerHeight / offsetAmt ;
var heightOffset = ( valueHeight * ( index - this . startIndex ) ) + this . paddingTop ;
if ( this . options . gridLines . offsetGridLines && includeOffset ) {
heightOffset += ( valueHeight / 2 ) ;
}
return this . top + Math . round ( heightOffset ) ;
}
} ,
getPixelForTick : function ( index , includeOffset ) {
return this . getPixelForValue ( this . ticks [ index ] , index + this . startIndex , null , includeOffset ) ;
}
} ) ;
Chart . scaleService . registerScaleType ( "category" , DatasetScale , defaultConfig ) ;
} ;
} , { } ] , 38 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
var defaultConfig = {
position : "left" ,
ticks : {
callback : function ( tickValue , index , ticks ) {
var delta = ticks [ 1 ] - ticks [ 0 ] ;
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
if ( Math . abs ( delta ) > 1 ) {
if ( tickValue !== Math . floor ( tickValue ) ) {
// not an integer
delta = tickValue - Math . floor ( tickValue ) ;
}
}
var logDelta = helpers . log10 ( Math . abs ( delta ) ) ;
var tickString = '' ;
if ( tickValue !== 0 ) {
var numDecimal = - 1 * Math . floor ( logDelta ) ;
numDecimal = Math . max ( Math . min ( numDecimal , 20 ) , 0 ) ; // toFixed has a max of 20 decimal places
tickString = tickValue . toFixed ( numDecimal ) ;
} else {
tickString = '0' ; // never show decimal places for 0
}
return tickString ;
}
}
} ;
var LinearScale = Chart . Scale . extend ( {
determineDataLimits : function ( ) {
// First Calculate the range
this . min = null ;
this . max = null ;
if ( this . options . stacked ) {
var valuesPerType = { } ;
var hasPositiveValues = false ;
var hasNegativeValues = false ;
helpers . each ( this . chart . data . datasets , function ( dataset ) {
if ( valuesPerType [ dataset . type ] === undefined ) {
valuesPerType [ dataset . type ] = {
positiveValues : [ ] ,
negativeValues : [ ]
} ;
}
// Store these per type
var positiveValues = valuesPerType [ dataset . type ] . positiveValues ;
var negativeValues = valuesPerType [ dataset . type ] . negativeValues ;
if ( helpers . isDatasetVisible ( dataset ) && ( this . isHorizontal ( ) ? dataset . xAxisID === this . id : dataset . yAxisID === this . id ) ) {
helpers . each ( dataset . data , function ( rawValue , index ) {
var value = + this . getRightValue ( rawValue ) ;
if ( isNaN ( value ) ) {
return ;
}
positiveValues [ index ] = positiveValues [ index ] || 0 ;
negativeValues [ index ] = negativeValues [ index ] || 0 ;
if ( this . options . relativePoints ) {
positiveValues [ index ] = 100 ;
} else {
if ( value < 0 ) {
hasNegativeValues = true ;
negativeValues [ index ] += value ;
} else {
hasPositiveValues = true ;
positiveValues [ index ] += value ;
}
}
} , this ) ;
}
} , this ) ;
helpers . each ( valuesPerType , function ( valuesForType ) {
var values = valuesForType . positiveValues . concat ( valuesForType . negativeValues ) ;
var minVal = helpers . min ( values ) ;
var maxVal = helpers . max ( values ) ;
this . min = this . min === null ? minVal : Math . min ( this . min , minVal ) ;
this . max = this . max === null ? maxVal : Math . max ( this . max , maxVal ) ;
} , this ) ;
} else {
helpers . each ( this . chart . data . datasets , function ( dataset ) {
if ( helpers . isDatasetVisible ( dataset ) && ( this . isHorizontal ( ) ? dataset . xAxisID === this . id : dataset . yAxisID === this . id ) ) {
helpers . each ( dataset . data , function ( rawValue , index ) {
var value = + this . getRightValue ( rawValue ) ;
if ( isNaN ( value ) ) {
return ;
}
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 ) ;
}
// 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 . ticks . 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 ;
}
}
if ( this . options . ticks . min !== undefined ) {
this . min = this . options . ticks . min ;
} else if ( this . options . ticks . suggestedMin !== undefined ) {
this . min = Math . min ( this . min , this . options . ticks . suggestedMin ) ;
}
if ( this . options . ticks . max !== undefined ) {
this . max = this . options . ticks . max ;
} else if ( this . options . ticks . suggestedMax !== undefined ) {
this . max = Math . max ( this . max , this . options . ticks . suggestedMax ) ;
}
if ( this . min === this . max ) {
this . min -- ;
this . max ++ ;
}
} ,
buildTicks : function ( ) {
// Then calulate the ticks
this . ticks = [ ] ;
// 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 ;
if ( this . isHorizontal ( ) ) {
maxTicks = Math . min ( this . options . ticks . maxTicksLimit ? this . options . ticks . maxTicksLimit : 11 , Math . ceil ( this . width / 50 ) ) ;
} else {
// The factor of 2 used to scale the font size has been experimentally determined.
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
maxTicks = Math . min ( this . options . ticks . maxTicksLimit ? this . options . ticks . maxTicksLimit : 11 , Math . ceil ( this . height / ( 2 * tickFontSize ) ) ) ;
}
// 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.
var spacing ;
var fixedStepSizeSet = ( this . options . ticks . fixedStepSize && this . options . ticks . fixedStepSize > 0 ) || ( this . options . ticks . stepSize && this . options . ticks . stepSize > 0 ) ;
if ( fixedStepSizeSet ) {
spacing = helpers . getValueOrDefault ( this . options . ticks . fixedStepSize , this . options . ticks . stepSize ) ;
} else {
var niceRange = helpers . niceNum ( this . max - this . min , false ) ;
spacing = helpers . niceNum ( niceRange / ( maxTicks - 1 ) , true ) ;
}
var niceMin = Math . floor ( this . min / spacing ) * spacing ;
var niceMax = Math . ceil ( this . max / spacing ) * spacing ;
var numSpaces = ( niceMax - niceMin ) / spacing ;
// If very close to our rounded value, use it.
if ( helpers . almostEquals ( numSpaces , Math . round ( numSpaces ) , spacing / 1000 ) ) {
numSpaces = Math . round ( numSpaces ) ;
} else {
numSpaces = Math . ceil ( numSpaces ) ;
}
// Put the values into the ticks array
this . ticks . push ( this . options . ticks . min !== undefined ? this . options . ticks . min : niceMin ) ;
for ( var j = 1 ; j < numSpaces ; ++ j ) {
this . ticks . push ( niceMin + ( j * spacing ) ) ;
}
this . ticks . push ( this . options . ticks . max !== undefined ? this . options . ticks . max : niceMax ) ;
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 ) ;
if ( this . options . ticks . reverse ) {
this . ticks . reverse ( ) ;
this . start = this . max ;
this . end = this . min ;
} else {
this . start = this . min ;
this . end = this . max ;
}
} ,
getLabelForIndex : function ( index , datasetIndex ) {
return + this . getRightValue ( this . chart . data . datasets [ datasetIndex ] . data [ index ] ) ;
} ,
convertTicksToLabels : function ( ) {
this . ticksAsNumbers = this . ticks . slice ( ) ;
this . zeroLineIndex = this . ticks . indexOf ( 0 ) ;
Chart . Scale . prototype . convertTicksToLabels . call ( this ) ;
} ,
// Utils
getPixelForValue : function ( value , index , datasetIndex , includeOffset ) {
// This must be called after fit has been run so that
// this.left, this.top, this.right, and this.bottom have been defined
var rightValue = + this . getRightValue ( value ) ;
var pixel ;
var range = this . end - this . start ;
if ( this . isHorizontal ( ) ) {
var innerWidth = this . width - ( this . paddingLeft + this . paddingRight ) ;
pixel = this . left + ( innerWidth / range * ( rightValue - this . start ) ) ;
return Math . round ( pixel + this . paddingLeft ) ;
} else {
var innerHeight = this . height - ( this . paddingTop + this . paddingBottom ) ;
pixel = ( this . bottom - this . paddingBottom ) - ( innerHeight / range * ( rightValue - this . start ) ) ;
return Math . round ( pixel ) ;
}
} ,
getPixelForTick : function ( index , includeOffset ) {
return this . getPixelForValue ( this . ticksAsNumbers [ index ] , null , null , includeOffset ) ;
}
} ) ;
Chart . scaleService . registerScaleType ( "linear" , LinearScale , defaultConfig ) ;
} ;
} , { } ] , 39 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
var defaultConfig = {
position : "left" ,
// label settings
ticks : {
callback : function ( value , index , arr ) {
var remain = value / ( Math . pow ( 10 , Math . floor ( Chart . helpers . log10 ( value ) ) ) ) ;
if ( remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr . length - 1 ) {
return value . toExponential ( ) ;
} else {
return '' ;
}
}
}
} ;
var LogarithmicScale = Chart . Scale . extend ( {
determineDataLimits : function ( ) {
// Calculate Range
this . min = null ;
this . max = null ;
if ( this . options . stacked ) {
var valuesPerType = { } ;
helpers . each ( this . chart . data . datasets , function ( dataset ) {
if ( helpers . isDatasetVisible ( dataset ) && ( this . isHorizontal ( ) ? dataset . xAxisID === this . id : dataset . yAxisID === this . id ) ) {
if ( valuesPerType [ dataset . type ] === undefined ) {
valuesPerType [ dataset . type ] = [ ] ;
}
helpers . each ( dataset . data , function ( rawValue , index ) {
var values = valuesPerType [ dataset . type ] ;
var value = + this . getRightValue ( rawValue ) ;
if ( isNaN ( value ) ) {
return ;
}
values [ index ] = values [ index ] || 0 ;
if ( this . options . relativePoints ) {
values [ index ] = 100 ;
} else {
// Don't need to split positive and negative since the log scale can't handle a 0 crossing
values [ index ] += value ;
}
} , this ) ;
}
} , this ) ;
helpers . each ( valuesPerType , function ( valuesForType ) {
var minVal = helpers . min ( valuesForType ) ;
var maxVal = helpers . max ( valuesForType ) ;
this . min = this . min === null ? minVal : Math . min ( this . min , minVal ) ;
this . max = this . max === null ? maxVal : Math . max ( this . max , maxVal ) ;
} , this ) ;
} else {
helpers . each ( this . chart . data . datasets , function ( dataset ) {
if ( helpers . isDatasetVisible ( dataset ) && ( this . isHorizontal ( ) ? dataset . xAxisID === this . id : dataset . yAxisID === this . id ) ) {
helpers . each ( dataset . data , function ( rawValue , index ) {
var value = + this . getRightValue ( rawValue ) ;
if ( isNaN ( value ) ) {
return ;
}
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 . min = this . options . ticks . min !== undefined ? this . options . ticks . min : this . min ;
this . max = this . options . ticks . max !== undefined ? this . options . ticks . max : this . max ;
if ( this . min === this . max ) {
if ( this . min !== 0 && this . min !== null ) {
this . min = Math . pow ( 10 , Math . floor ( helpers . log10 ( this . min ) ) - 1 ) ;
this . max = Math . pow ( 10 , Math . floor ( helpers . log10 ( this . max ) ) + 1 ) ;
} else {
this . min = 1 ;
this . max = 10 ;
}
}
} ,
buildTicks : function ( ) {
// 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 = [ ] ;
// 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 tickVal = this . options . ticks . min !== undefined ? this . options . ticks . min : Math . pow ( 10 , Math . floor ( helpers . log10 ( this . min ) ) ) ;
while ( tickVal < this . max ) {
this . ticks . push ( tickVal ) ;
var exp = Math . floor ( helpers . log10 ( tickVal ) ) ;
var significand = Math . floor ( tickVal / Math . pow ( 10 , exp ) ) + 1 ;
if ( significand === 10 ) {
significand = 1 ;
++ exp ;
}
tickVal = significand * Math . pow ( 10 , exp ) ;
}
var lastTick = this . options . ticks . max !== undefined ? this . options . ticks . max : tickVal ;
this . ticks . push ( lastTick ) ;
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 ) ;
if ( this . options . ticks . reverse ) {
this . ticks . reverse ( ) ;
this . start = this . max ;
this . end = this . min ;
} else {
this . start = this . min ;
this . end = this . max ;
}
} ,
convertTicksToLabels : function ( ) {
this . tickValues = this . ticks . slice ( ) ;
Chart . Scale . prototype . convertTicksToLabels . call ( this ) ;
} ,
// Get the correct tooltip label
getLabelForIndex : function ( index , datasetIndex ) {
return + this . getRightValue ( this . chart . data . datasets [ datasetIndex ] . data [ index ] ) ;
} ,
getPixelForTick : function ( index , includeOffset ) {
return this . getPixelForValue ( this . tickValues [ index ] , null , null , includeOffset ) ;
} ,
getPixelForValue : function ( value , index , datasetIndex , includeOffset ) {
var pixel ;
var newVal = + this . getRightValue ( value ) ;
var range = helpers . log10 ( this . end ) - helpers . log10 ( this . start ) ;
if ( this . isHorizontal ( ) ) {
if ( newVal === 0 ) {
pixel = this . left + this . paddingLeft ;
} else {
var innerWidth = this . width - ( this . paddingLeft + this . paddingRight ) ;
pixel = this . left + ( innerWidth / range * ( helpers . log10 ( newVal ) - helpers . log10 ( this . start ) ) ) ;
pixel += this . paddingLeft ;
}
} else {
// Bottom - top since pixels increase downard on a screen
if ( newVal === 0 ) {
pixel = this . top + this . paddingTop ;
} else {
var innerHeight = this . height - ( this . paddingTop + this . paddingBottom ) ;
pixel = ( this . bottom - this . paddingBottom ) - ( innerHeight / range * ( helpers . log10 ( newVal ) - helpers . log10 ( this . start ) ) ) ;
}
}
return pixel ;
}
} ) ;
Chart . scaleService . registerScaleType ( "logarithmic" , LogarithmicScale , defaultConfig ) ;
} ;
} , { } ] , 40 : [ function ( require , module , exports ) {
"use strict" ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
var defaultConfig = {
display : true ,
//Boolean - Whether to animate scaling the chart from the centre
animate : true ,
lineArc : false ,
position : "chartArea" ,
angleLines : {
display : true ,
color : "rgba(0, 0, 0, 0.1)" ,
lineWidth : 1
} ,
// label settings
ticks : {
//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 : {
//Number - Point label font size in pixels
fontSize : 10 ,
//Function - Used to convert point labels
callback : function ( label ) {
return label ;
}
}
} ;
var LinearRadialScale = Chart . Scale . extend ( {
getValueCount : function ( ) {
return this . chart . data . labels . length ;
} ,
setDimensions : function ( ) {
// Set the unconstrained dimension before label rotation
this . width = this . maxWidth ;
this . height = this . maxHeight ;
this . xCenter = Math . round ( this . width / 2 ) ;
this . yCenter = Math . round ( this . height / 2 ) ;
var minSize = helpers . min ( [ this . height , this . width ] ) ;
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
this . drawingArea = ( this . options . display ) ? ( minSize / 2 ) - ( tickFontSize / 2 + this . options . ticks . backdropPaddingY ) : ( minSize / 2 ) ;
} ,
determineDataLimits : function ( ) {
this . min = null ;
this . max = null ;
helpers . each ( this . chart . data . datasets , function ( dataset ) {
if ( helpers . isDatasetVisible ( dataset ) ) {
helpers . each ( dataset . data , function ( rawValue , index ) {
var value = + this . getRightValue ( rawValue ) ;
if ( isNaN ( value ) ) {
return ;
}
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 ) ;
// 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 . ticks . 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 ;
}
}
if ( this . options . ticks . min !== undefined ) {
this . min = this . options . ticks . min ;
} else if ( this . options . ticks . suggestedMin !== undefined ) {
this . min = Math . min ( this . min , this . options . ticks . suggestedMin ) ;
}
if ( this . options . ticks . max !== undefined ) {
this . max = this . options . ticks . max ;
} else if ( this . options . ticks . suggestedMax !== undefined ) {
this . max = Math . max ( this . max , this . options . ticks . suggestedMax ) ;
}
if ( this . min === this . max ) {
this . min -- ;
this . max ++ ;
}
} ,
buildTicks : function ( ) {
this . ticks = [ ] ;
// 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 tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
var maxTicks = Math . min ( this . options . ticks . maxTicksLimit ? this . options . ticks . maxTicksLimit : 11 , Math . ceil ( this . drawingArea / ( 1.5 * tickFontSize ) ) ) ;
maxTicks = Math . max ( 2 , maxTicks ) ; // Make sure we always have at least 2 ticks
// 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.
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 ;
var numSpaces = Math . ceil ( ( niceMax - niceMin ) / spacing ) ;
// Put the values into the ticks array
this . ticks . push ( this . options . ticks . min !== undefined ? this . options . ticks . min : niceMin ) ;
for ( var j = 1 ; j < numSpaces ; ++ j ) {
this . ticks . push ( niceMin + ( j * spacing ) ) ;
}
this . ticks . push ( this . options . ticks . max !== undefined ? this . options . ticks . max : niceMax ) ;
// 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 ) ;
if ( this . options . ticks . reverse ) {
this . ticks . reverse ( ) ;
this . start = this . max ;
this . end = this . min ;
} else {
this . start = this . min ;
this . end = this . max ;
}
this . zeroLineIndex = this . ticks . indexOf ( 0 ) ;
} ,
convertTicksToLabels : function ( ) {
Chart . Scale . prototype . convertTicksToLabels . call ( this ) ;
// Point labels
this . pointLabels = this . chart . data . labels . map ( this . options . pointLabels . callback , this ) ;
} ,
getLabelForIndex : function ( index , datasetIndex ) {
return + this . getRightValue ( this . chart . data . datasets [ datasetIndex ] . data [ index ] ) ;
} ,
fit : 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
* /
var pointLabelFontSize = helpers . getValueOrDefault ( this . options . pointLabels . fontSize , Chart . defaults . global . defaultFontSize ) ;
var pointLabeFontStyle = helpers . getValueOrDefault ( this . options . pointLabels . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var pointLabeFontFamily = helpers . getValueOrDefault ( this . options . pointLabels . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var pointLabeFont = helpers . fontString ( pointLabelFontSize , pointLabeFontStyle , pointLabeFontFamily ) ;
// 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 - pointLabelFontSize - 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 = pointLabeFont ;
for ( i = 0 ; i < this . getValueCount ( ) ; 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 ( this . pointLabels [ i ] ? this . pointLabels [ i ] : '' ) . width + 5 ;
if ( i === 0 || i === this . getValueCount ( ) / 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 . getValueCount ( ) / 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 . getValueCount ( ) / 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 = Math . round ( largestPossibleRadius - ( radiusReductionLeft + radiusReductionRight ) / 2 ) ;
this . setCenterPoint ( radiusReductionLeft , radiusReductionRight ) ;
} ,
setCenterPoint : function ( leftMovement , rightMovement ) {
var maxRight = this . width - rightMovement - this . drawingArea ,
maxLeft = leftMovement + this . drawingArea ;
this . xCenter = Math . round ( ( ( maxLeft + maxRight ) / 2 ) + this . left ) ;
// Always vertically in the centre as the text height doesn't change
this . yCenter = Math . round ( ( this . height / 2 ) + this . top ) ;
} ,
getIndexAngle : function ( index ) {
var angleMultiplier = ( Math . PI * 2 ) / this . getValueCount ( ) ;
// Start from the top instead of right, so remove a quarter of the circle
return index * angleMultiplier - ( Math . PI / 2 ) ;
} ,
getDistanceFromCenterForValue : function ( value ) {
if ( value === null ) {
return 0 ; // null always in center
}
// Take into account half font size + the yPadding of the top value
var scalingFactor = this . drawingArea / ( this . max - this . min ) ;
if ( this . options . reverse ) {
return ( this . max - value ) * scalingFactor ;
} else {
return ( value - this . min ) * scalingFactor ;
}
} ,
getPointPosition : function ( index , distanceFromCenter ) {
var thisAngle = this . getIndexAngle ( index ) ;
return {
x : Math . round ( Math . cos ( thisAngle ) * distanceFromCenter ) + this . xCenter ,
y : Math . round ( Math . sin ( thisAngle ) * distanceFromCenter ) + this . yCenter
} ;
} ,
getPointPositionForValue : function ( index , value ) {
return this . getPointPosition ( index , this . getDistanceFromCenterForValue ( value ) ) ;
} ,
draw : function ( ) {
if ( this . options . display ) {
var ctx = this . ctx ;
helpers . each ( this . ticks , function ( label , index ) {
// Don't draw a centre value (if it is minimum)
if ( index > 0 || this . options . reverse ) {
var yCenterOffset = this . getDistanceFromCenterForValue ( this . ticks [ index ] ) ;
var yHeight = this . yCenter - yCenterOffset ;
// Draw circular lines around the scale
if ( this . options . gridLines . display ) {
ctx . strokeStyle = this . options . gridLines . color ;
ctx . lineWidth = this . options . gridLines . lineWidth ;
if ( this . options . lineArc ) {
// Draw circular arcs between the points
ctx . beginPath ( ) ;
ctx . arc ( this . xCenter , this . yCenter , yCenterOffset , 0 , Math . PI * 2 ) ;
ctx . closePath ( ) ;
ctx . stroke ( ) ;
} else {
// Draw straight lines connecting each index
ctx . beginPath ( ) ;
for ( var i = 0 ; i < this . getValueCount ( ) ; i ++ ) {
var pointPosition = this . getPointPosition ( i , this . getDistanceFromCenterForValue ( 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 . ticks . display ) {
var tickFontColor = helpers . getValueOrDefault ( this . options . ticks . fontColor , Chart . defaults . global . defaultFontColor ) ;
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
var tickFontStyle = helpers . getValueOrDefault ( this . options . ticks . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var tickFontFamily = helpers . getValueOrDefault ( this . options . ticks . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var tickLabelFont = helpers . fontString ( tickFontSize , tickFontStyle , tickFontFamily ) ;
ctx . font = tickLabelFont ;
if ( this . options . ticks . showLabelBackdrop ) {
var labelWidth = ctx . measureText ( label ) . width ;
ctx . fillStyle = this . options . ticks . backdropColor ;
ctx . fillRect (
this . xCenter - labelWidth / 2 - this . options . ticks . backdropPaddingX ,
yHeight - tickFontSize / 2 - this . options . ticks . backdropPaddingY ,
labelWidth + this . options . ticks . backdropPaddingX * 2 ,
tickFontSize + this . options . ticks . backdropPaddingY * 2
) ;
}
ctx . textAlign = 'center' ;
ctx . textBaseline = "middle" ;
ctx . fillStyle = tickFontColor ;
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 . getValueCount ( ) - 1 ; i >= 0 ; i -- ) {
if ( this . options . angleLines . display ) {
var outerPosition = this . getPointPosition ( i , this . getDistanceFromCenterForValue ( this . options . reverse ? this . min : 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 . getDistanceFromCenterForValue ( this . options . reverse ? this . min : this . max ) + 5 ) ;
var pointLabelFontColor = helpers . getValueOrDefault ( this . options . pointLabels . fontColor , Chart . defaults . global . defaultFontColor ) ;
var pointLabelFontSize = helpers . getValueOrDefault ( this . options . pointLabels . fontSize , Chart . defaults . global . defaultFontSize ) ;
var pointLabeFontStyle = helpers . getValueOrDefault ( this . options . pointLabels . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var pointLabeFontFamily = helpers . getValueOrDefault ( this . options . pointLabels . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var pointLabeFont = helpers . fontString ( pointLabelFontSize , pointLabeFontStyle , pointLabeFontFamily ) ;
ctx . font = pointLabeFont ;
ctx . fillStyle = pointLabelFontColor ;
var labelsCount = this . pointLabels . length ,
halfLabelsCount = this . pointLabels . 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 . pointLabels [ i ] ? this . pointLabels [ i ] : '' , pointLabelPosition . x , pointLabelPosition . y ) ;
}
}
}
}
} ) ;
Chart . scaleService . registerScaleType ( "radialLinear" , LinearRadialScale , defaultConfig ) ;
} ;
} , { } ] , 41 : [ function ( require , module , exports ) {
/*global window: false */
"use strict" ;
var moment = require ( 'moment' ) ;
moment = typeof ( moment ) === 'function' ? moment : window . moment ;
module . exports = function ( Chart ) {
var helpers = Chart . helpers ;
var time = {
units : [ {
name : 'millisecond' ,
steps : [ 1 , 2 , 5 , 10 , 20 , 50 , 100 , 250 , 500 ]
} , {
name : 'second' ,
steps : [ 1 , 2 , 5 , 10 , 30 ]
} , {
name : 'minute' ,
steps : [ 1 , 2 , 5 , 10 , 30 ]
} , {
name : 'hour' ,
steps : [ 1 , 2 , 3 , 6 , 12 ]
} , {
name : 'day' ,
steps : [ 1 , 2 , 5 ]
} , {
name : 'week' ,
maxStep : 4
} , {
name : 'month' ,
maxStep : 3
} , {
name : 'quarter' ,
maxStep : 4
} , {
name : 'year' ,
maxStep : false
} ]
} ;
var defaultConfig = {
position : "bottom" ,
time : {
parser : false , // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
format : false , // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
unit : false , // false == automatic or override with week, month, year, etc.
round : false , // none, or override with week, month, year, etc.
displayFormat : false , // DEPRECATED
// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
displayFormats : {
'millisecond' : 'h:mm:ss.SSS a' , // 11:20:01.123 AM,
'second' : 'h:mm:ss a' , // 11:20:01 AM
'minute' : 'h:mm:ss a' , // 11:20:01 AM
'hour' : 'MMM D, hA' , // Sept 4, 5PM
'day' : 'll' , // Sep 4 2015
'week' : 'll' , // Week 46, or maybe "[W]WW - YYYY" ?
'month' : 'MMM YYYY' , // Sept 2015
'quarter' : '[Q]Q - YYYY' , // Q3
'year' : 'YYYY' // 2015
}
} ,
ticks : {
autoSkip : false
}
} ;
var TimeScale = Chart . Scale . extend ( {
initialize : function ( ) {
if ( ! moment ) {
throw new Error ( 'Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com' ) ;
}
Chart . Scale . prototype . initialize . call ( this ) ;
} ,
getLabelMoment : function ( datasetIndex , index ) {
return this . labelMoments [ datasetIndex ] [ index ] ;
} ,
determineDataLimits : function ( ) {
this . labelMoments = [ ] ;
// Only parse these once. If the dataset does not have data as x,y pairs, we will use
// these
var scaleLabelMoments = [ ] ;
if ( this . chart . data . labels && this . chart . data . labels . length > 0 ) {
helpers . each ( this . chart . data . labels , function ( label , index ) {
var labelMoment = this . parseTime ( label ) ;
if ( this . options . time . round ) {
labelMoment . startOf ( this . options . time . round ) ;
}
scaleLabelMoments . push ( labelMoment ) ;
} , this ) ;
this . firstTick = moment . min . call ( this , scaleLabelMoments ) ;
this . lastTick = moment . max . call ( this , scaleLabelMoments ) ;
} else {
this . firstTick = null ;
this . lastTick = null ;
}
helpers . each ( this . chart . data . datasets , function ( dataset , datasetIndex ) {
var momentsForDataset = [ ] ;
if ( typeof dataset . data [ 0 ] === 'object' ) {
helpers . each ( dataset . data , function ( value , index ) {
var labelMoment = this . parseTime ( this . getRightValue ( value ) ) ;
if ( this . options . time . round ) {
labelMoment . startOf ( this . options . time . round ) ;
}
momentsForDataset . push ( labelMoment ) ;
// May have gone outside the scale ranges, make sure we keep the first and last ticks updated
this . firstTick = this . firstTick !== null ? moment . min ( this . firstTick , labelMoment ) : labelMoment ;
this . lastTick = this . lastTick !== null ? moment . max ( this . lastTick , labelMoment ) : labelMoment ;
} , this ) ;
} else {
// We have no labels. Use the ones from the scale
momentsForDataset = scaleLabelMoments ;
}
this . labelMoments . push ( momentsForDataset ) ;
} , this ) ;
// Set these after we've done all the data
if ( this . options . time . min ) {
this . firstTick = this . parseTime ( this . options . time . min ) ;
}
if ( this . options . time . max ) {
this . lastTick = this . parseTime ( this . options . time . max ) ;
}
// We will modify these, so clone for later
this . firstTick = ( this . firstTick || moment ( ) ) . clone ( ) ;
this . lastTick = ( this . lastTick || moment ( ) ) . clone ( ) ;
} ,
buildTicks : function ( index ) {
this . ctx . save ( ) ;
var tickFontSize = helpers . getValueOrDefault ( this . options . ticks . fontSize , Chart . defaults . global . defaultFontSize ) ;
var tickFontStyle = helpers . getValueOrDefault ( this . options . ticks . fontStyle , Chart . defaults . global . defaultFontStyle ) ;
var tickFontFamily = helpers . getValueOrDefault ( this . options . ticks . fontFamily , Chart . defaults . global . defaultFontFamily ) ;
var tickLabelFont = helpers . fontString ( tickFontSize , tickFontStyle , tickFontFamily ) ;
this . ctx . font = tickLabelFont ;
this . ticks = [ ] ;
this . unitScale = 1 ; // How much we scale the unit by, ie 2 means 2x unit per step
this . scaleSizeInUnits = 0 ; // How large the scale is in the base unit (seconds, minutes, etc)
// Set unit override if applicable
if ( this . options . time . unit ) {
this . tickUnit = this . options . time . unit || 'day' ;
this . displayFormat = this . options . time . displayFormats [ this . tickUnit ] ;
this . scaleSizeInUnits = this . lastTick . diff ( this . firstTick , this . tickUnit , true ) ;
this . unitScale = helpers . getValueOrDefault ( this . options . time . unitStepSize , 1 ) ;
} else {
// Determine the smallest needed unit of the time
var innerWidth = this . isHorizontal ( ) ? this . width - ( this . paddingLeft + this . paddingRight ) : this . height - ( this . paddingTop + this . paddingBottom ) ;
// Crude approximation of what the label length might be
var tempFirstLabel = this . tickFormatFunction ( this . firstTick , 0 , [ ] ) ;
var tickLabelWidth = this . ctx . measureText ( tempFirstLabel ) . width ;
var cosRotation = Math . cos ( helpers . toRadians ( this . options . ticks . maxRotation ) ) ;
var sinRotation = Math . sin ( helpers . toRadians ( this . options . ticks . maxRotation ) ) ;
tickLabelWidth = ( tickLabelWidth * cosRotation ) + ( tickFontSize * sinRotation ) ;
var labelCapacity = innerWidth / ( tickLabelWidth ) ;
// Start as small as possible
this . tickUnit = 'millisecond' ;
this . scaleSizeInUnits = this . lastTick . diff ( this . firstTick , this . tickUnit , true ) ;
this . displayFormat = this . options . time . displayFormats [ this . tickUnit ] ;
var unitDefinitionIndex = 0 ;
var unitDefinition = time . units [ unitDefinitionIndex ] ;
// While we aren't ideal and we don't have units left
while ( unitDefinitionIndex < time . units . length ) {
// Can we scale this unit. If `false` we can scale infinitely
this . unitScale = 1 ;
if ( helpers . isArray ( unitDefinition . steps ) && Math . ceil ( this . scaleSizeInUnits / labelCapacity ) < helpers . max ( unitDefinition . steps ) ) {
// Use one of the prefedined steps
for ( var idx = 0 ; idx < unitDefinition . steps . length ; ++ idx ) {
if ( unitDefinition . steps [ idx ] >= Math . ceil ( this . scaleSizeInUnits / labelCapacity ) ) {
this . unitScale = helpers . getValueOrDefault ( this . options . time . unitStepSize , unitDefinition . steps [ idx ] ) ;
break ;
}
}
break ;
} else if ( ( unitDefinition . maxStep === false ) || ( Math . ceil ( this . scaleSizeInUnits / labelCapacity ) < unitDefinition . maxStep ) ) {
// We have a max step. Scale this unit
this . unitScale = helpers . getValueOrDefault ( this . options . time . unitStepSize , Math . ceil ( this . scaleSizeInUnits / labelCapacity ) ) ;
break ;
} else {
// Move to the next unit up
++ unitDefinitionIndex ;
unitDefinition = time . units [ unitDefinitionIndex ] ;
this . tickUnit = unitDefinition . name ;
this . scaleSizeInUnits = this . lastTick . diff ( this . firstTick , this . tickUnit , true ) ;
this . displayFormat = this . options . time . displayFormats [ unitDefinition . name ] ;
}
}
}
var roundedStart ;
// Only round the first tick if we have no hard minimum
if ( ! this . options . time . min ) {
this . firstTick . startOf ( this . tickUnit ) ;
roundedStart = this . firstTick ;
} else {
roundedStart = this . firstTick . clone ( ) . startOf ( this . tickUnit ) ;
}
// Only round the last tick if we have no hard maximum
if ( ! this . options . time . max ) {
this . lastTick . endOf ( this . tickUnit ) ;
}
this . smallestLabelSeparation = this . width ;
helpers . each ( this . chart . data . datasets , function ( dataset , datasetIndex ) {
for ( var i = 1 ; i < this . labelMoments [ datasetIndex ] . length ; i ++ ) {
this . smallestLabelSeparation = Math . min ( this . smallestLabelSeparation , this . labelMoments [ datasetIndex ] [ i ] . diff ( this . labelMoments [ datasetIndex ] [ i - 1 ] , this . tickUnit , true ) ) ;
}
} , this ) ;
// Tick displayFormat override
if ( this . options . time . displayFormat ) {
this . displayFormat = this . options . time . displayFormat ;
}
// first tick. will have been rounded correctly if options.time.min is not specified
this . ticks . push ( this . firstTick . clone ( ) ) ;
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
for ( var i = 1 ; i < this . scaleSizeInUnits ; ++ i ) {
var newTick = roundedStart . clone ( ) . add ( i , this . tickUnit ) ;
// Are we greater than the max time
if ( this . options . time . max && newTick . diff ( this . lastTick , this . tickUnit , true ) >= 0 ) {
break ;
}
if ( i % this . unitScale === 0 ) {
this . ticks . push ( newTick ) ;
}
}
// Always show the right tick
if ( this . ticks [ this . ticks . length - 1 ] . diff ( this . lastTick , this . tickUnit ) !== 0 || this . scaleSizeInUnits === 0 ) {
// this is a weird case. If the <max> option is the same as the end option, we can't just diff the times because the tick was created from the roundedStart
// but the last tick was not rounded.
if ( this . options . time . max ) {
this . ticks . push ( this . lastTick . clone ( ) ) ;
this . scaleSizeInUnits = this . lastTick . diff ( this . ticks [ 0 ] , this . tickUnit , true ) ;
} else {
this . scaleSizeInUnits = Math . ceil ( this . scaleSizeInUnits / this . unitScale ) * this . unitScale ;
this . ticks . push ( this . firstTick . clone ( ) . add ( this . scaleSizeInUnits , this . tickUnit ) ) ;
this . lastTick = this . ticks [ this . ticks . length - 1 ] . clone ( ) ;
}
}
this . ctx . restore ( ) ;
} ,
// Get tooltip label
getLabelForIndex : function ( index , datasetIndex ) {
var label = this . chart . data . labels && index < this . chart . data . labels . length ? this . chart . data . labels [ index ] : '' ;
if ( typeof this . chart . data . datasets [ datasetIndex ] . data [ 0 ] === 'object' ) {
label = this . getRightValue ( this . chart . data . datasets [ datasetIndex ] . data [ index ] ) ;
}
// Format nicely
if ( this . options . time . tooltipFormat ) {
label = this . parseTime ( label ) . format ( this . options . time . tooltipFormat ) ;
}
return label ;
} ,
// Function to format an individual tick mark
tickFormatFunction : function tickFormatFunction ( tick , index , ticks ) {
var formattedTick = tick . format ( this . displayFormat ) ;
if ( this . options . ticks . userCallback ) {
return this . options . ticks . userCallback ( formattedTick , index , ticks ) ;
} else {
return formattedTick ;
}
} ,
convertTicksToLabels : function ( ) {
this . ticks = this . ticks . map ( this . tickFormatFunction , this ) ;
} ,
getPixelForValue : function ( value , index , datasetIndex , includeOffset ) {
var labelMoment = this . getLabelMoment ( datasetIndex , index ) ;
if ( labelMoment ) {
var offset = labelMoment . diff ( this . firstTick , this . tickUnit , true ) ;
var decimal = offset / this . scaleSizeInUnits ;
if ( this . isHorizontal ( ) ) {
var innerWidth = this . width - ( this . paddingLeft + this . paddingRight ) ;
var valueWidth = innerWidth / Math . max ( this . ticks . length - 1 , 1 ) ;
var valueOffset = ( innerWidth * decimal ) + this . paddingLeft ;
return this . left + Math . round ( valueOffset ) ;
} else {
var innerHeight = this . height - ( this . paddingTop + this . paddingBottom ) ;
var valueHeight = innerHeight / Math . max ( this . ticks . length - 1 , 1 ) ;
var heightOffset = ( innerHeight * decimal ) + this . paddingTop ;
return this . top + Math . round ( heightOffset ) ;
}
}
} ,
parseTime : function ( label ) {
if ( typeof this . options . time . parser === 'string' ) {
return moment ( label , this . options . time . parser ) ;
}
if ( typeof this . options . time . parser === 'function' ) {
return this . options . time . parser ( label ) ;
}
// Date objects
if ( typeof label . getMonth === 'function' || typeof label === 'number' ) {
return moment ( label ) ;
}
// Moment support
if ( label . isValid && label . isValid ( ) ) {
return label ;
}
// Custom parsing (return an instance of moment)
if ( typeof this . options . time . format !== 'string' && this . options . time . format . call ) {
console . warn ( "options.time.format is deprecated and replaced by options.time.parser. See http://nnnick.github.io/Chart.js/docs-v2/#scales-time-scale" ) ;
return this . options . time . format ( label ) ;
}
// Moment format parsing
return moment ( label , this . options . time . format ) ;
}
} ) ;
Chart . scaleService . registerScaleType ( "time" , TimeScale , defaultConfig ) ;
} ;
} , { "moment" : 1 } ] } , { } , [ 7 ] ) ;