Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…

/*! | |
* Chart.js | |
* http://chartjs.org/ | |
* Version: 2.7.0 | |
* | |
* Copyright 2017 Nick Downie | |
* Released under the MIT license | |
* https://github.com/chartjs/Chart.js/blob/master/LICENSE.md | |
*/ | |
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (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 */ | |
var colorNames = require(6); | |
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})$/i, | |
hex = /^#([a-fA-F0-9]{6})$/i, | |
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, | |
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, | |
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; | |
} | |
},{"6":6}],3:[function(require,module,exports){ | |
/* MIT license */ | |
var convert = require(5); | |
var string = require(2); | |
var Color = function (obj) { | |
if (obj instanceof Color) { | |
return obj; | |
} | |
if (!(this instanceof Color)) { | |
return new Color(obj); | |
} | |
this.valid = false; | |
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 | |
var vals; | |
if (typeof obj === 'string') { | |
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 if (typeof obj === 'object') { | |
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); | |
} | |
} | |
}; | |
Color.prototype = { | |
isValid: function () { | |
return this.valid; | |
}, | |
rgb: function () { | |
return this.setSpace('rgb', arguments); | |
}, | |
hsl: function () { | |
return this.setSpace('hsl', arguments); | |
}, | |
hsv: function () { | |
return this.setSpace('hsv', arguments); | |
}, | |
hwb: function () { | |
return this.setSpace('hwb', arguments); | |
}, | |
cmyk: function () { | |
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 () { | |
var values = this.values; | |
if (values.alpha !== 1) { | |
return values.hwb.concat([values.alpha]); | |
} | |
return values.hwb; | |
}, | |
cmykArray: function () { | |
return this.values.cmyk; | |
}, | |
rgbaArray: function () { | |
var values = this.values; | |
return values.rgb.concat([values.alpha]); | |
}, | |
hslaArray: function () { | |
var values = this.values; | |
return values.hsl.concat([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) { | |
if (val) { | |
val %= 360; | |
val = val < 0 ? 360 + val : 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 () { | |
var rgb = this.values.rgb; | |
return (rgb[0] << 16) | (rgb[1] << 8) | 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); | |
if (contrastRatio >= 7.1) { | |
return 'AAA'; | |
} | |
return (contrastRatio >= 4.5) ? 'AA' : ''; | |
}, | |
dark: function () { | |
// YIQ equation from http://24ways.org/2010/calculating-color-contrast | |
var rgb = this.values.rgb; | |
var 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) { | |
var hsl = this.values.hsl; | |
hsl[2] += hsl[2] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
darken: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[2] -= hsl[2] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
saturate: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[1] += hsl[1] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
desaturate: function (ratio) { | |
var hsl = this.values.hsl; | |
hsl[1] -= hsl[1] * ratio; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
whiten: function (ratio) { | |
var hwb = this.values.hwb; | |
hwb[1] += hwb[1] * ratio; | |
this.setValues('hwb', hwb); | |
return this; | |
}, | |
blacken: function (ratio) { | |
var hwb = this.values.hwb; | |
hwb[2] += hwb[2] * ratio; | |
this.setValues('hwb', 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) { | |
var alpha = this.values.alpha; | |
this.setValues('alpha', alpha - (alpha * ratio)); | |
return this; | |
}, | |
opaquer: function (ratio) { | |
var alpha = this.values.alpha; | |
this.setValues('alpha', alpha + (alpha * ratio)); | |
return this; | |
}, | |
rotate: function (degrees) { | |
var hsl = this.values.hsl; | |
var hue = (hsl[0] + degrees) % 360; | |
hsl[0] = hue < 0 ? 360 + hue : hue; | |
this.setValues('hsl', hsl); | |
return this; | |
}, | |
/** | |
* Ported from sass implementation in C | |
* https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 | |
*/ | |
mix: function (mixinColor, weight) { | |
var color1 = this; | |
var color2 = mixinColor; | |
var p = weight === undefined ? 0.5 : weight; | |
var w = 2 * p - 1; | |
var a = color1.alpha() - color2.alpha(); | |
var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; | |
var w2 = 1 - w1; | |
return this | |
.rgb( | |
w1 * color1.red() + w2 * color2.red(), | |
w1 * color1.green() + w2 * color2.green(), | |
w1 * color1.blue() + w2 * color2.blue() | |
) | |
.alpha(color1.alpha() * p + color2.alpha() * (1 - p)); | |
}, | |
toJSON: function () { | |
return this.rgb(); | |
}, | |
clone: function () { | |
// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, | |
// making the final build way to big to embed in Chart.js. So let's do it manually, | |
// assuming that values to clone are 1 dimension arrays containing only numbers, | |
// except 'alpha' which is a number. | |
var result = new Color(); | |
var source = this.values; | |
var target = result.values; | |
var value, type; | |
for (var prop in source) { | |
if (source.hasOwnProperty(prop)) { | |
value = source[prop]; | |
type = ({}).toString.call(value); | |
if (type === '[object Array]') { | |
target[prop] = value.slice(0); | |
} else if (type === '[object Number]') { | |
target[prop] = value; | |
} else { | |
console.error('unexpected color value:', value); | |
} | |
} | |
} | |
return result; | |
} | |
}; | |
Color.prototype.spaces = { | |
rgb: ['red', 'green', 'blue'], | |
hsl: ['hue', 'saturation', 'lightness'], | |
hsv: ['hue', 'saturation', 'value'], | |
hwb: ['hue', 'whiteness', 'blackness'], | |
cmyk: ['cyan', 'magenta', 'yellow', 'black'] | |
}; | |
Color.prototype.maxes = { | |
rgb: [255, 255, 255], | |
hsl: [360, 100, 100], | |
hsv: [360, 100, 100], | |
hwb: [360, 100, 100], | |
cmyk: [100, 100, 100, 100] | |
}; | |
Color.prototype.getValues = function (space) { | |
var values = this.values; | |
var vals = {}; | |
for (var i = 0; i < space.length; i++) { | |
vals[space.charAt(i)] = values[space][i]; | |
} | |
if (values.alpha !== 1) { | |
vals.a = values.alpha; | |
} | |
// {r: 255, g: 255, b: 255, a: 0.4} | |
return vals; | |
}; | |
Color.prototype.setValues = function (space, vals) { | |
var values = this.values; | |
var spaces = this.spaces; | |
var maxes = this.maxes; | |
var alpha = 1; | |
var i; | |
this.valid = true; | |
if (space === 'alpha') { | |
alpha = vals; | |
} else if (vals.length) { | |
// [10, 10, 10] | |
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 (i = 0; i < space.length; i++) { | |
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 (i = 0; i < space.length; i++) { | |
values[space][i] = vals[chans[i]]; | |
} | |
alpha = vals.alpha; | |
} | |
values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); | |
if (space === 'alpha') { | |
return false; | |
} | |
var capped; | |
// cap values of the space prior converting all values | |
for (i = 0; i < space.length; i++) { | |
capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); | |
values[space][i] = Math.round(capped); | |
} | |
// convert to all the other color spaces | |
for (var sname in spaces) { | |
if (sname !== space) { | |
values[sname] = convert[space][sname](values[space]); | |
} | |
} | |
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) { | |
var svalues = this.values[space]; | |
if (val === undefined) { | |
// color.red() | |
return svalues[index]; | |
} else if (val === svalues[index]) { | |
// color.red(color.red()) | |
return this; | |
} | |
// color.red(100) | |
svalues[index] = val; | |
this.setValues(space, svalues); | |
return this; | |
}; | |
if (typeof window !== 'undefined') { | |
window.Color = Color; | |
} | |
module.exports = Color; | |
},{"2":2,"5":5}],4:[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; | |
} | |
},{}],5:[function(require,module,exports){ | |
var conversions = require(4); | |
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 = {}; | |
}; | |
/* Either get the values for a space or | |
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; | |
}; | |
/* Get the values for a space. If there's already | |
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; | |
},{"4":4}],6:[function(require,module,exports){ | |
'use strict' | |
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] | |
}; | |
},{}],7:[function(require,module,exports){ | |
/** | |
* @namespace Chart | |
*/ | |
var Chart = require(29)(); | |
Chart.helpers = require(45); | |
// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! | |
require(27)(Chart); | |
Chart.defaults = require(25); | |
Chart.Element = require(26); | |
Chart.elements = require(40); | |
Chart.Interaction = require(28); | |
Chart.platform = require(48); | |
require(31)(Chart); | |
require(22)(Chart); | |
require(23)(Chart); | |
require(24)(Chart); | |
require(30)(Chart); | |
require(33)(Chart); | |
require(32)(Chart); | |
require(35)(Chart); | |
require(54)(Chart); | |
require(52)(Chart); | |
require(53)(Chart); | |
require(55)(Chart); | |
require(56)(Chart); | |
require(57)(Chart); | |
// Controllers must be loaded after elements | |
// See Chart.core.datasetController.dataElementType | |
require(15)(Chart); | |
require(16)(Chart); | |
require(17)(Chart); | |
require(18)(Chart); | |
require(19)(Chart); | |
require(20)(Chart); | |
require(21)(Chart); | |
require(8)(Chart); | |
require(9)(Chart); | |
require(10)(Chart); | |
require(11)(Chart); | |
require(12)(Chart); | |
require(13)(Chart); | |
require(14)(Chart); | |
// Loading built-it plugins | |
var plugins = []; | |
plugins.push( | |
require(49)(Chart), | |
require(50)(Chart), | |
require(51)(Chart) | |
); | |
Chart.plugins.register(plugins); | |
Chart.platform.initialize(); | |
module.exports = Chart; | |
if (typeof window !== 'undefined') { | |
window.Chart = Chart; | |
} | |
// DEPRECATIONS | |
/** | |
* Provided for backward compatibility, use Chart.helpers.canvas instead. | |
* @namespace Chart.canvasHelpers | |
* @deprecated since version 2.6.0 | |
* @todo remove at version 3 | |
* @private | |
*/ | |
Chart.canvasHelpers = Chart.helpers.canvas; | |
},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],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) { | |
Chart.Radar = function(context, config) { | |
config.type = 'radar'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],14:[function(require,module,exports){ | |
'use strict'; | |
module.exports = function(Chart) { | |
Chart.Scatter = function(context, config) { | |
config.type = 'scatter'; | |
return new Chart(context, config); | |
}; | |
}; | |
},{}],15:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var elements = require(40); | |
var helpers = require(45); | |
defaults._set('bar', { | |
hover: { | |
mode: 'label' | |
}, | |
scales: { | |
xAxes: [{ | |
type: 'category', | |
// Specific to Bar Controller | |
categoryPercentage: 0.8, | |
barPercentage: 0.9, | |
// offset settings | |
offset: true, | |
// grid line settings | |
gridLines: { | |
offsetGridLines: true | |
} | |
}], | |
yAxes: [{ | |
type: 'linear' | |
}] | |
} | |
}); | |
defaults._set('horizontalBar', { | |
hover: { | |
mode: 'index', | |
axis: 'y' | |
}, | |
scales: { | |
xAxes: [{ | |
type: 'linear', | |
position: 'bottom' | |
}], | |
yAxes: [{ | |
position: 'left', | |
type: 'category', | |
// Specific to Horizontal Bar Controller | |
categoryPercentage: 0.8, | |
barPercentage: 0.9, | |
// offset settings | |
offset: true, | |
// grid line settings | |
gridLines: { | |
offsetGridLines: true | |
} | |
}] | |
}, | |
elements: { | |
rectangle: { | |
borderSkipped: 'left' | |
} | |
}, | |
tooltips: { | |
callbacks: { | |
title: function(item, data) { | |
// Pick first xLabel for now | |
var title = ''; | |
if (item.length > 0) { | |
if (item[0].yLabel) { | |
title = item[0].yLabel; | |
} else if (data.labels.length > 0 && item[0].index < data.labels.length) { | |
title = data.labels[item[0].index]; | |
} | |
} | |
return title; | |
}, | |
label: function(item, data) { | |
var datasetLabel = data.datasets[item.datasetIndex].label || ''; | |
return datasetLabel + ': ' + item.xLabel; | |
} | |
}, | |
mode: 'index', | |
axis: 'y' | |
} | |
}); | |
module.exports = function(Chart) { | |
Chart.controllers.bar = Chart.DatasetController.extend({ | |
dataElementType: elements.Rectangle, | |
initialize: function() { | |
var me = this; | |
var meta; | |
Chart.DatasetController.prototype.initialize.apply(me, arguments); | |
meta = me.getMeta(); | |
meta.stack = me.getDataset().stack; | |
meta.bar = true; | |
}, | |
update: function(reset) { | |
var me = this; | |
var rects = me.getMeta().data; | |
var i, ilen; | |
me._ruler = me.getRuler(); | |
for (i = 0, ilen = rects.length; i < ilen; ++i) { | |
me.updateElement(rects[i], i, reset); | |
} | |
}, | |
updateElement: function(rectangle, index, reset) { | |
var me = this; | |
var chart = me.chart; | |
var meta = me.getMeta(); | |
var dataset = me.getDataset(); | |
var custom = rectangle.custom || {}; | |
var rectangleOptions = chart.options.elements.rectangle; | |
rectangle._xScale = me.getScaleForId(meta.xAxisID); | |
rectangle._yScale = me.getScaleForId(meta.yAxisID); | |
rectangle._datasetIndex = me.index; | |
rectangle._index = index; | |
rectangle._model = { | |
datasetLabel: dataset.label, | |
label: chart.data.labels[index], | |
borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), | |
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) | |
}; | |
me.updateElementGeometry(rectangle, index, reset); | |
rectangle.pivot(); | |
}, | |
/** | |
* @private | |
*/ | |
updateElementGeometry: function(rectangle, index, reset) { | |
var me = this; | |
var model = rectangle._model; | |
var vscale = me.getValueScale(); | |
var base = vscale.getBasePixel(); | |
var horizontal = vscale.isHorizontal(); | |
var ruler = me._ruler || me.getRuler(); | |
var vpixels = me.calculateBarValuePixels(me.index, index); | |
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); | |
model.horizontal = horizontal; | |
model.base = reset ? base : vpixels.base; | |
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; | |
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; | |
model.height = horizontal ? ipixels.size : undefined; | |
model.width = horizontal ? undefined : ipixels.size; | |
}, | |
/** | |
* @private | |
*/ | |
getValueScaleId: function() { | |
return this.getMeta().yAxisID; | |
}, | |
/** | |
* @private | |
*/ | |
getIndexScaleId: function() { | |
return this.getMeta().xAxisID; | |
}, | |
/** | |
* @private | |
*/ | |
getValueScale: function() { | |
return this.getScaleForId(this.getValueScaleId()); | |
}, | |
/** | |
* @private | |
*/ | |
getIndexScale: function() { | |
return this.getScaleForId(this.getIndexScaleId()); | |
}, | |
/** | |
* Returns the effective number of stacks based on groups and bar visibility. | |
* @private | |
*/ | |
getStackCount: function(last) { | |
var me = this; | |
var chart = me.chart; | |
var scale = me.getIndexScale(); | |
var stacked = scale.options.stacked; | |
var ilen = last === undefined ? chart.data.datasets.length : last + 1; | |
var stacks = []; | |
var i, meta; | |
for (i = 0; i < ilen; ++i) { | |
meta = chart.getDatasetMeta(i); | |
if (meta.bar && chart.isDatasetVisible(i) && | |
(stacked === false || | |
(stacked === true && stacks.indexOf(meta.stack) === -1) || | |
(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { | |
stacks.push(meta.stack); | |
} | |
} | |
return stacks.length; | |
}, | |
/** | |
* Returns the stack index for the given dataset based on groups and bar visibility. | |
* @private | |
*/ | |
getStackIndex: function(datasetIndex) { | |
return this.getStackCount(datasetIndex) - 1; | |
}, | |
/** | |
* @private | |
*/ | |
getRuler: function() { | |
var me = this; | |
var scale = me.getIndexScale(); | |
var stackCount = me.getStackCount(); | |
var datasetIndex = me.index; | |
var pixels = []; | |
var isHorizontal = scale.isHorizontal(); | |
var start = isHorizontal ? scale.left : scale.top; | |
var end = start + (isHorizontal ? scale.width : scale.height); | |
var i, ilen; | |
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { | |
pixels.push(scale.getPixelForValue(null, i, datasetIndex)); | |
} | |
return { | |
pixels: pixels, | |
start: start, | |
end: end, | |
stackCount: stackCount, | |
scale: scale | |
}; | |
}, | |
/** | |
* Note: pixel values are not clamped to the scale area. | |
* @private | |
*/ | |
calculateBarValuePixels: function(datasetIndex, index) { | |
var me = this; | |
var chart = me.chart; | |
var meta = me.getMeta(); | |
var scale = me.getValueScale(); | |
var datasets = chart.data.datasets; | |
var value = scale.getRightValue(datasets[datasetIndex].data[index]); | |
var stacked = scale.options.stacked; | |
var stack = meta.stack; | |
var start = 0; | |
var i, imeta, ivalue, base, head, size; | |
if (stacked || (stacked === undefined && stack !== undefined)) { | |
for (i = 0; i < datasetIndex; ++i) { | |
imeta = chart.getDatasetMeta(i); | |
if (imeta.bar && | |
imeta.stack === stack && | |
imeta.controller.getValueScaleId() === scale.id && | |
chart.isDatasetVisible(i)) { | |
ivalue = scale.getRightValue(datasets[i].data[index]); | |
if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { | |
start += ivalue; | |
} | |
} | |
} | |
} | |
base = scale.getPixelForValue(start); | |
head = scale.getPixelForValue(start + value); | |
size = (head - base) / 2; | |
return { | |
size: size, | |
base: base, | |
head: head, | |
center: head + size / 2 | |
}; | |
}, | |
/** | |
* @private | |
*/ | |
calculateBarIndexPixels: function(datasetIndex, index, ruler) { | |
var me = this; | |
var options = ruler.scale.options; | |
var stackIndex = me.getStackIndex(datasetIndex); | |
var pixels = ruler.pixels; | |
var base = pixels[index]; | |
var length = pixels.length; | |
var start = ruler.start; | |
var end = ruler.end; | |
var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size; | |
if (length === 1) { | |
leftSampleSize = base > start ? base - start : end - base; | |
rightSampleSize = base < end ? end - base : base - start; | |
} else { | |
if (index > 0) { | |
leftSampleSize = (base - pixels[index - 1]) / 2; | |
if (index === length - 1) { | |
rightSampleSize = leftSampleSize; | |
} | |
} | |
if (index < length - 1) { | |
rightSampleSize = (pixels[index + 1] - base) / 2; | |
if (index === 0) { | |
leftSampleSize = rightSampleSize; | |
} | |
} | |
} | |
leftCategorySize = leftSampleSize * options.categoryPercentage; | |
rightCategorySize = rightSampleSize * options.categoryPercentage; | |
fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount; | |
size = fullBarSize * options.barPercentage; | |
size = Math.min( | |
helpers.valueOrDefault(options.barThickness, size), | |
helpers.valueOrDefault(options.maxBarThickness, Infinity)); | |
base -= leftCategorySize; | |
base += fullBarSize * stackIndex; | |
base += (fullBarSize - size) / 2; | |
return { | |
size: size, | |
base: base, | |
head: base + size, | |
center: base + size / 2 | |
}; | |
}, | |
draw: function() { | |
var me = this; | |
var chart = me.chart; | |
var scale = me.getValueScale(); | |
var rects = me.getMeta().data; | |
var dataset = me.getDataset(); | |
var ilen = rects.length; | |
var i = 0; | |
helpers.canvas.clipArea(chart.ctx, chart.chartArea); | |
for (; i < ilen; ++i) { | |
if (!isNaN(scale.getRightValue(dataset.data[i]))) { | |
rects[i].draw(); | |
} | |
} | |
helpers.canvas.unclipArea(chart.ctx); | |
}, | |
setHoverStyle: function(rectangle) { | |
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; | |
var index = rectangle._index; | |
var custom = rectangle.custom || {}; | |
var model = rectangle._model; | |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); | |
}, | |
removeHoverStyle: function(rectangle) { | |
var dataset = this.chart.data.datasets[rectangle._datasetIndex]; | |
var index = rectangle._index; | |
var custom = rectangle.custom || {}; | |
var model = rectangle._model; | |
var rectangleElementOptions = this.chart.options.elements.rectangle; | |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); | |
model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); | |
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); | |
} | |
}); | |
Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ | |
/** | |
* @private | |
*/ | |
getValueScaleId: function() { | |
return this.getMeta().xAxisID; | |
}, | |
/** | |
* @private | |
*/ | |
getIndexScaleId: function() { | |
return this.getMeta().yAxisID; | |
} | |
}); | |
}; | |
},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var elements = require(40); | |
var helpers = require(45); | |
defaults._set('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() { | |
// Title doesn't make sense for scatter since we format the data as a point | |
return ''; | |
}, | |
label: function(item, data) { | |
var datasetLabel = data.datasets[item.datasetIndex].label || ''; | |
var dataPoint = data.datasets[item.datasetIndex].data[item.index]; | |
return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; | |
} | |
} | |
} | |
}); | |
module.exports = function(Chart) { | |
Chart.controllers.bubble = Chart.DatasetController.extend({ | |
/** | |
* @protected | |
*/ | |
dataElementType: elements.Point, | |
/** | |
* @protected | |
*/ | |
update: function(reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var points = meta.data; | |
// Update Points | |
helpers.each(points, function(point, index) { | |
me.updateElement(point, index, reset); | |
}); | |
}, | |
/** | |
* @protected | |
*/ | |
updateElement: function(point, index, reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var custom = point.custom || {}; | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var options = me._resolveElementOptions(point, index); | |
var data = me.getDataset().data[index]; | |
var dsIndex = me.index; | |
var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); | |
var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); | |
point._xScale = xScale; | |
point._yScale = yScale; | |
point._options = options; | |
point._datasetIndex = dsIndex; | |
point._index = index; | |
point._model = { | |
backgroundColor: options.backgroundColor, | |
borderColor: options.borderColor, | |
borderWidth: options.borderWidth, | |
hitRadius: options.hitRadius, | |
pointStyle: options.pointStyle, | |
radius: reset ? 0 : options.radius, | |
skip: custom.skip || isNaN(x) || isNaN(y), | |
x: x, | |
y: y, | |
}; | |
point.pivot(); | |
}, | |
/** | |
* @protected | |
*/ | |
setHoverStyle: function(point) { | |
var model = point._model; | |
var options = point._options; | |
model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); | |
model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); | |
model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); | |
model.radius = options.radius + options.hoverRadius; | |
}, | |
/** | |
* @protected | |
*/ | |
removeHoverStyle: function(point) { | |
var model = point._model; | |
var options = point._options; | |
model.backgroundColor = options.backgroundColor; | |
model.borderColor = options.borderColor; | |
model.borderWidth = options.borderWidth; | |
model.radius = options.radius; | |
}, | |
/** | |
* @private | |
*/ | |
_resolveElementOptions: function(point, index) { | |
var me = this; | |
var chart = me.chart; | |
var datasets = chart.data.datasets; | |
var dataset = datasets[me.index]; | |
var custom = point.custom || {}; | |
var options = chart.options.elements.point; | |
var resolve = helpers.options.resolve; | |
var data = dataset.data[index]; | |
var values = {}; | |
var i, ilen, key; | |
// Scriptable options | |
var context = { | |
chart: chart, | |
dataIndex: index, | |
dataset: dataset, | |
datasetIndex: me.index | |
}; | |
var keys = [ | |
'backgroundColor', | |
'borderColor', | |
'borderWidth', | |
'hoverBackgroundColor', | |
'hoverBorderColor', | |
'hoverBorderWidth', | |
'hoverRadius', | |
'hitRadius', | |
'pointStyle' | |
]; | |
for (i = 0, ilen = keys.length; i < ilen; ++i) { | |
key = keys[i]; | |
values[key] = resolve([ | |
custom[key], | |
dataset[key], | |
options[key] | |
], context, index); | |
} | |
// Custom radius resolution | |
values.radius = resolve([ | |
custom.radius, | |
data ? data.r : undefined, | |
dataset.radius, | |
options.radius | |
], context, index); | |
return values; | |
} | |
}); | |
}; | |
},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var elements = require(40); | |
var helpers = require(45); | |
defaults._set('doughnut', { | |
animation: { | |
// Boolean - Whether we animate the rotation of the Doughnut | |
animateRotate: true, | |
// Boolean - Whether we animate scaling the Doughnut from the centre | |
animateScale: false | |
}, | |
hover: { | |
mode: 'single' | |
}, | |
legendCallback: function(chart) { | |
var text = []; | |
text.push('<ul class="' + chart.id + '-legend">'); | |
var data = chart.data; | |
var datasets = data.datasets; | |
var labels = data.labels; | |
if (datasets.length) { | |
for (var i = 0; i < datasets[0].data.length; ++i) { | |
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); | |
if (labels[i]) { | |
text.push(labels[i]); | |
} | |
text.push('</li>'); | |
} | |
} | |
text.push('</ul>'); | |
return text.join(''); | |
}, | |
legend: { | |
labels: { | |
generateLabels: function(chart) { | |
var data = chart.data; | |
if (data.labels.length && data.datasets.length) { | |
return data.labels.map(function(label, i) { | |
var meta = chart.getDatasetMeta(0); | |
var ds = data.datasets[0]; | |
var arc = meta.data[i]; | |
var custom = arc && arc.custom || {}; | |
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; | |
var arcOpts = chart.options.elements.arc; | |
var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); | |
var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); | |
var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); | |
return { | |
text: label, | |
fillStyle: fill, | |
strokeStyle: stroke, | |
lineWidth: bw, | |
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, | |
// Extra data used for toggling the correct item | |
index: i | |
}; | |
}); | |
} | |
return []; | |
} | |
}, | |
onClick: function(e, legendItem) { | |
var index = legendItem.index; | |
var chart = this.chart; | |
var i, ilen, meta; | |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { | |
meta = chart.getDatasetMeta(i); | |
// toggle visibility of index if exists | |
if (meta.data[index]) { | |
meta.data[index].hidden = !meta.data[index].hidden; | |
} | |
} | |
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) { | |
var dataLabel = data.labels[tooltipItem.index]; | |
var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; | |
if (helpers.isArray(dataLabel)) { | |
// show value on first line of multiline label | |
// need to clone because we are changing the value | |
dataLabel = dataLabel.slice(); | |
dataLabel[0] += value; | |
} else { | |
dataLabel += value; | |
} | |
return dataLabel; | |
} | |
} | |
} | |
}); | |
defaults._set('pie', helpers.clone(defaults.doughnut)); | |
defaults._set('pie', { | |
cutoutPercentage: 0 | |
}); | |
module.exports = function(Chart) { | |
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ | |
dataElementType: elements.Arc, | |
linkScales: helpers.noop, | |
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly | |
getRingIndex: function(datasetIndex) { | |
var ringIndex = 0; | |
for (var j = 0; j < datasetIndex; ++j) { | |
if (this.chart.isDatasetVisible(j)) { | |
++ringIndex; | |
} | |
} | |
return ringIndex; | |
}, | |
update: function(reset) { | |
var me = this; | |
var chart = me.chart; | |
var chartArea = chart.chartArea; | |
var opts = chart.options; | |
var arcOpts = opts.elements.arc; | |
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; | |
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; | |
var minSize = Math.min(availableWidth, availableHeight); | |
var offset = {x: 0, y: 0}; | |
var meta = me.getMeta(); | |
var cutoutPercentage = opts.cutoutPercentage; | |
var circumference = opts.circumference; | |
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc | |
if (circumference < Math.PI * 2.0) { | |
var startAngle = opts.rotation % (Math.PI * 2.0); | |
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); | |
var endAngle = startAngle + 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 && endAngle >= 0) || (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 = 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}; | |
} | |
chart.borderWidth = me.getMaxBorderWidth(meta.data); | |
chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); | |
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); | |
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); | |
chart.offsetX = offset.x * chart.outerRadius; | |
chart.offsetY = offset.y * chart.outerRadius; | |
meta.total = me.calculateTotal(); | |
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); | |
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); | |
helpers.each(meta.data, function(arc, index) { | |
me.updateElement(arc, index, reset); | |
}); | |
}, | |
updateElement: function(arc, index, reset) { | |
var me = this; | |
var chart = me.chart; | |
var chartArea = chart.chartArea; | |
var opts = chart.options; | |
var animationOpts = opts.animation; | |
var centerX = (chartArea.left + chartArea.right) / 2; | |
var centerY = (chartArea.top + chartArea.bottom) / 2; | |
var startAngle = opts.rotation; // non reset case handled later | |
var endAngle = opts.rotation; // non reset case handled later | |
var dataset = me.getDataset(); | |
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); | |
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; | |
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; | |
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; | |
helpers.extend(arc, { | |
// Utility | |
_datasetIndex: me.index, | |
_index: index, | |
// Desired view properties | |
_model: { | |
x: centerX + chart.offsetX, | |
y: centerY + chart.offsetY, | |
startAngle: startAngle, | |
endAngle: endAngle, | |
circumference: circumference, | |
outerRadius: outerRadius, | |
innerRadius: innerRadius, | |
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) | |
} | |
}); | |
var model = arc._model; | |
// Resets the visual styles | |
this.removeHoverStyle(arc); | |
// Set correct angles if not resetting | |
if (!reset || !animationOpts.animateRotate) { | |
if (index === 0) { | |
model.startAngle = opts.rotation; | |
} else { | |
model.startAngle = me.getMeta().data[index - 1]._model.endAngle; | |
} | |
model.endAngle = model.startAngle + model.circumference; | |
} | |
arc.pivot(); | |
}, | |
removeHoverStyle: function(arc) { | |
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); | |
}, | |
calculateTotal: function() { | |
var dataset = this.getDataset(); | |
var meta = this.getMeta(); | |
var total = 0; | |
var value; | |
helpers.each(meta.data, function(element, index) { | |
value = dataset.data[index]; | |
if (!isNaN(value) && !element.hidden) { | |
total += Math.abs(value); | |
} | |
}); | |
/* if (total === 0) { | |
total = NaN; | |
}*/ | |
return total; | |
}, | |
calculateCircumference: function(value) { | |
var total = this.getMeta().total; | |
if (total > 0 && !isNaN(value)) { | |
return (Math.PI * 2.0) * (value / total); | |
} | |
return 0; | |
}, | |
// gets the max border or hover width to properly scale pie charts | |
getMaxBorderWidth: function(arcs) { | |
var max = 0; | |
var index = this.index; | |
var length = arcs.length; | |
var borderWidth; | |
var hoverWidth; | |
for (var i = 0; i < length; i++) { | |
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; | |
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; | |
max = borderWidth > max ? borderWidth : max; | |
max = hoverWidth > max ? hoverWidth : max; | |
} | |
return max; | |
} | |
}); | |
}; | |
},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var elements = require(40); | |
var helpers = require(45); | |
defaults._set('line', { | |
showLines: true, | |
spanGaps: false, | |
hover: { | |
mode: 'label' | |
}, | |
scales: { | |
xAxes: [{ | |
type: 'category', | |
id: 'x-axis-0' | |
}], | |
yAxes: [{ | |
type: 'linear', | |
id: 'y-axis-0' | |
}] | |
} | |
}); | |
module.exports = function(Chart) { | |
function lineEnabled(dataset, options) { | |
return helpers.valueOrDefault(dataset.showLine, options.showLines); | |
} | |
Chart.controllers.line = Chart.DatasetController.extend({ | |
datasetElementType: elements.Line, | |
dataElementType: elements.Point, | |
update: function(reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var line = meta.dataset; | |
var points = meta.data || []; | |
var options = me.chart.options; | |
var lineElementOptions = options.elements.line; | |
var scale = me.getScaleForId(meta.yAxisID); | |
var i, ilen, custom; | |
var dataset = me.getDataset(); | |
var showLine = lineEnabled(dataset, options); | |
// Update Line | |
if (showLine) { | |
custom = line.custom || {}; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { | |
dataset.lineTension = dataset.tension; | |
} | |
// Utility | |
line._scale = scale; | |
line._datasetIndex = me.index; | |
// Data | |
line._children = points; | |
// Model | |
line._model = { | |
// Appearance | |
// The default behavior of lines is to break at null values, according | |
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 | |
// This option gives lines the ability to span gaps | |
spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, | |
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), | |
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), | |
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), | |
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), | |
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), | |
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), | |
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), | |
steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), | |
cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), | |
}; | |
line.pivot(); | |
} | |
// Update Points | |
for (i = 0, ilen = points.length; i < ilen; ++i) { | |
me.updateElement(points[i], i, reset); | |
} | |
if (showLine && line._model.tension !== 0) { | |
me.updateBezierControlPoints(); | |
} | |
// Now pivot the point for animation | |
for (i = 0, ilen = points.length; i < ilen; ++i) { | |
points[i].pivot(); | |
} | |
}, | |
getPointBackgroundColor: function(point, index) { | |
var backgroundColor = this.chart.options.elements.point.backgroundColor; | |
var dataset = this.getDataset(); | |
var custom = point.custom || {}; | |
if (custom.backgroundColor) { | |
backgroundColor = custom.backgroundColor; | |
} else if (dataset.pointBackgroundColor) { | |
backgroundColor = helpers.valueAtIndexOrDefault(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(); | |
var custom = point.custom || {}; | |
if (custom.borderColor) { | |
borderColor = custom.borderColor; | |
} else if (dataset.pointBorderColor) { | |
borderColor = helpers.valueAtIndexOrDefault(dataset.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(); | |
var custom = point.custom || {}; | |
if (!isNaN(custom.borderWidth)) { | |
borderWidth = custom.borderWidth; | |
} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { | |
borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); | |
} else if (!isNaN(dataset.borderWidth)) { | |
borderWidth = dataset.borderWidth; | |
} | |
return borderWidth; | |
}, | |
updateElement: function(point, index, reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var custom = point.custom || {}; | |
var dataset = me.getDataset(); | |
var datasetIndex = me.index; | |
var value = dataset.data[index]; | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var xScale = me.getScaleForId(meta.xAxisID); | |
var pointOptions = me.chart.options.elements.point; | |
var x, y; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { | |
dataset.pointRadius = dataset.radius; | |
} | |
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { | |
dataset.pointHitRadius = dataset.hitRadius; | |
} | |
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); | |
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); | |
// Utility | |
point._xScale = xScale; | |
point._yScale = yScale; | |
point._datasetIndex = datasetIndex; | |
point._index = index; | |
// Desired view properties | |
point._model = { | |
x: x, | |
y: y, | |
skip: custom.skip || isNaN(x) || isNaN(y), | |
// Appearance | |
radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), | |
pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), | |
backgroundColor: me.getPointBackgroundColor(point, index), | |
borderColor: me.getPointBorderColor(point, index), | |
borderWidth: me.getPointBorderWidth(point, index), | |
tension: meta.dataset._model ? meta.dataset._model.tension : 0, | |
steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, | |
// Tooltip | |
hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) | |
}; | |
}, | |
calculatePointY: function(value, index, datasetIndex) { | |
var me = this; | |
var chart = me.chart; | |
var meta = me.getMeta(); | |
var yScale = me.getScaleForId(meta.yAxisID); | |
var sumPos = 0; | |
var sumNeg = 0; | |
var i, ds, dsMeta; | |
if (yScale.options.stacked) { | |
for (i = 0; i < datasetIndex; i++) { | |
ds = chart.data.datasets[i]; | |
dsMeta = chart.getDatasetMeta(i); | |
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { | |
var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); | |
if (stackedRightValue < 0) { | |
sumNeg += stackedRightValue || 0; | |
} else { | |
sumPos += stackedRightValue || 0; | |
} | |
} | |
} | |
var rightValue = Number(yScale.getRightValue(value)); | |
if (rightValue < 0) { | |
return yScale.getPixelForValue(sumNeg + rightValue); | |
} | |
return yScale.getPixelForValue(sumPos + rightValue); | |
} | |
return yScale.getPixelForValue(value); | |
}, | |
updateBezierControlPoints: function() { | |
var me = this; | |
var meta = me.getMeta(); | |
var area = me.chart.chartArea; | |
var points = (meta.data || []); | |
var i, ilen, point, model, controlPoints; | |
// Only consider points that are drawn in case the spanGaps option is used | |
if (meta.dataset._model.spanGaps) { | |
points = points.filter(function(pt) { | |
return !pt._model.skip; | |
}); | |
} | |
function capControlPoint(pt, min, max) { | |
return Math.max(Math.min(pt, max), min); | |
} | |
if (meta.dataset._model.cubicInterpolationMode === 'monotone') { | |
helpers.splineCurveMonotone(points); | |
} else { | |
for (i = 0, ilen = points.length; i < ilen; ++i) { | |
point = points[i]; | |
model = point._model; | |
controlPoints = helpers.splineCurve( | |
helpers.previousItem(points, i)._model, | |
model, | |
helpers.nextItem(points, i)._model, | |
meta.dataset._model.tension | |
); | |
model.controlPointPreviousX = controlPoints.previous.x; | |
model.controlPointPreviousY = controlPoints.previous.y; | |
model.controlPointNextX = controlPoints.next.x; | |
model.controlPointNextY = controlPoints.next.y; | |
} | |
} | |
if (me.chart.options.elements.line.capBezierPoints) { | |
for (i = 0, ilen = points.length; i < ilen; ++i) { | |
model = points[i]._model; | |
model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); | |
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); | |
model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); | |
model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); | |
} | |
} | |
}, | |
draw: function() { | |
var me = this; | |
var chart = me.chart; | |
var meta = me.getMeta(); | |
var points = meta.data || []; | |
var area = chart.chartArea; | |
var ilen = points.length; | |
var i = 0; | |
helpers.canvas.clipArea(chart.ctx, area); | |
if (lineEnabled(me.getDataset(), chart.options)) { | |
meta.dataset.draw(); | |
} | |
helpers.canvas.unclipArea(chart.ctx); | |
// Draw the points | |
for (; i < ilen; ++i) { | |
points[i].draw(area); | |
} | |
}, | |
setHoverStyle: function(point) { | |
// Point | |
var dataset = this.chart.data.datasets[point._datasetIndex]; | |
var index = point._index; | |
var custom = point.custom || {}; | |
var model = point._model; | |
model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); | |
model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); | |
}, | |
removeHoverStyle: function(point) { | |
var me = this; | |
var dataset = me.chart.data.datasets[point._datasetIndex]; | |
var index = point._index; | |
var custom = point.custom || {}; | |
var model = point._model; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { | |
dataset.pointRadius = dataset.radius; | |
} | |
model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); | |
model.backgroundColor = me.getPointBackgroundColor(point, index); | |
model.borderColor = me.getPointBorderColor(point, index); | |
model.borderWidth = me.getPointBorderWidth(point, index); | |
} | |
}); | |
}; | |
},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var elements = require(40); | |
var helpers = require(45); | |
defaults._set('polarArea', { | |
scale: { | |
type: 'radialLinear', | |
angleLines: { | |
display: false | |
}, | |
gridLines: { | |
circular: true | |
}, | |
pointLabels: { | |
display: false | |
}, | |
ticks: { | |
beginAtZero: true | |
} | |
}, | |
// Boolean - Whether to animate the rotation of the chart | |
animation: { | |
animateRotate: true, | |
animateScale: true | |
}, | |
startAngle: -0.5 * Math.PI, | |
legendCallback: function(chart) { | |
var text = []; | |
text.push('<ul class="' + chart.id + '-legend">'); | |
var data = chart.data; | |
var datasets = data.datasets; | |
var labels = data.labels; | |
if (datasets.length) { | |
for (var i = 0; i < datasets[0].data.length; ++i) { | |
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); | |
if (labels[i]) { | |
text.push(labels[i]); | |
} | |
text.push('</li>'); | |
} | |
} | |
text.push('</ul>'); | |
return text.join(''); | |
}, | |
legend: { | |
labels: { | |
generateLabels: function(chart) { | |
var data = chart.data; | |
if (data.labels.length && data.datasets.length) { | |
return data.labels.map(function(label, i) { | |
var meta = chart.getDatasetMeta(0); | |
var ds = data.datasets[0]; | |
var arc = meta.data[i]; | |
var custom = arc.custom || {}; | |
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; | |
var arcOpts = chart.options.elements.arc; | |
var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); | |
var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); | |
var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); | |
return { | |
text: label, | |
fillStyle: fill, | |
strokeStyle: stroke, | |
lineWidth: bw, | |
hidden: isNaN(ds.data[i]) || meta.data[i].hidden, | |
// Extra data used for toggling the correct item | |
index: i | |
}; | |
}); | |
} | |
return []; | |
} | |
}, | |
onClick: function(e, legendItem) { | |
var index = legendItem.index; | |
var chart = this.chart; | |
var i, ilen, meta; | |
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { | |
meta = chart.getDatasetMeta(i); | |
meta.data[index].hidden = !meta.data[index].hidden; | |
} | |
chart.update(); | |
} | |
}, | |
// Need to override these to give a nice default | |
tooltips: { | |
callbacks: { | |
title: function() { | |
return ''; | |
}, | |
label: function(item, data) { | |
return data.labels[item.index] + ': ' + item.yLabel; | |
} | |
} | |
} | |
}); | |
module.exports = function(Chart) { | |
Chart.controllers.polarArea = Chart.DatasetController.extend({ | |
dataElementType: elements.Arc, | |
linkScales: helpers.noop, | |
update: function(reset) { | |
var me = this; | |
var chart = me.chart; | |
var chartArea = chart.chartArea; | |
var meta = me.getMeta(); | |
var opts = chart.options; | |
var arcOpts = opts.elements.arc; | |
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); | |
chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); | |
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); | |
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); | |
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); | |
me.innerRadius = me.outerRadius - chart.radiusLength; | |
meta.count = me.countVisibleElements(); | |
helpers.each(meta.data, function(arc, index) { | |
me.updateElement(arc, index, reset); | |
}); | |
}, | |
updateElement: function(arc, index, reset) { | |
var me = this; | |
var chart = me.chart; | |
var dataset = me.getDataset(); | |
var opts = chart.options; | |
var animationOpts = opts.animation; | |
var scale = chart.scale; | |
var labels = chart.data.labels; | |
var circumference = me.calculateCircumference(dataset.data[index]); | |
var centerX = scale.xCenter; | |
var centerY = scale.yCenter; | |
// 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 visibleCount = 0; | |
var meta = me.getMeta(); | |
for (var i = 0; i < index; ++i) { | |
if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { | |
++visibleCount; | |
} | |
} | |
// var negHalfPI = -0.5 * Math.PI; | |
var datasetStartAngle = opts.startAngle; | |
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); | |
var startAngle = datasetStartAngle + (circumference * visibleCount); | |
var endAngle = startAngle + (arc.hidden ? 0 : circumference); | |
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); | |
helpers.extend(arc, { | |
// Utility | |
_datasetIndex: me.index, | |
_index: index, | |
_scale: scale, | |
// Desired view properties | |
_model: { | |
x: centerX, | |
y: centerY, | |
innerRadius: 0, | |
outerRadius: reset ? resetRadius : distance, | |
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, | |
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, | |
label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) | |
} | |
}); | |
// Apply border and fill style | |
me.removeHoverStyle(arc); | |
arc.pivot(); | |
}, | |
removeHoverStyle: function(arc) { | |
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); | |
}, | |
countVisibleElements: function() { | |
var dataset = this.getDataset(); | |
var meta = this.getMeta(); | |
var count = 0; | |
helpers.each(meta.data, function(element, index) { | |
if (!isNaN(dataset.data[index]) && !element.hidden) { | |
count++; | |
} | |
}); | |
return count; | |
}, | |
calculateCircumference: function(value) { | |
var count = this.getMeta().count; | |
if (count > 0 && !isNaN(value)) { | |
return (2 * Math.PI) / count; | |
} | |
return 0; | |
} | |
}); | |
}; | |
},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var elements = require(40); | |
var helpers = require(45); | |
defaults._set('radar', { | |
scale: { | |
type: 'radialLinear' | |
}, | |
elements: { | |
line: { | |
tension: 0 // no bezier in radar | |
} | |
} | |
}); | |
module.exports = function(Chart) { | |
Chart.controllers.radar = Chart.DatasetController.extend({ | |
datasetElementType: elements.Line, | |
dataElementType: elements.Point, | |
linkScales: helpers.noop, | |
update: function(reset) { | |
var me = this; | |
var meta = me.getMeta(); | |
var line = meta.dataset; | |
var points = meta.data; | |
var custom = line.custom || {}; | |
var dataset = me.getDataset(); | |
var lineElementOptions = me.chart.options.elements.line; | |
var scale = me.chart.scale; | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { | |
dataset.lineTension = dataset.tension; | |
} | |
helpers.extend(meta.dataset, { | |
// Utility | |
_datasetIndex: me.index, | |
_scale: scale, | |
// Data | |
_children: points, | |
_loop: true, | |
// Model | |
_model: { | |
// Appearance | |
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), | |
borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), | |
fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), | |
borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), | |
borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), | |
borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), | |
borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), | |
} | |
}); | |
meta.dataset.pivot(); | |
// Update Points | |
helpers.each(points, function(point, index) { | |
me.updateElement(point, index, reset); | |
}, me); | |
// Update bezier control points | |
me.updateBezierControlPoints(); | |
}, | |
updateElement: function(point, index, reset) { | |
var me = this; | |
var custom = point.custom || {}; | |
var dataset = me.getDataset(); | |
var scale = me.chart.scale; | |
var pointElementOptions = me.chart.options.elements.point; | |
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); | |
// Compatibility: If the properties are defined with only the old name, use those values | |
if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { | |
dataset.pointRadius = dataset.radius; | |
} | |
if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { | |
dataset.pointHitRadius = dataset.hitRadius; | |
} | |
helpers.extend(point, { | |
// Utility | |
_datasetIndex: me.index, | |
_index: index, | |
_scale: scale, | |
// Desired view properties | |
_model: { | |
x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales | |
y: reset ? scale.yCenter : pointPosition.y, | |
// Appearance | |
tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), | |
radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), | |
backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), | |
borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), | |
borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), | |
pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), | |
// Tooltip | |
hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) | |
} | |
}); | |
point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); | |
}, | |
updateBezierControlPoints: function() { | |
var chartArea = this.chart.chartArea; | |
var meta = this.getMeta(); | |
helpers.each(meta.data, function(point, index) { | |
var model = point._model; | |
var controlPoints = helpers.splineCurve( | |
helpers.previousItem(meta.data, index, true)._model, | |
model, | |
helpers.nextItem(meta.data, index, true)._model, | |
model.tension | |
); | |
// Prevent the bezier going outside of the bounds of the graph | |
model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); | |
model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); | |
model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); | |
model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); | |
// Now pivot the point for animation | |
point.pivot(); | |
}); | |
}, | |
setHoverStyle: function(point) { | |
// Point | |
var dataset = this.chart.data.datasets[point._datasetIndex]; | |
var custom = point.custom || {}; | |
var index = point._index; | |
var model = point._model; | |
model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); | |
model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); | |
model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); | |
model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); | |
}, | |
removeHoverStyle: function(point) { | |
var dataset = this.chart.data.datasets[point._datasetIndex]; | |
var custom = point.custom || {}; | |
var index = point._index; | |
var model = point._model; | |
var pointElementOptions = this.chart.options.elements.point; | |
model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); | |
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); | |
model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); | |
model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); | |
} | |
}); | |
}; | |
},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
defaults._set('scatter', { | |
hover: { | |
mode: 'single' | |
}, | |
scales: { | |
xAxes: [{ | |
id: 'x-axis-1', // need an ID so datasets can reference the scale | |
type: 'linear', // scatter should not use a category axis | |
position: 'bottom' | |
}], | |
yAxes: [{ | |
id: 'y-axis-1', | |
type: 'linear', | |
position: 'left' | |
}] | |
}, | |
showLines: false, | |
tooltips: { | |
callbacks: { | |
title: function() { | |
return ''; // doesn't make sense for scatter since data are formatted as a point | |
}, | |
label: function(item) { | |
return '(' + item.xLabel + ', ' + item.yLabel + ')'; | |
} | |
} | |
} | |
}); | |
module.exports = function(Chart) { | |
// Scatter charts use line controllers | |
Chart.controllers.scatter = Chart.controllers.line; | |
}; | |
},{"25":25}],22:[function(require,module,exports){ | |
/* global window: false */ | |
'use strict'; | |
var defaults = require(25); | |
var Element = require(26); | |
var helpers = require(45); | |
defaults._set('global', { | |
animation: { | |
duration: 1000, | |
easing: 'easeOutQuart', | |
onProgress: helpers.noop, | |
onComplete: helpers.noop | |
} | |
}); | |
module.exports = function(Chart) { | |
Chart.Animation = Element.extend({ | |
chart: null, // the animation associated chart instance | |
currentStep: 0, // 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, | |
/** | |
* @param {Chart} chart - The chart to animate. | |
* @param {Chart.Animation} animation - The animation that we will animate. | |
* @param {Number} duration - The animation duration in ms. | |
* @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions | |
*/ | |
addAnimation: function(chart, animation, duration, lazy) { | |
var animations = this.animations; | |
var i, ilen; | |
animation.chart = chart; | |
if (!lazy) { | |
chart.animating = true; | |
} | |
for (i = 0, ilen = animations.length; i < ilen; ++i) { | |
if (animations[i].chart === chart) { | |
animations[i] = animation; | |
return; | |
} | |
} | |
animations.push(animation); | |
// If there are no animations queued, manually kickstart a digest, for lack of a better word | |
if (animations.length === 1) { | |
this.requestAnimationFrame(); | |
} | |
}, | |
cancelAnimation: function(chart) { | |
var index = helpers.findIndex(this.animations, function(animation) { | |
return animation.chart === chart; | |
}); | |
if (index !== -1) { | |
this.animations.splice(index, 1); | |
chart.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(); | |
}); | |
} | |
}, | |
/** | |
* @private | |
*/ | |
startDigest: function() { | |
var me = this; | |
var startTime = Date.now(); | |
var framesToDrop = 0; | |
if (me.dropFrames > 1) { | |
framesToDrop = Math.floor(me.dropFrames); | |
me.dropFrames = me.dropFrames % 1; | |
} | |
me.advance(1 + framesToDrop); | |
var endTime = Date.now(); | |
me.dropFrames += (endTime - startTime) / me.frameDuration; | |
// Do we have more stuff to animate? | |
if (me.animations.length > 0) { | |
me.requestAnimationFrame(); | |
} | |
}, | |
/** | |
* @private | |
*/ | |
advance: function(count) { | |
var animations = this.animations; | |
var animation, chart; | |
var i = 0; | |
while (i < animations.length) { | |
animation = animations[i]; | |
chart = animation.chart; | |
animation.currentStep = (animation.currentStep || 0) + count; | |
animation.currentStep = Math.min(animation.currentStep, animation.numSteps); | |
helpers.callback(animation.render, [chart, animation], chart); | |
helpers.callback(animation.onAnimationProgress, [animation], chart); | |
if (animation.currentStep >= animation.numSteps) { | |
helpers.callback(animation.onAnimationComplete, [animation], chart); | |
chart.animating = false; | |
animations.splice(i, 1); | |
} else { | |
++i; | |
} | |
} | |
} | |
}; | |
/** | |
* Provided for backward compatibility, use Chart.Animation instead | |
* @prop Chart.Animation#animationObject | |
* @deprecated since version 2.6.0 | |
* @todo remove at version 3 | |
*/ | |
Object.defineProperty(Chart.Animation.prototype, 'animationObject', { | |
get: function() { | |
return this; | |
} | |
}); | |
/** | |
* Provided for backward compatibility, use Chart.Animation#chart instead | |
* @prop Chart.Animation#chartInstance | |
* @deprecated since version 2.6.0 | |
* @todo remove at version 3 | |
*/ | |
Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { | |
get: function() { | |
return this.chart; | |
}, | |
set: function(value) { | |
this.chart = value; | |
} | |
}); | |
}; | |
},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ | |
'use strict'; | |
var defaults = require(25); | |
var helpers = require(45); | |
var Interaction = require(28); | |
var platform = require(48); | |
module.exports = function(Chart) { | |
var plugins = Chart.plugins; | |
// 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 = {}; | |
/** | |
* Initializes the given config with global and chart default values. | |
*/ | |
function initConfig(config) { | |
config = config || {}; | |
// Do NOT use configMerge() for the data object because this method merges arrays | |
// and so would change references to labels and datasets, preventing data updates. | |
var data = config.data = config.data || {}; | |
data.datasets = data.datasets || []; | |
data.labels = data.labels || []; | |
config.options = helpers.configMerge( | |
defaults.global, | |
defaults[config.type], | |
config.options || {}); | |
return config; | |
} | |
/** | |
* Updates the config of the chart | |
* @param chart {Chart} chart to update the options for | |
*/ | |
function updateConfig(chart) { | |
var newOptions = chart.options; | |
// Update Scale(s) with options | |
if (newOptions.scale) { | |
chart.scale.options = newOptions.scale; | |
} else if (newOptions.scales) { | |
newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { | |
chart.scales[scaleOptions.id].options = scaleOptions; | |
}); | |
} | |
// Tooltip | |
chart.tooltip._options = newOptions.tooltips; | |
} | |
function positionIsHorizontal(position) { | |
return position === 'top' || position === 'bottom'; | |
} | |
helpers.extend(Chart.prototype, /** @lends Chart */ { | |
/** | |
* @private | |
*/ | |
construct: function(item, config) { | |
var me = this; | |
config = initConfig(config); | |
var context = platform.acquireContext(item, config); | |
var canvas = context && context.canvas; | |
var height = canvas && canvas.height; | |
var width = canvas && canvas.width; | |
me.id = helpers.uid(); | |
me.ctx = context; | |
me.canvas = canvas; | |
me.config = config; | |
me.width = width; | |
me.height = height; | |
me.aspectRatio = height ? width / height : null; | |
me.options = config.options; | |
me._bufferedRender = false; | |
/** | |
* Provided for backward compatibility, Chart and Chart.Controller have been merged, | |
* the "instance" still need to be defined since it might be called from plugins. | |
* @prop Chart#chart | |
* @deprecated since version 2.6.0 | |
* @todo remove at version 3 | |
* @private | |
*/ | |
me.chart = me; | |
me.controller = me; // chart.chart.controller #inception | |
// Add the chart instance to the global namespace | |
Chart.instances[me.id] = me; | |
// Define alias to the config data: `chart.data === chart.config.data` | |
Object.defineProperty(me, 'data', { | |
get: function() { | |
return me.config.data; | |
}, | |
set: function(value) { | |
me.config.data = value; | |
} | |
}); | |
if (!context || !canvas) { | |
// The given item is not a compatible context2d element, let's return before finalizing | |
// the chart initialization but after setting basic chart / controller properties that | |
// can help to figure out that the chart is not valid (e.g chart.canvas !== null); | |
// https://github.com/chartjs/Chart.js/issues/2807 | |
console.error("Failed to create chart: can't acquire context from the given item"); | |
return; | |
} | |
me.initialize(); | |
me.update(); | |
}, | |
/** | |
* @private | |
*/ | |
initialize: function() { | |
var me = this; | |
// Before init plugin notification | |
plugins.notify(me, 'beforeInit'); | |
helpers.retinaScale(me, me.options.devicePixelRatio); | |
me.bindEvents(); | |
if (me.options.responsive) { | |
// Initial resize before chart draws (must be silent to preserve initial animations). | |
me.resize(true); | |
} | |
// Make sure scales have IDs and are built before we build any controllers. | |
me.ensureScalesHaveIDs(); | |
me.buildScales(); | |
me.initToolTip(); | |
// After init plugin notification | |
plugins.notify(me, 'afterInit'); | |
return me; | |
}, | |
clear: function() { | |
helpers.canvas.clear(this); | |
return this; | |
}, | |
stop: function() { | |
// Stops any current animation loop occurring | |
Chart.animationService.cancelAnimation(this); | |
return this; | |
}, | |
resize: function(silent) { | |
var me = this; | |
var options = me.options; | |
var canvas = me.canvas; | |
var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; | |
// the canvas render width and height will be casted to integers so make sure that | |
// the canvas display style uses the same integer values to avoid blurring effect. | |
// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased | |
var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); | |
var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); | |
if (me.width === newWidth && me.height === newHeight) { | |
return; | |
} | |
canvas.width = me.width = newWidth; | |
canvas.height = me.height = newHeight; | |
canvas.style.width = newWidth + 'px'; | |
canvas.style.height = newHeight + 'px'; | |
helpers.retinaScale(me, options.devicePixelRatio); | |
if (!silent) { | |
// Notify any plugins about the resize | |
var newSize = {width: newWidth, height: newHeight}; | |
plugins.notify(me, 'resize', [newSize]); | |
// Notify of resize | |
if (me.options.onResize) { | |
me.options.onResize(me, newSize); | |
} | |
me.stop(); | |
me.update(me.options.responsiveAnimationDuration); | |
} | |
}, | |
ensureScalesHaveIDs: function() { | |
var options = this.options; | |
var scalesOptions = options.scales || {}; | |
var scaleOptions = options.scale; | |
helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { | |
xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); | |
}); | |
helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { | |
yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); | |
}); | |
if (scaleOptions) { | |
scaleOptions.id = scaleOptions.id || 'scale'; | |
} | |
}, | |
/** | |
* Builds a map of scale ID to scale object for future lookup. | |
*/ | |
buildScales: function() { | |
var me = this; | |
var options = me.options; | |
var scales = me.scales = {}; | |
var items = []; | |
if (options.scales) { | |
items = items.concat( | |
(options.scales.xAxes || []).map(function(xAxisOptions) { | |
return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; | |
}), | |
(options.scales.yAxes || []).map(function(yAxisOptions) { | |
return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; | |
}) | |
); | |
} | |
if (options.scale) { | |
items.push({ | |
options: options.scale, | |
dtype: 'radialLinear', | |
isDefault: true, | |
dposition: 'chartArea' | |
}); | |
} | |
helpers.each(items, function(item) { | |
var scaleOptions = item.options; | |
var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); | |
var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); | |
if (!scaleClass) { | |
return; | |
} | |
if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { | |
scaleOptions.position = item.dposition; | |
} | |
var scale = new scaleClass({ | |
id: scaleOptions.id, | |
options: scaleOptions, | |
ctx: me.ctx, | |
chart: me | |
}); | |
scales[scale.id] = scale; | |
scale.mergeTicksOptions(); | |
// TODO(SB): I think we should be able to remove this custom case (options.scale) | |
// and consider it as a regular scale part of the "scales"" map only! This would | |
// make the logic easier and remove some useless? custom code. | |
if (item.isDefault) { | |
me.scale = scale; | |
} | |
}); | |
Chart.scaleService.addScalesToLayout(this); | |
}, | |
buildOrUpdateControllers: function() { | |
var me = this; | |
var types = []; | |
var newControllers = []; | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
var meta = me.getDatasetMeta(datasetIndex); | |
var type = dataset.type || me.config.type; | |
if (meta.type && meta.type !== type) { | |
me.destroyDatasetMeta(datasetIndex); | |
meta = me.getDatasetMeta(datasetIndex); | |
} | |
meta.type = type; | |
types.push(meta.type); | |
if (meta.controller) { | |
meta.controller.updateIndex(datasetIndex); | |
} else { | |
var ControllerClass = Chart.controllers[meta.type]; | |
if (ControllerClass === undefined) { | |
throw new Error('"' + meta.type + '" is not a chart type.'); | |
} | |
meta.controller = new ControllerClass(me, datasetIndex); | |
newControllers.push(meta.controller); | |
} | |
}, me); | |
return newControllers; | |
}, | |
/** | |
* Reset the elements of all datasets | |
* @private | |
*/ | |
resetElements: function() { | |
var me = this; | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
me.getDatasetMeta(datasetIndex).controller.reset(); | |
}, me); | |
}, | |
/** | |
* Resets the chart back to it's state before the initial animation | |
*/ | |
reset: function() { | |
this.resetElements(); | |
this.tooltip.initialize(); | |
}, | |
update: function(config) { | |
var me = this; | |
if (!config || typeof config !== 'object') { | |
// backwards compatibility | |
config = { | |
duration: config, | |
lazy: arguments[1] | |
}; | |
} | |
updateConfig(me); | |
if (plugins.notify(me, 'beforeUpdate') === false) { | |
return; | |
} | |
// In case the entire data object changed | |
me.tooltip._data = me.data; | |
// Make sure dataset controllers are updated and new controllers are reset | |
var newControllers = me.buildOrUpdateControllers(); | |
// Make sure all dataset controllers have correct meta data counts | |
helpers.each(me.data.datasets, function(dataset, datasetIndex) { | |
me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); | |
}, me); | |
me.updateLayout(); | |
// Can only reset the new controllers after the scales have been updated | |
helpers.each(newControllers, function(controller) { | |
controller.reset(); | |
}); | |
me.updateDatasets(); | |
// Do this before render so that any plugins that need final scale updates can use it | |
plugins.notify(me, 'afterUpdate'); | |
if (me._bufferedRender) { | |
me._bufferedRequest = { | |
duration: config.duration, | |
easing: config.easing, | |
lazy: config.lazy | |
}; | |
} else { | |
me.render(config); | |
} | |
}, | |
/** | |
* Updates the chart layout unless a plugin returns `false` to the `beforeLayout` | |
* hook, in which case, plugins will not be called on `afterLayout`. | |
* @private | |
*/ | |
updateLayout: function() { | |
var me = this; | |
if (plugins.notify(me, 'beforeLayout') === false) { | |
return; | |
} | |
Chart.layoutService.update(this, this.width, this.height); | |
/** | |
* Provided for backward compatibility, use `afterLayout` instead. | |
* @method IPlugin#afterScaleUpdate | |
* @deprecated since version 2.5.0 | |
* @todo remove at version 3 | |
* @private | |
*/ | |
plugins.notify(me, 'afterScaleUpdate'); | |
plugins.notify(me, 'afterLayout'); | |
}, | |
/** | |
* Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` | |
* hook, in which case, plugins will not be called on `afterDatasetsUpdate`. | |
* @private | |
*/ | |
updateDatasets: function() { | |
var me = this; | |
if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { | |
return; | |
} | |
for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { | |
me.updateDataset(i); | |
} | |
plugins.notify(me, 'afterDatasetsUpdate'); | |
}, | |
/** | |
* Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` | |
* hook, in which case, plugins will not be called on `afterDatasetUpdate`. | |
* @private | |
*/ | |
updateDataset: function(index) { | |
var me = this; | |
var meta = me.getDatasetMeta(index); | |
var args = { | |
meta: meta, | |
index: index | |
}; | |
if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { | |
return; | |
} | |
meta.controller.update(); | |
plugins.notify(me, 'afterDatasetUpdate', [args]); | |
}, | |
render: function(config) { | |
var me = this; | |
if (!config || typeof config !== 'object') { | |
// backwards compatibility | |
config = { | |
duration: config, | |
lazy: arguments[1] | |
}; | |
} | |
var duration = config.duration; | |
var lazy = config.lazy; | |
if (plugins.notify(me, 'beforeRender') === false) { | |
return; | |
} | |
var animationOptions = me.options.animation; | |
var onComplete = function(animation) { | |
plugins.notify(me, 'afterRender'); | |
helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); | |
}; | |
if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { | |
var animation = new Chart.Animation({ | |
numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps | |
easing: config.easing || animationOptions.easing, | |
render: function(chart, animationObject) { | |
var easingFunction = helpers.easing.effects[animationObject.easing]; | |
var currentStep = animationObject.currentStep; | |
var stepDecimal = currentStep / animationObject.numSteps; | |
chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); | |
}, | |
onAnimationProgress: animationOptions.onProgress, | |
onAnimationComplete: onComplete | |
}); | |
Chart.animationService.addAnimation(me, animation, duration, lazy); | |
} else { | |
me.draw(); | |
// See https://github.com/chartjs/Chart.js/issues/3781 | |
onComplete(new Chart.Animation({numSteps: 0, chart: me})); | |
} | |
return me; | |
}, | |
draw: function(easingValue) { | |
var me = this; | |
me.clear(); | |
if (helpers.isNullOrUndef(easingValue)) { | |
easingValue = 1; | |
} | |
me.transition(easingValue); | |
if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { | |
return; | |
} | |
// Draw all the scales | |
helpers.each(me.boxes, function(box) { | |
box.draw(me.chartArea); | |
}, me); | |
if (me.scale) { | |
me.scale.draw(); | |
} | |
me.drawDatasets(easingValue); | |
// Finally draw the tooltip | |
me.tooltip.draw(); | |
plugins.notify(me, 'afterDraw', [easingValue]); | |
}, | |
/** | |
* @private | |
*/ | |
transition: function(easingValue) { | |
var me = this; | |
for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { | |
if (me.isDatasetVisible(i)) { | |
me.getDatasetMeta(i).controller.transition(easingValue); | |
} | |
} | |
me.tooltip.transition(easingValue); | |
}, | |
/** | |
* Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` | |
* hook, in which case, plugins will not be called on `afterDatasetsDraw`. | |
* @private | |
*/ | |
drawDatasets: function(easingValue) { | |
var me = this; | |
if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { | |
return; | |
} | |
// Draw datasets reversed to support proper line stacking | |
for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { | |
if (me.isDatasetVisible(i)) { | |
me.drawDataset(i, easingValue); | |
} | |
} | |
plugins.notify(me, 'afterDatasetsDraw', [easingValue]); | |
}, | |
/** | |
* Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` | |
* hook, in which case, plugins will not be called on `afterDatasetDraw`. | |
* @private | |
*/ | |
drawDataset: function(index, easingValue) { | |
var me = this; | |
var meta = me.getDatasetMeta(index); | |
var args = { | |
meta: meta, | |
index: index, | |
easingValue: easingValue | |
}; | |
if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { | |
return; | |
} | |
meta.controller.draw(easingValue); | |
plugins.notify(me, 'afterDatasetDraw', [args]); | |
}, | |
// 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) { | |
return Interaction.modes.single(this, e); | |
}, | |
getElementsAtEvent: function(e) { | |
return Interaction.modes.label(this, e, {intersect: true}); | |
}, | |
getElementsAtXAxis: function(e) { | |
return Interaction.modes['x-axis'](this, e, {intersect: true}); | |
}, | |
getElementsAtEventForMode: function(e, mode, options) { | |
var method = Interaction.modes[mode]; | |
if (typeof method === 'function') { | |
return method(this, e, options); | |
} | |
return []; | |
}, | |
getDatasetAtEvent: function(e) { | |
return Interaction.modes.dataset(this, e, {intersect: true}); | |
}, | |
getDatasetMeta: function(datasetIndex) { | |
var me = this; | |
var dataset = me.data.datasets[datasetIndex]; | |
if (!dataset._meta) { | |
dataset._meta = {}; | |
} | |
var meta = dataset._meta[me.id]; | |
if (!meta) { | |
meta = dataset._meta[me.id] = { | |
type: null, | |
data: [], | |
dataset: null, | |
controller: null, | |
hidden: null, // See isDatasetVisible() comment | |
xAxisID: null, | |
yAxisID: null | |
}; | |
} | |
return meta; | |
}, | |
getVisibleDatasetCount: function() { | |
var count = 0; | |
for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { | |
if (this.isDatasetVisible(i)) { | |
count++; | |
} | |
} | |
return count; | |
}, | |
isDatasetVisible: function(datasetIndex) { | |
var meta = this.getDatasetMeta(datasetIndex); | |
// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, | |
// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. | |
return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; | |
}, | |
generateLegend: function() { | |
return this.options.legendCallback(this); | |
}, | |
/** | |
* @private | |
*/ | |
destroyDatasetMeta: function(datasetIndex) { | |
var id = this.id; | |
var dataset = this.data.datasets[datasetIndex]; | |
var meta = dataset._meta && dataset._meta[id]; | |
if (meta) { | |
meta.controller.destroy(); | |
delete dataset._meta[id]; | |
} | |
}, | |
destroy: function() { | |
var me = this; | |
var canvas = me.canvas; | |
var i, ilen; | |
me.stop(); | |
// dataset controllers need to cleanup associated data | |
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { | |
me.destroyDatasetMeta(i); | |
} | |
if (canvas) { | |
me.unbindEvents(); | |
helpers.canvas.clear(me); | |
platform.releaseContext(me.ctx); | |
me.canvas = null; | |
me.ctx = null; | |
} | |
plugins.notify(me, 'destroy'); | |
delete Chart.instances[me.id]; | |
}, | |
toBase64Image: function() { | |
return this.canvas.toDataURL.apply(this.canvas, arguments); | |
}, | |
initToolTip: function() { | |
var me = this; | |
me.tooltip = new Chart.Tooltip({ | |
_chart: me, | |
_chartInstance: me, // deprecated, backward compatibility | |
_data: me.data, | |
_options: me.options.tooltips | |
}, me); | |
}, | |
/** | |
* @private | |
*/ | |
bindEvents: function() { | |
var me = this; | |
var listeners = me._listeners = {}; | |
var listener = function() { | |
me.eventHandler.apply(me, arguments); | |
}; | |
helpers.each(me.options.events, function(type) { | |
platform.addEventListener(me, type, listener); | |
listeners[type] = listener; | |
}); | |
// Elements used to detect size change should not be injected for non responsive charts. | |
// See https://github.com/chartjs/Chart.js/issues/2210 | |
if (me.options.responsive) { | |
listener = function() { | |
me.resize(); | |
}; | |
platform.addEventListener(me, 'resize', listener); | |
listeners.resize = listener; | |
} | |
}, | |
/** | |
* @private | |
*/ | |
unbindEvents: function() { | |
var me = this; | |
var listeners = me._listeners; | |
if (!listeners) { | |
return; | |
} | |
delete me._listeners; | |
helpers.each(listeners, function(listener, type) { | |
platform.removeEventListener(me, type, listener); | |
}); | |
}, | |
updateHoverStyle: function(elements, mode, enabled) { | |
var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; | |
var element, i, ilen; | |
for (i = 0, ilen = elements.length; i < ilen; ++i) { | |
element = elements[i]; | |
if (element) { | |
this.getDatasetMeta(element._datasetIndex).controller[method](element); | |
} | |
} | |
}, | |
/** | |
* @private | |
*/ | |
eventHandler: function(e) { | |
var me = this; | |
var tooltip = me.tooltip; | |
if (plugins.notify(me, 'beforeEvent', [e]) === false) { | |
return; | |
} | |
// Buffer any update calls so that renders do not occur | |
me._bufferedRender = true; | |
me._bufferedRequest = null; | |
var changed = me.handleEvent(e); | |
changed |= tooltip && tooltip.handleEvent(e); | |
plugins.notify(me, 'afterEvent', [e]); | |
var bufferedRequest = me._bufferedRequest; | |
if (bufferedRequest) { | |
// If we have an update that was triggered, we need to do a normal render | |
me.render(bufferedRequest); | |
} else if (changed && !me.animating) { | |
// If entering, leaving, or changing elements, animate the change via pivot | |
me.stop(); | |
// We only need to render at this point. Updating will cause scales to be | |
// recomputed generating flicker & using more memory than necessary. | |
me.render(me.options.hover.animationDuration, true); | |
} | |
me._bufferedRender = false; | |
me._bufferedRequest = null; | |
return me; | |
}, | |
/** | |
* Handle an event | |
* @private | |
* @param {IEvent} event the event to handle | |
* @return {Boolean} true if the chart needs to re-render | |
*/ | |
handleEvent: function(e) { | |
var me = this; | |
var options = me.options || {}; | |
var hoverOptions = options.hover; | |
var changed = false; | |
me.lastActive = me.lastActive || []; | |
// Find Active Elements for hover and tooltips | |
if (e.type === 'mouseout') { | |
me.active = []; | |
} else { | |
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); | |
} | |
// Invoke onHover hook | |
// Need to call with native event here to not break backwards compatibility | |
helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); | |
if (e.type === 'mouseup' || e.type === 'click') { | |
if (options.onClick) { | |
// Use e.native here for backwards compatibility | |
options.onClick.call(me, e.native, me.active); | |
} | |
} | |
// Remove styling for last active (even if it may still be active) | |
if (me.lastActive.length) { | |
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); | |
} | |
// Built in hover styling | |
if (me.active.length && hoverOptions.mode) { | |
me.updateHoverStyle(me.active, hoverOptions.mode, true); | |
} | |
changed = !helpers.arrayEquals(me.active, me.lastActive); | |
// Remember Last Actives | |
me.lastActive = me.active; | |
return changed; | |
} | |
}); | |
/** | |
* Provided for backward compatibility, use Chart instead. | |
* @class Chart.Controller | |
* @deprecated since version 2.6.0 | |
* @todo remove at version 3 | |
* @private | |
*/ | |
Chart.Controller = Chart; | |
}; | |
},{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){ | |
'use strict'; | |
var helpers = require(45); | |
module.exports = function(Chart) { | |
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; | |
/** | |
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', | |
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are | |
* called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. | |
*/ | |
function listenArrayEvents(array, listener) { | |
if (array._chartjs) { | |
array._chartjs.listeners.push(listener); | |
return; | |
} | |
Object.defineProperty(array, '_chartjs', { | |
configurable: true, | |
enumerable: false, | |
value: { | |
listeners: [listener] | |
} | |
}); | |
arrayEvents.forEach(function(key) { | |
var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); | |
var base = array[key]; | |
Object.defineProperty(array, key, { | |
configurable: true, | |
enumerable: false, | |
value: function() { | |
var args = Array.prototype.slice.call(arguments); | |
var res = base.apply(this, args); | |
helpers.each(array._chartjs.listeners, function(object) { | |
if (typeof object[method] === 'function') { | |
object[method].apply(object, args); | |
} | |
}); | |
return res; | |
} | |
}); | |
}); | |
} | |
/** | |
* Removes the given array event listener and cleanup extra attached properties (such as | |
* the _chartjs stub and overridden methods) if array doesn't have any more listeners. | |
*/ | |
function unlistenArrayEvents(array, listener) { | |
var stub = array._chartjs; | |
if (!stub) { | |
return; | |
} | |
var listeners = stub.listeners; | |
var index = listeners.indexOf(listener); | |
if (index !== -1) { | |
listeners.splice(index, 1); | |
} | |
if (listeners.length > 0) { | |
return; | |
} | |
arrayEvents.forEach(function(key) { | |
delete array[key]; | |
}); | |
delete array._chartjs; | |
} | |
// Base class for all dataset controllers (line, bar, etc) | |
Chart.DatasetController = function(chart, datasetIndex) { | |
this.initialize(chart, datasetIndex); | |
}; | |
helpers.extend(Chart.DatasetController.prototype, { | |
/** | |
* Element type used to generate a meta dataset (e.g. Chart.element.Line). | |
* @type {Chart.core.element} | |
*/ | |
datasetElementType: null, | |
/** | |
* Element type used to generate a meta data (e.g. Chart.element.Point). | |
* @type {Chart.core.element} | |
*/ | |
dataElementType: null, | |
initialize: function(chart, datasetIndex) { | |
var me = this; | |
me.chart = chart; | |
me.index = datasetIndex; | |
me.linkScales(); | |
me.addElements(); | |
}, | |
updateIndex: function(datasetIndex) { | |
this.index = datasetIndex; | |
}, | |
linkScales: function() { | |
var me = this; | |
var meta = me.getMeta(); | |
var dataset = me.getDataset(); | |
if (meta.xAxisID === null) { | |
meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; | |
} | |
if (meta.yAxisID === null) { | |
meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; | |
} | |
}, | |
getDataset: function() { | |
return this.chart.data.datasets[this.index]; | |
}, | |
getMeta: function() { | |
return this.chart.getDatasetMeta(this.index); | |
}, | |
getScaleForId: function(scaleID) { | |
return this.chart.scales[scaleID]; | |
}, | |
reset: function() { | |
this.update(true); | |
}, | |
/** | |
* @private | |
*/ | |
destroy: function() { | |
if (this._data) { | |
unlistenArrayEvents(this._data, this); | |
} | |
}, | |
createMetaDataset: function() { | |
var me = this; | |
var type = me.datasetElementType; | |
return type && new type({ | |
_chart: me.chart, | |
_datasetIndex: me.index | |
}); | |
}, | |
createMetaData: function(index) { | |
var me = this; | |
var type = me.dataElementType; | |
return type && new type({ | |
_chart: me.chart, | |
_datasetIndex: me.index, | |
_index: index | |
}); | |
}, | |
addElements: function() { | |
var me = this; | |
var meta = me.getMeta(); | |
var data = me.getDataset().data || []; | |
var metaData = meta.data; | |
var i, ilen; | |
for (i = 0, ilen = data.length; i < ilen; ++i) { | |
metaData[i] = metaData[i] || me.createMetaData(i); | |
} | |
meta.dataset = meta.dataset || me.createMetaDataset(); | |
}, | |
addElementAndReset: function(index) { | |
var element = this.createMetaData(index); | |
this.getMeta().data.splice(index, 0, element); | |