diff --git a/lib/chart-js-rails/version.rb b/lib/chart-js-rails/version.rb index 29296e9..68b0c84 100644 --- a/lib/chart-js-rails/version.rb +++ b/lib/chart-js-rails/version.rb @@ -1,5 +1,5 @@ module ChartJs - module Rails - VERSION = "0.1.0" - end + module Rails + VERSION = '0.1.1'.freeze + end end diff --git a/vendor/assets/javascripts/Chart.js b/vendor/assets/javascripts/Chart.js old mode 100755 new mode 100644 index 05bb6d6..c66671c --- a/vendor/assets/javascripts/Chart.js +++ b/vendor/assets/javascripts/Chart.js @@ -1,434 +1,723 @@ /*! * Chart.js * http://chartjs.org/ - * Version: 2.0.2 + * Version: 2.4.0 * * Copyright 2016 Nick Downie * Released under the MIT license - * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md + * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ -(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 lum2) { - return (lum1 + 0.05) / (lum2 + 0.05) - }; - return (lum2 + 0.05) / (lum1 + 0.05); - }, - - level: function(color2) { - var contrastRatio = this.contrast(color2); - return (contrastRatio >= 7.1) ? 'AAA' : (contrastRatio >= 4.5) ? 'AA' : ''; - }, - - dark: function() { - // YIQ equation from http://24ways.org/2010/calculating-color-contrast - var rgb = this.values.rgb, - yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; - return yiq < 128; - }, - - light: function() { - return !this.dark(); - }, - - negate: function() { - var rgb = [] - for (var i = 0; i < 3; i++) { - rgb[i] = 255 - this.values.rgb[i]; - } - this.setValues("rgb", rgb); - return this; - }, - - lighten: function(ratio) { - this.values.hsl[2] += this.values.hsl[2] * ratio; - this.setValues("hsl", this.values.hsl); - return this; - }, - - darken: function(ratio) { - this.values.hsl[2] -= this.values.hsl[2] * ratio; - this.setValues("hsl", this.values.hsl); - return this; - }, - - saturate: function(ratio) { - this.values.hsl[1] += this.values.hsl[1] * ratio; - this.setValues("hsl", this.values.hsl); - return this; - }, - - desaturate: function(ratio) { - this.values.hsl[1] -= this.values.hsl[1] * ratio; - this.setValues("hsl", this.values.hsl); - return this; - }, - - whiten: function(ratio) { - this.values.hwb[1] += this.values.hwb[1] * ratio; - this.setValues("hwb", this.values.hwb); - return this; - }, - - blacken: function(ratio) { - this.values.hwb[2] += this.values.hwb[2] * ratio; - this.setValues("hwb", this.values.hwb); - return this; - }, - - greyscale: function() { - var rgb = this.values.rgb; - // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale - var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; - this.setValues("rgb", [val, val, val]); - return this; - }, - - clearer: function(ratio) { - this.setValues("alpha", this.values.alpha - (this.values.alpha * ratio)); - return this; - }, - - opaquer: function(ratio) { - this.setValues("alpha", this.values.alpha + (this.values.alpha * ratio)); - return this; - }, - - rotate: function(degrees) { - var hue = this.values.hsl[0]; - hue = (hue + degrees) % 360; - hue = hue < 0 ? 360 + hue : hue; - this.values.hsl[0] = hue; - this.setValues("hsl", this.values.hsl); - return this; - }, - - mix: function(color2, weight) { - weight = 1 - (weight == null ? 0.5 : weight); - - // algorithm from Sass's mix(). Ratio of first color in mix is - // determined by the alphas of both colors and the weight - var t1 = weight * 2 - 1, - d = this.alpha() - color2.alpha(); - - var weight1 = (((t1 * d == -1) ? t1 : (t1 + d) / (1 + t1 * d)) + 1) / 2; - var weight2 = 1 - weight1; - - var rgb = this.rgbArray(); - var rgb2 = color2.rgbArray(); - - for (var i = 0; i < rgb.length; i++) { - rgb[i] = rgb[i] * weight1 + rgb2[i] * weight2; - } - this.setValues("rgb", rgb); - - var alpha = this.alpha() * weight + color2.alpha() * (1 - weight); - this.setValues("alpha", alpha); - - return this; - }, - - toJSON: function() { - return this.rgb(); - }, - - clone: function() { - return new Color(this.rgb()); - } -} - - -Color.prototype.getValues = function(space) { - var vals = {}; - for (var i = 0; i < space.length; i++) { - vals[space.charAt(i)] = this.values[space][i]; - } - if (this.values.alpha != 1) { - vals["a"] = this.values.alpha; - } - // {r: 255, g: 255, b: 255, a: 0.4} - return vals; -} - -Color.prototype.setValues = function(space, vals) { - var spaces = { - "rgb": ["red", "green", "blue"], - "hsl": ["hue", "saturation", "lightness"], - "hsv": ["hue", "saturation", "value"], - "hwb": ["hue", "whiteness", "blackness"], - "cmyk": ["cyan", "magenta", "yellow", "black"] - }; - - var maxes = { - "rgb": [255, 255, 255], - "hsl": [360, 100, 100], - "hsv": [360, 100, 100], - "hwb": [360, 100, 100], - "cmyk": [100, 100, 100, 100] - }; - - var alpha = 1; - if (space == "alpha") { - alpha = vals; - } else if (vals.length) { - // [10, 10, 10] - this.values[space] = vals.slice(0, space.length); - alpha = vals[space.length]; - } else if (vals[space.charAt(0)] !== undefined) { - // {r: 10, g: 10, b: 10} - for (var i = 0; i < space.length; i++) { - this.values[space][i] = vals[space.charAt(i)]; - } - alpha = vals.a; - } else if (vals[spaces[space][0]] !== undefined) { - // {red: 10, green: 10, blue: 10} - var chans = spaces[space]; - for (var i = 0; i < space.length; i++) { - this.values[space][i] = vals[chans[i]]; - } - alpha = vals.alpha; - } - this.values.alpha = Math.max(0, Math.min(1, (alpha !== undefined ? alpha : this.values.alpha))); - if (space == "alpha") { - return; - } - - // cap values of the space prior converting all values - for (var i = 0; i < space.length; i++) { - var capped = Math.max(0, Math.min(maxes[space][i], this.values[space][i])); - this.values[space][i] = Math.round(capped); - } +/* 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})$/, + hex = /^#([a-fA-F0-9]{6})$/, + rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, + per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, + keyword = /(\w+)/; + + var rgb = [0, 0, 0], + a = 1, + match = string.match(abbr); + if (match) { + match = match[1]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i] + match[i], 16); + } + } + else if (match = string.match(hex)) { + match = match[1]; + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); + } + } + else if (match = string.match(rgba)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = parseInt(match[i + 1]); + } + a = parseFloat(match[4]); + } + else if (match = string.match(per)) { + for (var i = 0; i < rgb.length; i++) { + rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); + } + a = parseFloat(match[4]); + } + else if (match = string.match(keyword)) { + if (match[1] == "transparent") { + return [0, 0, 0, 0]; + } + rgb = colorNames[match[1]]; + if (!rgb) { + return; + } + } + + for (var i = 0; i < rgb.length; i++) { + rgb[i] = scale(rgb[i], 0, 255); + } + if (!a && a != 0) { + a = 1; + } + else { + a = scale(a, 0, 1); + } + rgb[3] = a; + return rgb; +} + +function getHsla(string) { + if (!string) { + return; + } + var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hsl); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + s = scale(parseFloat(match[2]), 0, 100), + l = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, s, l, a]; + } +} + +function getHwb(string) { + if (!string) { + return; + } + var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; + var match = string.match(hwb); + if (match) { + var alpha = parseFloat(match[4]); + var h = scale(parseInt(match[1]), 0, 360), + w = scale(parseFloat(match[2]), 0, 100), + b = scale(parseFloat(match[3]), 0, 100), + a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); + return [h, w, b, a]; + } +} + +function getRgb(string) { + var rgba = getRgba(string); + return rgba && rgba.slice(0, 3); +} + +function getHsl(string) { + var hsla = getHsla(string); + return hsla && hsla.slice(0, 3); +} + +function getAlpha(string) { + var vals = getRgba(string); + if (vals) { + return vals[3]; + } + else if (vals = getHsla(string)) { + return vals[3]; + } + else if (vals = getHwb(string)) { + return vals[3]; + } +} + +// generators +function hexString(rgb) { + return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) + + hexDouble(rgb[2]); +} + +function rgbString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return rgbaString(rgba, alpha); + } + return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; +} + +function rgbaString(rgba, alpha) { + if (alpha === undefined) { + alpha = (rgba[3] !== undefined ? rgba[3] : 1); + } + return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + + ", " + alpha + ")"; +} + +function percentString(rgba, alpha) { + if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { + return percentaString(rgba, alpha); + } + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + + return "rgb(" + r + "%, " + g + "%, " + b + "%)"; +} + +function percentaString(rgba, alpha) { + var r = Math.round(rgba[0]/255 * 100), + g = Math.round(rgba[1]/255 * 100), + b = Math.round(rgba[2]/255 * 100); + return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; +} + +function hslString(hsla, alpha) { + if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { + return hslaString(hsla, alpha); + } + return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; +} + +function hslaString(hsla, alpha) { + if (alpha === undefined) { + alpha = (hsla[3] !== undefined ? hsla[3] : 1); + } + return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + + alpha + ")"; +} + +// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax +// (hwb have alpha optional & 1 is default value) +function hwbString(hwb, alpha) { + if (alpha === undefined) { + alpha = (hwb[3] !== undefined ? hwb[3] : 1); + } + return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" + + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; +} + +function keyword(rgb) { + return reverseNames[rgb.slice(0, 3)]; +} + +// helpers +function scale(num, min, max) { + return Math.min(Math.max(min, num), max); +} + +function hexDouble(num) { + var str = num.toString(16).toUpperCase(); + return (str.length < 2) ? "0" + str : str; +} + + +//create a list of reverse color names +var reverseNames = {}; +for (var name in colorNames) { + reverseNames[colorNames[name]] = name; +} - // convert to all the other color spaces - for (var sname in spaces) { - if (sname != space) { - this.values[sname] = convert[space][sname](this.values[space]) - } - - // cap values - for (var i = 0; i < sname.length; i++) { - var capped = Math.max(0, Math.min(maxes[sname][i], this.values[sname][i])); - this.values[sname][i] = Math.round(capped); - } - } - return true; -} - -Color.prototype.setSpace = function(space, args) { - var vals = args[0]; - if (vals === undefined) { - // color.rgb() - return this.getValues(space); - } - // color.rgb(10, 10, 10) - if (typeof vals == "number") { - vals = Array.prototype.slice.call(args); - } - this.setValues(space, vals); - return this; -} - -Color.prototype.setChannel = function(space, index, val) { - if (val === undefined) { - // color.red() - return this.values[space][index]; - } - // color.red(100) - this.values[space][index] = val; - this.setValues(space, this.values[space]); - return this; -} - -window.Color = module.exports = Color +},{"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.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 { + throw new Error('Unable to parse color from string "' + obj + '"'); + } + } 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); + } else { + throw new Error('Unable to parse color from object ' + JSON.stringify(obj)); + } + } +}; + +Color.prototype = { + 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; + + 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; -},{"color-convert":4,"color-string":6}],3:[function(require,module,exports){ +},{"2":2,"5":5}],4:[function(require,module,exports){ /* MIT license */ module.exports = { @@ -1128,8 +1417,8 @@ for (var key in cssKeywords) { reverseKeywords[JSON.stringify(cssKeywords[key])] = key; } -},{}],4:[function(require,module,exports){ -var conversions = require("./conversions"); +},{}],5:[function(require,module,exports){ +var conversions = require(4); var convert = function() { return new Converter(); @@ -1153,11 +1442,11 @@ for (var func in conversions) { // export rgb2hsl and ["rgb"]["hsl"] convert[from] = convert[from] || {}; - convert[from][to] = convert[func] = (function(func) { + 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 @@ -1185,12 +1474,12 @@ Converter.prototype.routeSpace = function(space, args) { } // color.rgb(10, 10, 10) if (typeof values == "number") { - values = Array.prototype.slice.call(args); + 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; @@ -1221,7 +1510,7 @@ Converter.prototype.getValues = function(space) { }); module.exports = convert; -},{"./conversions":3}],5:[function(require,module,exports){ +},{"4":4}],6:[function(require,module,exports){ module.exports = { "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], @@ -1333,8011 +1622,9936 @@ module.exports = { "orchid": [218, 112, 214], "palegoldenrod": [238, 232, 170], "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; -},{}],6:[function(require,module,exports){ -/* MIT license */ -var colorNames = require('color-name'); - -module.exports = { - getRgba: getRgba, - getHsla: getHsla, - getRgb: getRgb, - getHsl: getHsl, - getHwb: getHwb, - getAlpha: getAlpha, - - hexString: hexString, - rgbString: rgbString, - rgbaString: rgbaString, - percentString: percentString, - percentaString: percentaString, - hslString: hslString, - hslaString: hslaString, - hwbString: hwbString, - keyword: keyword -} - -function getRgba(string) { - if (!string) { - return; - } - var abbr = /^#([a-fA-F0-9]{3})$/, - hex = /^#([a-fA-F0-9]{6})$/, - rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, - per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, - keyword = /(\w+)/; - - var rgb = [0, 0, 0], - a = 1, - match = string.match(abbr); - if (match) { - match = match[1]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i] + match[i], 16); - } - } - else if (match = string.match(hex)) { - match = match[1]; - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); - } - } - else if (match = string.match(rgba)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = parseInt(match[i + 1]); - } - a = parseFloat(match[4]); - } - else if (match = string.match(per)) { - for (var i = 0; i < rgb.length; i++) { - rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); - } - a = parseFloat(match[4]); - } - else if (match = string.match(keyword)) { - if (match[1] == "transparent") { - return [0, 0, 0, 0]; - } - rgb = colorNames[match[1]]; - if (!rgb) { - return; - } - } - - for (var i = 0; i < rgb.length; i++) { - rgb[i] = scale(rgb[i], 0, 255); - } - if (!a && a != 0) { - a = 1; - } - else { - a = scale(a, 0, 1); - } - rgb[3] = a; - return rgb; -} - -function getHsla(string) { - if (!string) { - return; - } - var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hsl); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - s = scale(parseFloat(match[2]), 0, 100), - l = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, s, l, a]; - } -} - -function getHwb(string) { - if (!string) { - return; - } - var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; - var match = string.match(hwb); - if (match) { - var alpha = parseFloat(match[4]); - var h = scale(parseInt(match[1]), 0, 360), - w = scale(parseFloat(match[2]), 0, 100), - b = scale(parseFloat(match[3]), 0, 100), - a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); - return [h, w, b, a]; - } -} - -function getRgb(string) { - var rgba = getRgba(string); - return rgba && rgba.slice(0, 3); -} - -function getHsl(string) { - var hsla = getHsla(string); - return hsla && hsla.slice(0, 3); -} - -function getAlpha(string) { - var vals = getRgba(string); - if (vals) { - return vals[3]; - } - else if (vals = getHsla(string)) { - return vals[3]; - } - else if (vals = getHwb(string)) { - return vals[3]; - } -} - -// generators -function hexString(rgb) { - return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) - + hexDouble(rgb[2]); -} - -function rgbString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return rgbaString(rgba, alpha); - } - return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; -} - -function rgbaString(rgba, alpha) { - if (alpha === undefined) { - alpha = (rgba[3] !== undefined ? rgba[3] : 1); - } - return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] - + ", " + alpha + ")"; -} - -function percentString(rgba, alpha) { - if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { - return percentaString(rgba, alpha); - } - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - - return "rgb(" + r + "%, " + g + "%, " + b + "%)"; -} - -function percentaString(rgba, alpha) { - var r = Math.round(rgba[0]/255 * 100), - g = Math.round(rgba[1]/255 * 100), - b = Math.round(rgba[2]/255 * 100); - return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; -} - -function hslString(hsla, alpha) { - if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { - return hslaString(hsla, alpha); - } - return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; -} - -function hslaString(hsla, alpha) { - if (alpha === undefined) { - alpha = (hsla[3] !== undefined ? hsla[3] : 1); - } - return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " - + alpha + ")"; -} - -// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax -// (hwb have alpha optional & 1 is default value) -function hwbString(hwb, alpha) { - if (alpha === undefined) { - alpha = (hwb[3] !== undefined ? hwb[3] : 1); - } - return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" - + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; -} - -function keyword(rgb) { - return reverseNames[rgb.slice(0, 3)]; -} - -// helpers -function scale(num, min, max) { - return Math.min(Math.max(min, num), max); -} - -function hexDouble(num) { - var str = num.toString(16).toUpperCase(); - return (str.length < 2) ? "0" + str : str; -} - - -//create a list of reverse color names -var reverseNames = {}; -for (var name in colorNames) { - reverseNames[colorNames[name]] = name; -} - -},{"color-name":5}],7:[function(require,module,exports){ -var Chart = require('./core/core.js')(); - -require('./core/core.helpers')(Chart); -require('./core/core.element')(Chart); -require('./core/core.animation')(Chart); -require('./core/core.controller')(Chart); -require('./core/core.datasetController')(Chart); -require('./core/core.layoutService')(Chart); -require('./core/core.legend')(Chart); -require('./core/core.plugin.js')(Chart); -require('./core/core.scale')(Chart); -require('./core/core.scaleService')(Chart); -require('./core/core.title')(Chart); -require('./core/core.tooltip')(Chart); - -require('./controllers/controller.bar')(Chart); -require('./controllers/controller.bubble')(Chart); -require('./controllers/controller.doughnut')(Chart); -require('./controllers/controller.line')(Chart); -require('./controllers/controller.polarArea')(Chart); -require('./controllers/controller.radar')(Chart); - -require('./scales/scale.category')(Chart); -require('./scales/scale.linear')(Chart); -require('./scales/scale.logarithmic')(Chart); -require('./scales/scale.radialLinear')(Chart); -require('./scales/scale.time')(Chart); - -require('./elements/element.arc')(Chart); -require('./elements/element.line')(Chart); -require('./elements/element.point')(Chart); -require('./elements/element.rectangle')(Chart); - -require('./charts/Chart.Bar')(Chart); -require('./charts/Chart.Bubble')(Chart); -require('./charts/Chart.Doughnut')(Chart); -require('./charts/Chart.Line')(Chart); -require('./charts/Chart.PolarArea')(Chart); -require('./charts/Chart.Radar')(Chart); -require('./charts/Chart.Scatter')(Chart); - -window.Chart = module.exports = Chart; - -},{"./charts/Chart.Bar":8,"./charts/Chart.Bubble":9,"./charts/Chart.Doughnut":10,"./charts/Chart.Line":11,"./charts/Chart.PolarArea":12,"./charts/Chart.Radar":13,"./charts/Chart.Scatter":14,"./controllers/controller.bar":15,"./controllers/controller.bubble":16,"./controllers/controller.doughnut":17,"./controllers/controller.line":18,"./controllers/controller.polarArea":19,"./controllers/controller.radar":20,"./core/core.animation":21,"./core/core.controller":22,"./core/core.datasetController":23,"./core/core.element":24,"./core/core.helpers":25,"./core/core.js":26,"./core/core.layoutService":27,"./core/core.legend":28,"./core/core.plugin.js":29,"./core/core.scale":30,"./core/core.scaleService":31,"./core/core.title":32,"./core/core.tooltip":33,"./elements/element.arc":34,"./elements/element.line":35,"./elements/element.point":36,"./elements/element.rectangle":37,"./scales/scale.category":38,"./scales/scale.linear":39,"./scales/scale.logarithmic":40,"./scales/scale.radialLinear":41,"./scales/scale.time":42}],8:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - Chart.Bar = function(context, config) { - config.type = 'bar'; - - return new Chart(context, config); - }; - -}; -},{}],9:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - Chart.Bubble = function(context, config) { - config.type = 'bubble'; - return new Chart(context, config); - }; - -}; -},{}],10:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - Chart.Doughnut = function(context, config) { - config.type = 'doughnut'; - - return new Chart(context, config); - }; - -}; -},{}],11:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - Chart.Line = function(context, config) { - config.type = 'line'; - - return new Chart(context, config); - }; - -}; -},{}],12:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - Chart.PolarArea = function(context, config) { - config.type = 'polarArea'; - - return new Chart(context, config); - }; - -}; -},{}],13:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - var defaultConfig = { - aspectRatio: 1 - }; - - Chart.Radar = function(context, config) { - config.options = helpers.configMerge(defaultConfig, config.options); - config.type = 'radar'; - - return new Chart(context, config); - }; - -}; - -},{}],14:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var defaultConfig = { - hover: { - mode: 'single' - }, - - scales: { - xAxes: [{ - type: "linear", // scatter should not use a category axis - position: "bottom", - id: "x-axis-1" // need an ID so datasets can reference the scale - }], - yAxes: [{ - type: "linear", - position: "left", - id: "y-axis-1" - }] - }, - - tooltips: { - callbacks: { - title: function(tooltipItems, data) { - // Title doesn't make sense for scatter since we format the data as a point - return ''; - }, - label: function(tooltipItem, data) { - return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; - } - } - } - }; - - // Register the default config for this type - Chart.defaults.scatter = defaultConfig; - - // Scatter charts use line controllers - Chart.controllers.scatter = Chart.controllers.line; - - Chart.Scatter = function(context, config) { - config.type = 'scatter'; - return new Chart(context, config); - }; - -}; -},{}],15:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.bar = { - hover: { - mode: "label" - }, - - scales: { - xAxes: [{ - type: "category", - - // Specific to Bar Controller - categoryPercentage: 0.8, - barPercentage: 0.9, - - // grid line settings - gridLines: { - offsetGridLines: true - } - }], - yAxes: [{ - type: "linear" - }] - } - }; - - Chart.controllers.bar = Chart.DatasetController.extend({ - initialize: function(chart, datasetIndex) { - Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex); - - // Use this to indicate that this is a bar dataset. - this.getDataset().bar = true; - }, - // Get the number of datasets that display bars. We use this to correctly calculate the bar width - getBarCount: function getBarCount() { - var barCount = 0; - helpers.each(this.chart.data.datasets, function(dataset) { - if (helpers.isDatasetVisible(dataset) && dataset.bar) { - ++barCount; - } - }); - return barCount; - }, - - addElements: function() { - this.getDataset().metaData = this.getDataset().metaData || []; - helpers.each(this.getDataset().data, function(value, index) { - this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Rectangle({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - }, this); - }, - addElementAndReset: function(index) { - this.getDataset().metaData = this.getDataset().metaData || []; - var rectangle = new Chart.elements.Rectangle({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - - var numBars = this.getBarCount(); - - this.updateElement(rectangle, index, true, numBars); - this.getDataset().metaData.splice(index, 0, rectangle); - }, - - update: function update(reset) { - var numBars = this.getBarCount(); - - helpers.each(this.getDataset().metaData, function(rectangle, index) { - this.updateElement(rectangle, index, reset, numBars); - }, this); - }, - - updateElement: function updateElement(rectangle, index, reset, numBars) { - - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var yScale = this.getScaleForId(this.getDataset().yAxisID); - - var yScalePoint; - - if (yScale.min < 0 && yScale.max < 0) { - // all less than 0. use the top - yScalePoint = yScale.getPixelForValue(yScale.max); - } else if (yScale.min > 0 && yScale.max > 0) { - yScalePoint = yScale.getPixelForValue(yScale.min); - } else { - yScalePoint = yScale.getPixelForValue(0); - } - - helpers.extend(rectangle, { - // Utility - _chart: this.chart.chart, - _xScale: xScale, - _yScale: yScale, - _datasetIndex: this.index, - _index: index, - - - // Desired view properties - _model: { - x: this.calculateBarX(index, this.index), - y: reset ? yScalePoint : this.calculateBarY(index, this.index), - - // Tooltip - label: this.chart.data.labels[index], - datasetLabel: this.getDataset().label, - - // Appearance - base: reset ? yScalePoint : this.calculateBarBase(this.index, index), - width: this.calculateBarWidth(numBars), - backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor), - borderSkipped: rectangle.custom && rectangle.custom.borderSkipped ? rectangle.custom.borderSkipped : this.chart.options.elements.rectangle.borderSkipped, - borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor), - borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth) - } - }); - rectangle.pivot(); - }, - - calculateBarBase: function(datasetIndex, index) { - - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var yScale = this.getScaleForId(this.getDataset().yAxisID); - - var base = 0; - - if (yScale.options.stacked) { - - var value = this.chart.data.datasets[datasetIndex].data[index]; - - if (value < 0) { - for (var i = 0; i < datasetIndex; i++) { - var negDS = this.chart.data.datasets[i]; - if (helpers.isDatasetVisible(negDS) && negDS.yAxisID === yScale.id && negDS.bar) { - base += negDS.data[index] < 0 ? negDS.data[index] : 0; - } - } - } else { - for (var j = 0; j < datasetIndex; j++) { - var posDS = this.chart.data.datasets[j]; - if (helpers.isDatasetVisible(posDS) && posDS.yAxisID === yScale.id && posDS.bar) { - base += posDS.data[index] > 0 ? posDS.data[index] : 0; - } - } - } - - return yScale.getPixelForValue(base); - } - - base = yScale.getPixelForValue(yScale.min); - - if (yScale.beginAtZero || ((yScale.min <= 0 && yScale.max >= 0) || (yScale.min >= 0 && yScale.max <= 0))) { - base = yScale.getPixelForValue(0, 0); - //base += yScale.options.gridLines.lineWidth; - } else if (yScale.min < 0 && yScale.max < 0) { - // All values are negative. Use the top as the base - base = yScale.getPixelForValue(yScale.max); - } - - return base; - - }, - - getRuler: function() { - - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var yScale = this.getScaleForId(this.getDataset().yAxisID); - var datasetCount = this.getBarCount(); - - var tickWidth = (function() { - var min = xScale.getPixelForTick(1) - xScale.getPixelForTick(0); - for (var i = 2; i < this.getDataset().data.length; i++) { - min = Math.min(xScale.getPixelForTick(i) - xScale.getPixelForTick(i - 1), min); - } - return min; - }).call(this); - var categoryWidth = tickWidth * xScale.options.categoryPercentage; - var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; - var fullBarWidth = categoryWidth / datasetCount; - - if (xScale.ticks.length !== this.chart.data.labels.length) { - var perc = xScale.ticks.length / this.chart.data.labels.length; - fullBarWidth = fullBarWidth * perc; - } - - var barWidth = fullBarWidth * xScale.options.barPercentage; - var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage); - - return { - datasetCount: datasetCount, - tickWidth: tickWidth, - categoryWidth: categoryWidth, - categorySpacing: categorySpacing, - fullBarWidth: fullBarWidth, - barWidth: barWidth, - barSpacing: barSpacing - }; - }, - - calculateBarWidth: function() { - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var ruler = this.getRuler(); - return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth; - }, - - // Get bar index from the given dataset index accounting for the fact that not all bars are visible - getBarIndex: function(datasetIndex) { - var barIndex = 0; - - for (var j = 0; j < datasetIndex; ++j) { - if (helpers.isDatasetVisible(this.chart.data.datasets[j]) && this.chart.data.datasets[j].bar) { - ++barIndex; - } - } - - return barIndex; - }, - - calculateBarX: function(index, datasetIndex) { - - var yScale = this.getScaleForId(this.getDataset().yAxisID); - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var barIndex = this.getBarIndex(datasetIndex); - - var ruler = this.getRuler(); - var leftTick = xScale.getPixelForValue(null, index, datasetIndex, this.chart.isCombo); - leftTick -= this.chart.isCombo ? (ruler.tickWidth / 2) : 0; - - if (xScale.options.stacked) { - return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing; - } - - return leftTick + - (ruler.barWidth / 2) + - ruler.categorySpacing + - (ruler.barWidth * barIndex) + - (ruler.barSpacing / 2) + - (ruler.barSpacing * barIndex); - }, - - calculateBarY: function(index, datasetIndex) { - - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var yScale = this.getScaleForId(this.getDataset().yAxisID); - - var value = this.getDataset().data[index]; - - if (yScale.options.stacked) { - - var sumPos = 0, - sumNeg = 0; - - for (var i = 0; i < datasetIndex; i++) { - var ds = this.chart.data.datasets[i]; - if (helpers.isDatasetVisible(ds) && ds.bar && ds.yAxisID === yScale.id) { - if (ds.data[index] < 0) { - sumNeg += ds.data[index] || 0; - } else { - sumPos += ds.data[index] || 0; - } - } - } - - if (value < 0) { - return yScale.getPixelForValue(sumNeg + value); - } else { - return yScale.getPixelForValue(sumPos + value); - } - - return yScale.getPixelForValue(value); - } - - return yScale.getPixelForValue(value); - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - helpers.each(this.getDataset().metaData, function(rectangle, index) { - var d = this.getDataset().data[index]; - if (d !== null && d !== undefined && !isNaN(d)) { - rectangle.transition(easingDecimal).draw(); - } - }, this); - }, - - setHoverStyle: function(rectangle) { - var dataset = this.chart.data.datasets[rectangle._datasetIndex]; - var index = rectangle._index; - - rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.hoverBackgroundColor ? rectangle.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(rectangle._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); - rectangle._model.borderColor = rectangle.custom && rectangle.custom.hoverBorderColor ? rectangle.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(rectangle._model.borderColor).saturate(0.5).darken(0.1).rgbString()); - rectangle._model.borderWidth = rectangle.custom && rectangle.custom.hoverBorderWidth ? rectangle.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, rectangle._model.borderWidth); - }, - - removeHoverStyle: function(rectangle) { - var dataset = this.chart.data.datasets[rectangle._datasetIndex]; - var index = rectangle._index; - - rectangle._model.backgroundColor = rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor); - rectangle._model.borderColor = rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor); - rectangle._model.borderWidth = rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth); - } - - }); -}; - -},{}],16:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.bubble = { - hover: { - mode: "single" - }, - - scales: { - xAxes: [{ - type: "linear", // bubble should probably use a linear scale by default - position: "bottom", - id: "x-axis-0" // need an ID so datasets can reference the scale - }], - yAxes: [{ - type: "linear", - position: "left", - id: "y-axis-0" - }] - }, - - tooltips: { - callbacks: { - title: function(tooltipItems, data) { - // Title doesn't make sense for scatter since we format the data as a point - return ''; - }, - label: function(tooltipItem, data) { - var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; - var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - return datasetLabel + ': (' + dataPoint.x + ', ' + dataPoint.y + ', ' + dataPoint.r + ')'; - } - } - } - }; - - - Chart.controllers.bubble = Chart.DatasetController.extend({ - addElements: function() { - - this.getDataset().metaData = this.getDataset().metaData || []; - - helpers.each(this.getDataset().data, function(value, index) { - this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - }, this); - }, - addElementAndReset: function(index) { - this.getDataset().metaData = this.getDataset().metaData || []; - var point = new Chart.elements.Point({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - - // Reset the point - this.updateElement(point, index, true); - - // Add to the points array - this.getDataset().metaData.splice(index, 0, point); - }, - - update: function update(reset) { - var points = this.getDataset().metaData; - - var yScale = this.getScaleForId(this.getDataset().yAxisID); - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var scaleBase; - - if (yScale.min < 0 && yScale.max < 0) { - scaleBase = yScale.getPixelForValue(yScale.max); - } else if (yScale.min > 0 && yScale.max > 0) { - scaleBase = yScale.getPixelForValue(yScale.min); - } else { - scaleBase = yScale.getPixelForValue(0); - } - - // Update Points - helpers.each(points, function(point, index) { - this.updateElement(point, index, reset); - }, this); - - }, - - updateElement: function(point, index, reset) { - var yScale = this.getScaleForId(this.getDataset().yAxisID); - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var scaleBase; - - if (yScale.min < 0 && yScale.max < 0) { - scaleBase = yScale.getPixelForValue(yScale.max); - } else if (yScale.min > 0 && yScale.max > 0) { - scaleBase = yScale.getPixelForValue(yScale.min); - } else { - scaleBase = yScale.getPixelForValue(0); - } - - helpers.extend(point, { - // Utility - _chart: this.chart.chart, - _xScale: xScale, - _yScale: yScale, - _datasetIndex: this.index, - _index: index, - - // Desired view properties - _model: { - x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo), - y: reset ? scaleBase : yScale.getPixelForValue(this.getDataset().data[index], index, this.index), - // Appearance - radius: reset ? 0 : point.custom && point.custom.radius ? point.custom.radius : this.getRadius(this.getDataset().data[index]), - backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.point.backgroundColor), - borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.point.borderColor), - borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.point.borderWidth), - - // Tooltip - hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius) - } - }); - - point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); - - point.pivot(); - }, - - getRadius: function(value) { - return value.r || this.chart.options.elements.point.radius; - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - - // Transition and Draw the Points - helpers.each(this.getDataset().metaData, function(point, index) { - point.transition(easingDecimal); - point.draw(); - }); - - }, - - setHoverStyle: function(point) { - // Point - var dataset = this.chart.data.datasets[point._datasetIndex]; - var index = point._index; - - point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, this.chart.options.elements.point.hoverRadius)) + this.getRadius(this.getDataset().data[point._index]); - point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); - point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString()); - point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, point._model.borderWidth); - }, - - removeHoverStyle: function(point) { - var dataset = this.chart.data.datasets[point._datasetIndex]; - var index = point._index; - - point._model.radius = point.custom && point.custom.radius ? point.custom.radius : this.getRadius(this.getDataset().data[point._index]); - point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.point.backgroundColor); - point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.point.borderColor); - point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.point.borderWidth); - } - }); -}; -},{}],17:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.doughnut = { - animation: { - //Boolean - Whether we animate the rotation of the Doughnut - animateRotate: true, - //Boolean - Whether we animate scaling the Doughnut from the centre - animateScale: false - }, - aspectRatio: 1, - hover: { - mode: 'single' - }, - legendCallback: function(chart) { - var text = []; - text.push(''); - return text.join(""); - }, - legend: { - labels: { - generateLabels: function(data) { - if (data.labels.length && data.datasets.length) { - - return data.labels.map(function(label, i) { - var ds = data.datasets[0]; - var arc = ds.metaData[i]; - var fill = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(ds.backgroundColor, i, this.chart.options.elements.arc.backgroundColor); - var stroke = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(ds.borderColor, i, this.chart.options.elements.arc.borderColor); - var bw = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(ds.borderWidth, i, this.chart.options.elements.arc.borderWidth); - - return { - text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(data.datasets[0].data[i]), - - // Extra data used for toggling the correct item - index: i - }; - }, this); - } else { - return []; - } - } - }, - onClick: function(e, legendItem) { - helpers.each(this.chart.data.datasets, function(dataset) { - dataset.metaHiddenData = dataset.metaHiddenData || []; - var idx = legendItem.index; - - if (!isNaN(dataset.data[idx])) { - dataset.metaHiddenData[idx] = dataset.data[idx]; - dataset.data[idx] = NaN; - } else if (!isNaN(dataset.metaHiddenData[idx])) { - dataset.data[idx] = dataset.metaHiddenData[idx]; - } - }); - - this.chart.update(); - } - }, - - //The percentage of the chart that we cut out of the middle. - cutoutPercentage: 50, - - //The rotation of the chart, where the first data arc begins. - rotation: Math.PI * -0.5, - - //The total circumference of the chart. - circumference: Math.PI * 2.0, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; - } - } - } - }; - - Chart.defaults.pie = helpers.clone(Chart.defaults.doughnut); - helpers.extend(Chart.defaults.pie, { - cutoutPercentage: 0 - }); - - - Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ - linkScales: function() { - // no scales for doughnut - }, - - addElements: function() { - this.getDataset().metaData = this.getDataset().metaData || []; - helpers.each(this.getDataset().data, function(value, index) { - this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - }, this); - }, - addElementAndReset: function(index, colorForNewElement) { - this.getDataset().metaData = this.getDataset().metaData || []; - var arc = new Chart.elements.Arc({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - - if (colorForNewElement && helpers.isArray(this.getDataset().backgroundColor)) { - this.getDataset().backgroundColor.splice(index, 0, colorForNewElement); - } - - // Reset the point - this.updateElement(arc, index, true); - - // Add to the points array - this.getDataset().metaData.splice(index, 0, arc); - }, - - getVisibleDatasetCount: function getVisibleDatasetCount() { - return helpers.where(this.chart.data.datasets, function(ds) { - return helpers.isDatasetVisible(ds); - }).length; - }, - - // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly - getRingIndex: function getRingIndex(datasetIndex) { - var ringIndex = 0; - - for (var j = 0; j < datasetIndex; ++j) { - if (helpers.isDatasetVisible(this.chart.data.datasets[j])) { - ++ringIndex; - } - } - - return ringIndex; - }, - - update: function update(reset) { - var availableWidth = this.chart.chartArea.right - this.chart.chartArea.left - this.chart.options.elements.arc.borderWidth; - var availableHeight = this.chart.chartArea.bottom - this.chart.chartArea.top - this.chart.options.elements.arc.borderWidth; - var minSize = Math.min(availableWidth, availableHeight); - var offset = {x: 0, y: 0}; - - // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc - if (this.chart.options.circumference < Math.PI * 2.0) { - var startAngle = this.chart.options.rotation % (Math.PI * 2.0); - startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); - var endAngle = startAngle + this.chart.options.circumference; - var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; - var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; - var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); - var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); - var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); - var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); - var cutout = this.chart.options.cutoutPercentage / 100.0; - var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; - var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; - var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; - minSize = Math.min(availableWidth / size.width, availableHeight / size.height); - offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; - } - - this.chart.outerRadius = Math.max(minSize / 2, 0); - this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0); - this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount(); - this.chart.offsetX = offset.x * this.chart.outerRadius; - this.chart.offsetY = offset.y * this.chart.outerRadius; - - this.getDataset().total = 0; - helpers.each(this.getDataset().data, function(value) { - if (!isNaN(value)) { - this.getDataset().total += Math.abs(value); - } - }, this); - - this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.getRingIndex(this.index)); - this.innerRadius = this.outerRadius - this.chart.radiusLength; - - helpers.each(this.getDataset().metaData, function(arc, index) { - this.updateElement(arc, index, reset); - }, this); - }, - updateElement: function(arc, index, reset) { - var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2; - var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2; - var startAngle = this.chart.options.rotation; // non reset case handled later - var endAngle = this.chart.options.rotation; // non reset case handled later - var circumference = reset && this.chart.options.animation.animateRotate ? 0 : this.calculateCircumference(this.getDataset().data[index]) * (this.chart.options.circumference / (2.0 * Math.PI)); - var innerRadius = reset && this.chart.options.animation.animateScale ? 0 : this.innerRadius; - var outerRadius = reset && this.chart.options.animation.animateScale ? 0 : this.outerRadius; - - helpers.extend(arc, { - // Utility - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index, - - // Desired view properties - _model: { - x: centerX + this.chart.offsetX, - y: centerY + this.chart.offsetY, - startAngle: startAngle, - endAngle: endAngle, - circumference: circumference, - outerRadius: outerRadius, - innerRadius: innerRadius, - - backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor), - hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor), - borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth), - borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor), - - label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index]) - } - }); - - // Set correct angles if not resetting - if (!reset) { - - if (index === 0) { - arc._model.startAngle = this.chart.options.rotation; - } else { - arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle; - } - - arc._model.endAngle = arc._model.startAngle + arc._model.circumference; - } - - arc.pivot(); - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - helpers.each(this.getDataset().metaData, function(arc, index) { - arc.transition(easingDecimal).draw(); - }); - }, - - setHoverStyle: function(arc) { - var dataset = this.chart.data.datasets[arc._datasetIndex]; - var index = arc._index; - - arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); - arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString()); - arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth); - }, - - removeHoverStyle: function(arc) { - var dataset = this.chart.data.datasets[arc._datasetIndex]; - var index = arc._index; - - arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor); - arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor); - arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth); - }, - - calculateCircumference: function(value) { - if (this.getDataset().total > 0 && !isNaN(value)) { - return (Math.PI * 2.0) * (value / this.getDataset().total); - } else { - return 0; - } - } - }); -}; - -},{}],18:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.line = { - showLines: true, - - hover: { - mode: "label" - }, - - scales: { - xAxes: [{ - type: "category", - id: 'x-axis-0' - }], - yAxes: [{ - type: "linear", - id: 'y-axis-0' - }] - } - }; - - - Chart.controllers.line = Chart.DatasetController.extend({ - addElements: function() { - - this.getDataset().metaData = this.getDataset().metaData || []; - - this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _points: this.getDataset().metaData - }); - - helpers.each(this.getDataset().data, function(value, index) { - this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - }, this); - }, - addElementAndReset: function(index) { - this.getDataset().metaData = this.getDataset().metaData || []; - var point = new Chart.elements.Point({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - - // Reset the point - this.updateElement(point, index, true); - - // Add to the points array - this.getDataset().metaData.splice(index, 0, point); - - // Make sure bezier control points are updated - if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0) - this.updateBezierControlPoints(); - }, - - update: function update(reset) { - var line = this.getDataset().metaDataset; - var points = this.getDataset().metaData; - - var yScale = this.getScaleForId(this.getDataset().yAxisID); - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var scaleBase; - - if (yScale.min < 0 && yScale.max < 0) { - scaleBase = yScale.getPixelForValue(yScale.max); - } else if (yScale.min > 0 && yScale.max > 0) { - scaleBase = yScale.getPixelForValue(yScale.min); - } else { - scaleBase = yScale.getPixelForValue(0); - } - - // Update Line - if (this.chart.options.showLines) { - // Utility - line._scale = yScale; - line._datasetIndex = this.index; - // Data - line._children = points; - // Model - - // Compatibility: If the properties are defined with only the old name, use those values - if ((this.getDataset().tension !== undefined) && (this.getDataset().lineTension === undefined)) - { - this.getDataset().lineTension = this.getDataset().tension; - } - - line._model = { - // Appearance - tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(this.getDataset().lineTension, this.chart.options.elements.line.tension), - backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor), - borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth), - borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor), - borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle), - borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash), - borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset), - borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle), - fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill), - // Scale - scaleTop: yScale.top, - scaleBottom: yScale.bottom, - scaleZero: scaleBase - }; - line.pivot(); - } - - // Update Points - helpers.each(points, function(point, index) { - this.updateElement(point, index, reset); - }, this); - - if (this.chart.options.showLines && this.chart.options.elements.line.tension !== 0) - this.updateBezierControlPoints(); - }, - - getPointBackgroundColor: function(point, index) { - var backgroundColor = this.chart.options.elements.point.backgroundColor; - var dataset = this.getDataset(); - - if (point.custom && point.custom.backgroundColor) { - backgroundColor = point.custom.backgroundColor; - } else if (dataset.pointBackgroundColor) { - backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); - } else if (dataset.backgroundColor) { - backgroundColor = dataset.backgroundColor; - } - - return backgroundColor; - }, - getPointBorderColor: function(point, index) { - var borderColor = this.chart.options.elements.point.borderColor; - var dataset = this.getDataset(); - - if (point.custom && point.custom.borderColor) { - borderColor = point.custom.borderColor; - } else if (dataset.pointBorderColor) { - borderColor = helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, borderColor); - } else if (dataset.borderColor) { - borderColor = dataset.borderColor; - } - - return borderColor; - }, - getPointBorderWidth: function(point, index) { - var borderWidth = this.chart.options.elements.point.borderWidth; - var dataset = this.getDataset(); - - if (point.custom && point.custom.borderWidth !== undefined) { - borderWidth = point.custom.borderWidth; - } else if (dataset.pointBorderWidth !== undefined) { - borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); - } else if (dataset.borderWidth !== undefined) { - borderWidth = dataset.borderWidth; - } - - return borderWidth; - }, - - updateElement: function(point, index, reset) { - var yScale = this.getScaleForId(this.getDataset().yAxisID); - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var scaleBase; - - if (yScale.min < 0 && yScale.max < 0) { - scaleBase = yScale.getPixelForValue(yScale.max); - } else if (yScale.min > 0 && yScale.max > 0) { - scaleBase = yScale.getPixelForValue(yScale.min); - } else { - scaleBase = yScale.getPixelForValue(0); - } - - // Utility - point._chart = this.chart.chart; - point._xScale = xScale; - point._yScale = yScale; - point._datasetIndex = this.index; - point._index = index; - - // Desired view properties - - // Compatibility: If the properties are defined with only the old name, use those values - if ((this.getDataset().radius !== undefined) && (this.getDataset().pointRadius === undefined)) - { - this.getDataset().pointRadius = this.getDataset().radius; - } - if ((this.getDataset().hitRadius !== undefined) && (this.getDataset().pointHitRadius === undefined)) - { - this.getDataset().pointHitRadius = this.getDataset().hitRadius; - } - - point._model = { - x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index, this.chart.isCombo), - y: reset ? scaleBase : this.calculatePointY(this.getDataset().data[index], index, this.index, this.chart.isCombo), - // Appearance - radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().pointRadius, index, this.chart.options.elements.point.radius), - pointStyle: point.custom && point.custom.pointStyle ? point.custom.pointStyle : helpers.getValueAtIndexOrDefault(this.getDataset().pointStyle, index, this.chart.options.elements.point.pointStyle), - backgroundColor: this.getPointBackgroundColor(point, index), - borderColor: this.getPointBorderColor(point, index), - borderWidth: this.getPointBorderWidth(point, index), - // Tooltip - hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().pointHitRadius, index, this.chart.options.elements.point.hitRadius) - }; - - point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); - }, - - calculatePointY: function(value, index, datasetIndex, isCombo) { - - var xScale = this.getScaleForId(this.getDataset().xAxisID); - var yScale = this.getScaleForId(this.getDataset().yAxisID); - - if (yScale.options.stacked) { - - var sumPos = 0, - sumNeg = 0; - - for (var i = 0; i < datasetIndex; i++) { - var ds = this.chart.data.datasets[i]; - if (ds.type === 'line' && helpers.isDatasetVisible(ds)) { - if (ds.data[index] < 0) { - sumNeg += ds.data[index] || 0; - } else { - sumPos += ds.data[index] || 0; - } - } - } - - if (value < 0) { - return yScale.getPixelForValue(sumNeg + value); - } else { - return yScale.getPixelForValue(sumPos + value); - } - } - - return yScale.getPixelForValue(value); - }, - - updateBezierControlPoints: function() { - // Update bezier control points - helpers.each(this.getDataset().metaData, function(point, index) { - var controlPoints = helpers.splineCurve( - helpers.previousItem(this.getDataset().metaData, index)._model, - point._model, - helpers.nextItem(this.getDataset().metaData, index)._model, - this.getDataset().metaDataset._model.tension - ); - - // Prevent the bezier going outside of the bounds of the graph - point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left); - point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top); - - point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left); - point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top); - - // Now pivot the point for animation - point.pivot(); - }, this); - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - - // Transition Point Locations - helpers.each(this.getDataset().metaData, function(point) { - point.transition(easingDecimal); - }); - - // Transition and Draw the line - if (this.chart.options.showLines) - this.getDataset().metaDataset.transition(easingDecimal).draw(); - - // Draw the points - helpers.each(this.getDataset().metaData, function(point) { - point.draw(); - }); - }, - - setHoverStyle: function(point) { - // Point - var dataset = this.chart.data.datasets[point._datasetIndex]; - var index = point._index; - - point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); - point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); - point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString()); - point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, point._model.borderWidth); - }, - - removeHoverStyle: function(point) { - var dataset = this.chart.data.datasets[point._datasetIndex]; - var index = point._index; - - // Compatibility: If the properties are defined with only the old name, use those values - if ((this.getDataset().radius !== undefined) && (this.getDataset().pointRadius === undefined)) - { - this.getDataset().pointRadius = this.getDataset().radius; - } - - point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().pointRadius, index, this.chart.options.elements.point.radius); - point._model.backgroundColor = this.getPointBackgroundColor(point, index); - point._model.borderColor = this.getPointBorderColor(point, index); - point._model.borderWidth = this.getPointBorderWidth(point, index); - } - }); -}; - -},{}],19:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.polarArea = { - - scale: { - type: "radialLinear", - lineArc: true // so that lines are circular - }, - - //Boolean - Whether to animate the rotation of the chart - animateRotate: true, - animateScale: true, - - aspectRatio: 1, - legendCallback: function(chart) { - var text = []; - text.push(''); - return text.join(""); - }, - legend: { - labels: { - generateLabels: function(data) { - if (data.labels.length && data.datasets.length) { - return data.labels.map(function(label, i) { - var ds = data.datasets[0]; - var arc = ds.metaData[i]; - var fill = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(ds.backgroundColor, i, this.chart.options.elements.arc.backgroundColor); - var stroke = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(ds.borderColor, i, this.chart.options.elements.arc.borderColor); - var bw = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(ds.borderWidth, i, this.chart.options.elements.arc.borderWidth); - - return { - text: label, - fillStyle: fill, - strokeStyle: stroke, - lineWidth: bw, - hidden: isNaN(data.datasets[0].data[i]), - - // Extra data used for toggling the correct item - index: i - }; - }, this); - } else { - return []; - } - } - }, - onClick: function(e, legendItem) { - helpers.each(this.chart.data.datasets, function(dataset) { - dataset.metaHiddenData = dataset.metaHiddenData || []; - var idx = legendItem.index; - - if (!isNaN(dataset.data[idx])) { - dataset.metaHiddenData[idx] = dataset.data[idx]; - dataset.data[idx] = NaN; - } else if (!isNaN(dataset.metaHiddenData[idx])) { - dataset.data[idx] = dataset.metaHiddenData[idx]; - } - }); - - this.chart.update(); - } - }, - - // Need to override these to give a nice default - tooltips: { - callbacks: { - title: function() { - return ''; - }, - label: function(tooltipItem, data) { - return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; - } - } - } - }; - - Chart.controllers.polarArea = Chart.DatasetController.extend({ - linkScales: function() { - // no scales for doughnut - }, - addElements: function() { - this.getDataset().metaData = this.getDataset().metaData || []; - helpers.each(this.getDataset().data, function(value, index) { - this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - }, this); - }, - addElementAndReset: function(index) { - this.getDataset().metaData = this.getDataset().metaData || []; - var arc = new Chart.elements.Arc({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - - // Reset the point - this.updateElement(arc, index, true); - - // Add to the points array - this.getDataset().metaData.splice(index, 0, arc); - }, - getVisibleDatasetCount: function getVisibleDatasetCount() { - return helpers.where(this.chart.data.datasets, function(ds) { - return helpers.isDatasetVisible(ds); - }).length; - }, - - update: function update(reset) { - var minSize = Math.min(this.chart.chartArea.right - this.chart.chartArea.left, this.chart.chartArea.bottom - this.chart.chartArea.top); - this.chart.outerRadius = Math.max((minSize - this.chart.options.elements.arc.borderWidth / 2) / 2, 0); - this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0); - this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount(); - - this.getDataset().total = 0; - helpers.each(this.getDataset().data, function(value) { - this.getDataset().total += Math.abs(value); - }, this); - - this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.index); - this.innerRadius = this.outerRadius - this.chart.radiusLength; - - helpers.each(this.getDataset().metaData, function(arc, index) { - this.updateElement(arc, index, reset); - }, this); - }, - - updateElement: function(arc, index, reset) { - var circumference = this.calculateCircumference(this.getDataset().data[index]); - var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2; - var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2; - - // If there is NaN data before us, we need to calculate the starting angle correctly. - // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data - var notNullIndex = 0; - for (var i = 0; i < index; ++i) { - if (!isNaN(this.getDataset().data[i])) { - ++notNullIndex; - } - } - - var startAngle = (-0.5 * Math.PI) + (circumference * notNullIndex); - var endAngle = startAngle + circumference; - - var resetModel = { - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius: this.chart.options.animateScale ? 0 : this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]), - startAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : startAngle, - endAngle: this.chart.options.animateRotate ? Math.PI * -0.5 : endAngle, - - backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor), - borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth), - borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor), - - label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index]) - }; - - helpers.extend(arc, { - // Utility - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index, - _scale: this.chart.scale, - - // Desired view properties - _model: reset ? resetModel : { - x: centerX, - y: centerY, - innerRadius: 0, - outerRadius: this.chart.scale.getDistanceFromCenterForValue(this.getDataset().data[index]), - startAngle: startAngle, - endAngle: endAngle, - - backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor), - borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth), - borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor), - - label: helpers.getValueAtIndexOrDefault(this.chart.data.labels, index, this.chart.data.labels[index]) - } - }); - - arc.pivot(); - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - helpers.each(this.getDataset().metaData, function(arc, index) { - arc.transition(easingDecimal).draw(); - }); - }, - - setHoverStyle: function(arc) { - var dataset = this.chart.data.datasets[arc._datasetIndex]; - var index = arc._index; - - arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); - arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString()); - arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth); - }, - - removeHoverStyle: function(arc) { - var dataset = this.chart.data.datasets[arc._datasetIndex]; - var index = arc._index; - - arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor); - arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor); - arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth); - }, - - calculateCircumference: function(value) { - if (isNaN(value)) { - return 0; - } else { - // Count the number of NaN values - var numNaN = helpers.where(this.getDataset().data, function(data) { - return isNaN(data); - }).length; - - return (2 * Math.PI) / (this.getDataset().data.length - numNaN); - } - } - }); - -}; -},{}],20:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - - Chart.defaults.radar = { - scale: { - type: "radialLinear" - }, - elements: { - line: { - tension: 0 // no bezier in radar - } - } - }; - - Chart.controllers.radar = Chart.DatasetController.extend({ - linkScales: function() { - // No need. Single scale only - }, - - addElements: function() { - - this.getDataset().metaData = this.getDataset().metaData || []; - - this.getDataset().metaDataset = this.getDataset().metaDataset || new Chart.elements.Line({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _points: this.getDataset().metaData, - _loop: true - }); - - helpers.each(this.getDataset().data, function(value, index) { - this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Point({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index, - _model: { - x: 0, //xScale.getPixelForValue(null, index, true), - y: 0 //this.chartArea.bottom, - } - }); - }, this); - }, - addElementAndReset: function(index) { - this.getDataset().metaData = this.getDataset().metaData || []; - var point = new Chart.elements.Point({ - _chart: this.chart.chart, - _datasetIndex: this.index, - _index: index - }); - - // Reset the point - this.updateElement(point, index, true); - - // Add to the points array - this.getDataset().metaData.splice(index, 0, point); - - // Make sure bezier control points are updated - this.updateBezierControlPoints(); - }, - - update: function update(reset) { - - var line = this.getDataset().metaDataset; - var points = this.getDataset().metaData; - - var scale = this.chart.scale; - var scaleBase; - - if (scale.min < 0 && scale.max < 0) { - scaleBase = scale.getPointPositionForValue(0, scale.max); - } else if (scale.min > 0 && scale.max > 0) { - scaleBase = scale.getPointPositionForValue(0, scale.min); - } else { - scaleBase = scale.getPointPositionForValue(0, 0); - } - - helpers.extend(this.getDataset().metaDataset, { - // Utility - _datasetIndex: this.index, - // Data - _children: this.getDataset().metaData, - // Model - _model: { - // Appearance - tension: line.custom && line.custom.tension ? line.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension), - backgroundColor: line.custom && line.custom.backgroundColor ? line.custom.backgroundColor : (this.getDataset().backgroundColor || this.chart.options.elements.line.backgroundColor), - borderWidth: line.custom && line.custom.borderWidth ? line.custom.borderWidth : (this.getDataset().borderWidth || this.chart.options.elements.line.borderWidth), - borderColor: line.custom && line.custom.borderColor ? line.custom.borderColor : (this.getDataset().borderColor || this.chart.options.elements.line.borderColor), - fill: line.custom && line.custom.fill ? line.custom.fill : (this.getDataset().fill !== undefined ? this.getDataset().fill : this.chart.options.elements.line.fill), - borderCapStyle: line.custom && line.custom.borderCapStyle ? line.custom.borderCapStyle : (this.getDataset().borderCapStyle || this.chart.options.elements.line.borderCapStyle), - borderDash: line.custom && line.custom.borderDash ? line.custom.borderDash : (this.getDataset().borderDash || this.chart.options.elements.line.borderDash), - borderDashOffset: line.custom && line.custom.borderDashOffset ? line.custom.borderDashOffset : (this.getDataset().borderDashOffset || this.chart.options.elements.line.borderDashOffset), - borderJoinStyle: line.custom && line.custom.borderJoinStyle ? line.custom.borderJoinStyle : (this.getDataset().borderJoinStyle || this.chart.options.elements.line.borderJoinStyle), - - // Scale - scaleTop: scale.top, - scaleBottom: scale.bottom, - scaleZero: scaleBase - } - }); - - this.getDataset().metaDataset.pivot(); - - // Update Points - helpers.each(points, function(point, index) { - this.updateElement(point, index, reset); - }, this); - - - // Update bezier control points - this.updateBezierControlPoints(); - }, - updateElement: function(point, index, reset) { - var pointPosition = this.chart.scale.getPointPositionForValue(index, this.getDataset().data[index]); - - helpers.extend(point, { - // Utility - _datasetIndex: this.index, - _index: index, - _scale: this.chart.scale, - - // Desired view properties - _model: { - x: reset ? this.chart.scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales - y: reset ? this.chart.scale.yCenter : pointPosition.y, - - // Appearance - tension: point.custom && point.custom.tension ? point.custom.tension : helpers.getValueOrDefault(this.getDataset().tension, this.chart.options.elements.line.tension), - radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().pointRadius, index, this.chart.options.elements.point.radius), - backgroundColor: point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor), - borderColor: point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor), - borderWidth: point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth), - pointStyle: point.custom && point.custom.pointStyle ? point.custom.pointStyle : helpers.getValueAtIndexOrDefault(this.getDataset().pointStyle, index, this.chart.options.elements.point.pointStyle), - - // Tooltip - hitRadius: point.custom && point.custom.hitRadius ? point.custom.hitRadius : helpers.getValueAtIndexOrDefault(this.getDataset().hitRadius, index, this.chart.options.elements.point.hitRadius) - } - }); - - point._model.skip = point.custom && point.custom.skip ? point.custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); - }, - updateBezierControlPoints: function() { - helpers.each(this.getDataset().metaData, function(point, index) { - var controlPoints = helpers.splineCurve( - helpers.previousItem(this.getDataset().metaData, index, true)._model, - point._model, - helpers.nextItem(this.getDataset().metaData, index, true)._model, - point._model.tension - ); - - // Prevent the bezier going outside of the bounds of the graph - point._model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, this.chart.chartArea.right), this.chart.chartArea.left); - point._model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, this.chart.chartArea.bottom), this.chart.chartArea.top); - - point._model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, this.chart.chartArea.right), this.chart.chartArea.left); - point._model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, this.chart.chartArea.bottom), this.chart.chartArea.top); - - // Now pivot the point for animation - point.pivot(); - }, this); - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - - // Transition Point Locations - helpers.each(this.getDataset().metaData, function(point, index) { - point.transition(easingDecimal); - }); - - // Transition and Draw the line - this.getDataset().metaDataset.transition(easingDecimal).draw(); - - // Draw the points - helpers.each(this.getDataset().metaData, function(point) { - point.draw(); - }); - }, - - setHoverStyle: function(point) { - // Point - var dataset = this.chart.data.datasets[point._datasetIndex]; - var index = point._index; - - point._model.radius = point.custom && point.custom.hoverRadius ? point.custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); - point._model.backgroundColor = point.custom && point.custom.hoverBackgroundColor ? point.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.color(point._model.backgroundColor).saturate(0.5).darken(0.1).rgbString()); - point._model.borderColor = point.custom && point.custom.hoverBorderColor ? point.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.color(point._model.borderColor).saturate(0.5).darken(0.1).rgbString()); - point._model.borderWidth = point.custom && point.custom.hoverBorderWidth ? point.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, point._model.borderWidth); - }, - - removeHoverStyle: function(point) { - var dataset = this.chart.data.datasets[point._datasetIndex]; - var index = point._index; - - point._model.radius = point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius); - point._model.backgroundColor = point.custom && point.custom.backgroundColor ? point.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBackgroundColor, index, this.chart.options.elements.point.backgroundColor); - point._model.borderColor = point.custom && point.custom.borderColor ? point.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderColor, index, this.chart.options.elements.point.borderColor); - point._model.borderWidth = point.custom && point.custom.borderWidth ? point.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().pointBorderWidth, index, this.chart.options.elements.point.borderWidth); - } - }); -}; -},{}],21:[function(require,module,exports){ -/*global window: false */ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.animation = { - duration: 1000, - easing: "easeOutQuart", - onProgress: helpers.noop, - onComplete: helpers.noop - }; - - Chart.Animation = Chart.Element.extend({ - currentStep: null, // the current animation step - numSteps: 60, // default number of steps - easing: "", // the easing to use for this animation - render: null, // render function used by the animation service - - onAnimationProgress: null, // user specified callback to fire on each step of the animation - onAnimationComplete: null // user specified callback to fire when the animation finishes - }); - - Chart.animationService = { - frameDuration: 17, - animations: [], - dropFrames: 0, - request: null, - addAnimation: function(chartInstance, animationObject, duration, lazy) { - - if (!lazy) { - chartInstance.animating = true; - } - - for (var index = 0; index < this.animations.length; ++index) { - if (this.animations[index].chartInstance === chartInstance) { - // replacing an in progress animation - this.animations[index].animationObject = animationObject; - return; - } - } - - this.animations.push({ - chartInstance: chartInstance, - animationObject: animationObject - }); - - // If there are no animations queued, manually kickstart a digest, for lack of a better word - if (this.animations.length === 1) { - this.requestAnimationFrame(); - } - }, - // Cancel the animation for a given chart instance - cancelAnimation: function(chartInstance) { - var index = helpers.findIndex(this.animations, function(animationWrapper) { - return animationWrapper.chartInstance === chartInstance; - }); - - if (index !== -1) { - this.animations.splice(index, 1); - chartInstance.animating = false; - } - }, - requestAnimationFrame: function() { - var me = this; - if (me.request === null) { - // Skip animation frame requests until the active one is executed. - // This can happen when processing mouse events, e.g. 'mousemove' - // and 'mouseout' events will trigger multiple renders. - me.request = helpers.requestAnimFrame.call(window, function() { - me.request = null; - me.startDigest(); - }); - } - }, - startDigest: function() { - - var startTime = Date.now(); - var framesToDrop = 0; - - if (this.dropFrames > 1) { - framesToDrop = Math.floor(this.dropFrames); - this.dropFrames = this.dropFrames % 1; - } - - var i = 0; - while (i < this.animations.length) { - if (this.animations[i].animationObject.currentStep === null) { - this.animations[i].animationObject.currentStep = 0; - } - - this.animations[i].animationObject.currentStep += 1 + framesToDrop; - - if (this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps) { - this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps; - } - - this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject); - if (this.animations[i].animationObject.onAnimationProgress && this.animations[i].animationObject.onAnimationProgress.call) { - this.animations[i].animationObject.onAnimationProgress.call(this.animations[i].chartInstance, this.animations[i]); - } - - if (this.animations[i].animationObject.currentStep === this.animations[i].animationObject.numSteps) { - if (this.animations[i].animationObject.onAnimationComplete && this.animations[i].animationObject.onAnimationComplete.call) { - this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance, this.animations[i]); - } - - // executed the last frame. Remove the animation. - this.animations[i].chartInstance.animating = false; - - this.animations.splice(i, 1); - } else { - ++i; - } - } - - var endTime = Date.now(); - var dropFrames = (endTime - startTime) / this.frameDuration; - - this.dropFrames += dropFrames; - - // Do we have more stuff to animate? - if (this.animations.length > 0) { - this.requestAnimationFrame(); - } - } - }; -}; -},{}],22:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - //Create a dictionary of chart types, to allow for extension of existing types - Chart.types = {}; - - //Store a reference to each instance - allowing us to globally resize chart instances on window resize. - //Destroy method on the chart will remove the instance of the chart from this reference. - Chart.instances = {}; - - // Controllers available for dataset visualization eg. bar, line, slice, etc. - Chart.controllers = {}; - - // The main controller of a chart - Chart.Controller = function(instance) { - - this.chart = instance; - this.config = instance.config; - this.options = this.config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[this.config.type], this.config.options || {}); - this.id = helpers.uid(); - - Object.defineProperty(this, 'data', { - get: function() { - return this.config.data; - } - }); - - //Add the chart instance to the global namespace - Chart.instances[this.id] = this; - - if (this.options.responsive) { - // Silent resize before chart draws - this.resize(true); - } - - this.initialize(); - - return this; - }; - - helpers.extend(Chart.Controller.prototype, { - - initialize: function initialize() { - // Before init plugin notification - Chart.pluginService.notifyPlugins('beforeInit', [this]); - - this.bindEvents(); - - // Make sure controllers are built first so that each dataset is bound to an axis before the scales - // are built - this.ensureScalesHaveIDs(); - this.buildOrUpdateControllers(); - this.buildScales(); - this.buildSurroundingItems(); - this.updateLayout(); - this.resetElements(); - this.initToolTip(); - this.update(); - - // After init plugin notification - Chart.pluginService.notifyPlugins('afterInit', [this]); - - return this; - }, - - clear: function clear() { - helpers.clear(this.chart); - return this; - }, - - stop: function stop() { - // Stops any current animation loop occuring - Chart.animationService.cancelAnimation(this); - return this; - }, - - resize: function resize(silent) { - var canvas = this.chart.canvas; - var newWidth = helpers.getMaximumWidth(this.chart.canvas); - var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0) ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas); - - var sizeChanged = this.chart.width !== newWidth || this.chart.height !== newHeight; - - if (!sizeChanged) - return this; - - canvas.width = this.chart.width = newWidth; - canvas.height = this.chart.height = newHeight; - - helpers.retinaScale(this.chart); - - if (!silent) { - this.stop(); - this.update(this.options.responsiveAnimationDuration); - } - - return this; - }, - ensureScalesHaveIDs: function ensureScalesHaveIDs() { - var defaultXAxisID = 'x-axis-'; - var defaultYAxisID = 'y-axis-'; - - if (this.options.scales) { - if (this.options.scales.xAxes && this.options.scales.xAxes.length) { - helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) { - xAxisOptions.id = xAxisOptions.id || (defaultXAxisID + index); - }); - } - - if (this.options.scales.yAxes && this.options.scales.yAxes.length) { - // Build the y axes - helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) { - yAxisOptions.id = yAxisOptions.id || (defaultYAxisID + index); - }); - } - } - }, - buildScales: function buildScales() { - // Map of scale ID to scale object so we can lookup later - this.scales = {}; - - // Build the x axes - if (this.options.scales) { - if (this.options.scales.xAxes && this.options.scales.xAxes.length) { - helpers.each(this.options.scales.xAxes, function(xAxisOptions, index) { - var xType = helpers.getValueOrDefault(xAxisOptions.type, 'category'); - var ScaleClass = Chart.scaleService.getScaleConstructor(xType); - if (ScaleClass) { - var scale = new ScaleClass({ - ctx: this.chart.ctx, - options: xAxisOptions, - chart: this, - id: xAxisOptions.id - }); - - this.scales[scale.id] = scale; - } - }, this); - } - - if (this.options.scales.yAxes && this.options.scales.yAxes.length) { - // Build the y axes - helpers.each(this.options.scales.yAxes, function(yAxisOptions, index) { - var yType = helpers.getValueOrDefault(yAxisOptions.type, 'linear'); - var ScaleClass = Chart.scaleService.getScaleConstructor(yType); - if (ScaleClass) { - var scale = new ScaleClass({ - ctx: this.chart.ctx, - options: yAxisOptions, - chart: this, - id: yAxisOptions.id - }); - - this.scales[scale.id] = scale; - } - }, this); - } - } - if (this.options.scale) { - // Build radial axes - var ScaleClass = Chart.scaleService.getScaleConstructor(this.options.scale.type); - if (ScaleClass) { - var scale = new ScaleClass({ - ctx: this.chart.ctx, - options: this.options.scale, - chart: this - }); - - this.scale = scale; - - this.scales.radialScale = scale; - } - } - - Chart.scaleService.addScalesToLayout(this); - }, - - buildSurroundingItems: function() { - if (this.options.title) { - this.titleBlock = new Chart.Title({ - ctx: this.chart.ctx, - options: this.options.title, - chart: this - }); - - Chart.layoutService.addBox(this, this.titleBlock); - } - - if (this.options.legend) { - this.legend = new Chart.Legend({ - ctx: this.chart.ctx, - options: this.options.legend, - chart: this - }); - - Chart.layoutService.addBox(this, this.legend); - } - }, - - updateLayout: function() { - Chart.layoutService.update(this, this.chart.width, this.chart.height); - }, - - buildOrUpdateControllers: function buildOrUpdateControllers() { - var types = []; - var newControllers = []; - - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - if (!dataset.type) { - dataset.type = this.config.type; - } - - var type = dataset.type; - types.push(type); - - if (dataset.controller) { - dataset.controller.updateIndex(datasetIndex); - } else { - dataset.controller = new Chart.controllers[type](this, datasetIndex); - newControllers.push(dataset.controller); - } - }, this); - - if (types.length > 1) { - for (var i = 1; i < types.length; i++) { - if (types[i] !== types[i - 1]) { - this.isCombo = true; - break; - } - } - } - - return newControllers; - }, - - resetElements: function resetElements() { - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - dataset.controller.reset(); - }); - }, - - update: function update(animationDuration, lazy) { - Chart.pluginService.notifyPlugins('beforeUpdate', [this]); - - // In case the entire data object changed - this.tooltip._data = this.data; - - // Make sure dataset controllers are updated and new controllers are reset - var newControllers = this.buildOrUpdateControllers(); - - // Make sure all dataset controllers have correct meta data counts - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - dataset.controller.buildOrUpdateElements(); - }); - - Chart.layoutService.update(this, this.chart.width, this.chart.height); - - // Can only reset the new controllers after the scales have been updated - helpers.each(newControllers, function(controller) { - controller.reset(); - }); - - // This will loop through any data and do the appropriate element update for the type - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - dataset.controller.update(); - }); - this.render(animationDuration, lazy); - - Chart.pluginService.notifyPlugins('afterUpdate', [this]); - }, - - render: function render(duration, lazy) { - Chart.pluginService.notifyPlugins('beforeRender', [this]); - - if (this.options.animation && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && this.options.animation.duration !== 0))) { - var animation = new Chart.Animation(); - animation.numSteps = (duration || this.options.animation.duration) / 16.66; //60 fps - animation.easing = this.options.animation.easing; - - // render function - animation.render = function(chartInstance, animationObject) { - var easingFunction = helpers.easingEffects[animationObject.easing]; - var stepDecimal = animationObject.currentStep / animationObject.numSteps; - var easeDecimal = easingFunction(stepDecimal); - - chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); - }; - - // user events - animation.onAnimationProgress = this.options.animation.onProgress; - animation.onAnimationComplete = this.options.animation.onComplete; - - Chart.animationService.addAnimation(this, animation, duration, lazy); - } else { - this.draw(); - if (this.options.animation && this.options.animation.onComplete && this.options.animation.onComplete.call) { - this.options.animation.onComplete.call(this); - } - } - return this; - }, - - draw: function(ease) { - var easingDecimal = ease || 1; - this.clear(); - - Chart.pluginService.notifyPlugins('beforeDraw', [this, easingDecimal]); - - // Draw all the scales - helpers.each(this.boxes, function(box) { - box.draw(this.chartArea); - }, this); - if (this.scale) { - this.scale.draw(); - } - - // Clip out the chart area so that anything outside does not draw. This is necessary for zoom and pan to function - this.chart.ctx.save(); - this.chart.ctx.beginPath(); - this.chart.ctx.rect(this.chartArea.left, this.chartArea.top, this.chartArea.right - this.chartArea.left, this.chartArea.bottom - this.chartArea.top); - this.chart.ctx.clip(); - - // Draw each dataset via its respective controller (reversed to support proper line stacking) - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - if (helpers.isDatasetVisible(dataset)) { - dataset.controller.draw(ease); - } - }, null, true); - - // Restore from the clipping operation - this.chart.ctx.restore(); - - // Finally draw the tooltip - this.tooltip.transition(easingDecimal).draw(); - - Chart.pluginService.notifyPlugins('afterDraw', [this, easingDecimal]); - }, - - // Get the single element that was clicked on - // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw - getElementAtEvent: function(e) { - - var eventPosition = helpers.getRelativePosition(e, this.chart); - var elementsArray = []; - - helpers.each(this.data.datasets, function(dataset, datasetIndex) { - if (helpers.isDatasetVisible(dataset)) { - helpers.each(dataset.metaData, function(element, index) { - if (element.inRange(eventPosition.x, eventPosition.y)) { - elementsArray.push(element); - return elementsArray; - } - }); - } - }); - - return elementsArray; - }, - - getElementsAtEvent: function(e) { - var eventPosition = helpers.getRelativePosition(e, this.chart); - var elementsArray = []; - - var found = (function() { - if (this.data.datasets) { - for (var i = 0; i < this.data.datasets.length; i++) { - if (helpers.isDatasetVisible(this.data.datasets[i])) { - for (var j = 0; j < this.data.datasets[i].metaData.length; j++) { - if (this.data.datasets[i].metaData[j].inRange(eventPosition.x, eventPosition.y)) { - return this.data.datasets[i].metaData[j]; - } - } - } - } - } - }).call(this); - - if (!found) { - return elementsArray; - } - - helpers.each(this.data.datasets, function(dataset, dsIndex) { - if (helpers.isDatasetVisible(dataset)) { - elementsArray.push(dataset.metaData[found._index]); - } - }); - - return elementsArray; - }, - - getDatasetAtEvent: function(e) { - var elementsArray = this.getElementAtEvent(e); - - if (elementsArray.length > 0) { - elementsArray = this.data.datasets[elementsArray[0]._datasetIndex].metaData; - } - - return elementsArray; - }, - - generateLegend: function generateLegend() { - return this.options.legendCallback(this); - }, - - destroy: function destroy() { - this.clear(); - helpers.unbindEvents(this, this.events); - helpers.removeResizeListener(this.chart.canvas.parentNode); - - // Reset canvas height/width attributes - var canvas = this.chart.canvas; - canvas.width = this.chart.width; - canvas.height = this.chart.height; - - // if we scaled the canvas in response to a devicePixelRatio !== 1, we need to undo that transform here - if (this.chart.originalDevicePixelRatio !== undefined) { - this.chart.ctx.scale(1 / this.chart.originalDevicePixelRatio, 1 / this.chart.originalDevicePixelRatio); - } - - // Reset to the old style since it may have been changed by the device pixel ratio changes - canvas.style.width = this.chart.originalCanvasStyleWidth; - canvas.style.height = this.chart.originalCanvasStyleHeight; - - Chart.pluginService.notifyPlugins('destroy', [this]); - - delete Chart.instances[this.id]; - }, - - toBase64Image: function toBase64Image() { - return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments); - }, - - initToolTip: function initToolTip() { - this.tooltip = new Chart.Tooltip({ - _chart: this.chart, - _chartInstance: this, - _data: this.data, - _options: this.options - }, this); - }, - - bindEvents: function bindEvents() { - helpers.bindEvents(this, this.options.events, function(evt) { - this.eventHandler(evt); - }); - }, - eventHandler: function eventHandler(e) { - this.lastActive = this.lastActive || []; - this.lastTooltipActive = this.lastTooltipActive || []; - - // Find Active Elements for hover and tooltips - if (e.type === 'mouseout') { - this.active = []; - this.tooltipActive = []; - } else { - - var _this = this; - var getItemsForMode = function(mode) { - switch (mode) { - case 'single': - return _this.getElementAtEvent(e); - case 'label': - return _this.getElementsAtEvent(e); - case 'dataset': - return _this.getDatasetAtEvent(e); - default: - return e; - } - }; - - this.active = getItemsForMode(this.options.hover.mode); - this.tooltipActive = getItemsForMode(this.options.tooltips.mode); - } - - // On Hover hook - if (this.options.hover.onHover) { - this.options.hover.onHover.call(this, this.active); - } - - if (e.type === 'mouseup' || e.type === 'click') { - if (this.options.onClick) { - this.options.onClick.call(this, e, this.active); - } - - if (this.legend && this.legend.handleEvent) { - this.legend.handleEvent(e); - } - } - - var dataset; - var index; - - // Remove styling for last active (even if it may still be active) - if (this.lastActive.length) { - switch (this.options.hover.mode) { - case 'single': - this.data.datasets[this.lastActive[0]._datasetIndex].controller.removeHoverStyle(this.lastActive[0], this.lastActive[0]._datasetIndex, this.lastActive[0]._index); - break; - case 'label': - case 'dataset': - for (var i = 0; i < this.lastActive.length; i++) { - if (this.lastActive[i]) - this.data.datasets[this.lastActive[i]._datasetIndex].controller.removeHoverStyle(this.lastActive[i], this.lastActive[i]._datasetIndex, this.lastActive[i]._index); - } - break; - default: - // Don't change anything - } - } - - // Built in hover styling - if (this.active.length && this.options.hover.mode) { - switch (this.options.hover.mode) { - case 'single': - this.data.datasets[this.active[0]._datasetIndex].controller.setHoverStyle(this.active[0]); - break; - case 'label': - case 'dataset': - for (var j = 0; j < this.active.length; j++) { - if (this.active[j]) - this.data.datasets[this.active[j]._datasetIndex].controller.setHoverStyle(this.active[j]); - } - break; - default: - // Don't change anything - } - } - - - // Built in Tooltips - if (this.options.tooltips.enabled || this.options.tooltips.custom) { - - // The usual updates - this.tooltip.initialize(); - this.tooltip._active = this.tooltipActive; - this.tooltip.update(); - } - - // Hover animations - this.tooltip.pivot(); - - if (!this.animating) { - var changed; - - helpers.each(this.active, function(element, index) { - if (element !== this.lastActive[index]) { - changed = true; - } - }, this); - - helpers.each(this.tooltipActive, function(element, index) { - if (element !== this.lastTooltipActive[index]) { - changed = true; - } - }, this); - - // If entering, leaving, or changing elements, animate the change via pivot - if ((this.lastActive.length !== this.active.length) || - (this.lastTooltipActive.length !== this.tooltipActive.length) || - changed) { - - this.stop(); - - if (this.options.tooltips.enabled || this.options.tooltips.custom) { - this.tooltip.update(true); - } - - // We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more - // memory than necessary. - this.render(this.options.hover.animationDuration, true); - } - } - - // Remember Last Actives - this.lastActive = this.active; - this.lastTooltipActive = this.tooltipActive; - return this; - } - }); -}; - -},{}],23:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - // Base class for all dataset controllers (line, bar, etc) - Chart.DatasetController = function(chart, datasetIndex) { - this.initialize.call(this, chart, datasetIndex); - }; - - helpers.extend(Chart.DatasetController.prototype, { - initialize: function(chart, datasetIndex) { - this.chart = chart; - this.index = datasetIndex; - this.linkScales(); - this.addElements(); - }, - updateIndex: function(datasetIndex) { - this.index = datasetIndex; - }, - - linkScales: function() { - if (!this.getDataset().xAxisID) { - this.getDataset().xAxisID = this.chart.options.scales.xAxes[0].id; - } - - if (!this.getDataset().yAxisID) { - this.getDataset().yAxisID = this.chart.options.scales.yAxes[0].id; - } - }, - - getDataset: function() { - return this.chart.data.datasets[this.index]; - }, - - getScaleForId: function(scaleID) { - return this.chart.scales[scaleID]; - }, - - reset: function() { - this.update(true); - }, - - buildOrUpdateElements: function buildOrUpdateElements() { - // Handle the number of data points changing - var numData = this.getDataset().data.length; - var numMetaData = this.getDataset().metaData.length; - - // Make sure that we handle number of datapoints changing - if (numData < numMetaData) { - // Remove excess bars for data points that have been removed - this.getDataset().metaData.splice(numData, numMetaData - numData); - } else if (numData > numMetaData) { - // Add new elements - for (var index = numMetaData; index < numData; ++index) { - this.addElementAndReset(index); - } - } - }, - - // Controllers should implement the following - addElements: helpers.noop, - addElementAndReset: helpers.noop, - draw: helpers.noop, - removeHoverStyle: helpers.noop, - setHoverStyle: helpers.noop, - update: helpers.noop - }); - - Chart.DatasetController.extend = helpers.inherits; - -}; -},{}],24:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.elements = {}; - - Chart.Element = function(configuration) { - helpers.extend(this, configuration); - this.initialize.apply(this, arguments); - }; - helpers.extend(Chart.Element.prototype, { - initialize: function() {}, - pivot: function() { - if (!this._view) { - this._view = helpers.clone(this._model); - } - this._start = helpers.clone(this._view); - return this; - }, - transition: function(ease) { - if (!this._view) { - this._view = helpers.clone(this._model); - } - - // No animation -> No Transition - if (ease === 1) { - this._view = this._model; - this._start = null; - return this; - } - - if (!this._start) { - this.pivot(); - } - - helpers.each(this._model, function(value, key) { - - if (key[0] === '_' || !this._model.hasOwnProperty(key)) { - // Only non-underscored properties - } - - // Init if doesn't exist - else if (!this._view.hasOwnProperty(key)) { - if (typeof value === 'number' && !isNaN(this._view[key])) { - this._view[key] = value * ease; - } else { - this._view[key] = value; - } - } - - // No unnecessary computations - else if (value === this._view[key]) { - // It's the same! Woohoo! - } - - // Color transitions if possible - else if (typeof value === 'string') { - try { - var color = helpers.color(this._start[key]).mix(helpers.color(this._model[key]), ease); - this._view[key] = color.rgbString(); - } catch (err) { - this._view[key] = value; - } - } - // Number transitions - else if (typeof value === 'number') { - var startVal = this._start[key] !== undefined && isNaN(this._start[key]) === false ? this._start[key] : 0; - this._view[key] = ((this._model[key] - startVal) * ease) + startVal; - } - // Everything else - else { - this._view[key] = value; - } - }, this); - - return this; - }, - tooltipPosition: function() { - return { - x: this._model.x, - y: this._model.y - }; - }, - hasValue: function() { - return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); - } - }); - - Chart.Element.extend = helpers.inherits; - -}; - -},{}],25:[function(require,module,exports){ -/*global window: false */ -/*global document: false */ -"use strict"; - -var color = require('chartjs-color'); - -module.exports = function(Chart) { - - //Global Chart helpers object for utility methods and classes - var helpers = Chart.helpers = {}; - - //-- Basic js utility methods - helpers.each = function(loopable, callback, self, reverse) { - // Check to see if null or undefined firstly. - var i, len; - if (helpers.isArray(loopable)) { - len = loopable.length; - if (reverse) { - for (i = len - 1; i >= 0; i--) { - callback.call(self, loopable[i], i); - } - } else { - for (i = 0; i < len; i++) { - callback.call(self, loopable[i], i); - } - } - } else if (typeof loopable === 'object') { - var keys = Object.keys(loopable); - len = keys.length; - for (i = 0; i < len; i++) { - callback.call(self, loopable[keys[i]], keys[i]); - } - } - }; - helpers.clone = function(obj) { - var objClone = {}; - helpers.each(obj, function(value, key) { - if (obj.hasOwnProperty(key)) { - if (helpers.isArray(value)) { - objClone[key] = value.slice(0); - } else if (typeof value === 'object' && value !== null) { - objClone[key] = helpers.clone(value); - } else { - objClone[key] = value; - } - } - }); - return objClone; - }; - helpers.extend = function(base) { - var len = arguments.length; - var additionalArgs = []; - for (var i = 1; i < len; i++) { - additionalArgs.push(arguments[i]); - } - helpers.each(additionalArgs, function(extensionObject) { - helpers.each(extensionObject, function(value, key) { - if (extensionObject.hasOwnProperty(key)) { - base[key] = value; - } - }); - }); - return base; - }; - // Need a special merge function to chart configs since they are now grouped - helpers.configMerge = function(_base) { - var base = helpers.clone(_base); - helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) { - helpers.each(extension, function(value, key) { - if (extension.hasOwnProperty(key)) { - if (key === 'scales') { - // Scale config merging is complex. Add out own function here for that - base[key] = helpers.scaleMerge(base.hasOwnProperty(key) ? base[key] : {}, value); - - } else if (key === 'scale') { - // Used in polar area & radar charts since there is only one scale - base[key] = helpers.configMerge(base.hasOwnProperty(key) ? base[key] : {}, Chart.scaleService.getScaleDefaults(value.type), value); - } else if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) { - // In this case we have an array of objects replacing another array. Rather than doing a strict replace, - // merge. This allows easy scale option merging - var baseArray = base[key]; - - helpers.each(value, function(valueObj, index) { - - if (index < baseArray.length) { - if (typeof baseArray[index] === 'object' && baseArray[index] !== null && typeof valueObj === 'object' && valueObj !== null) { - // Two objects are coming together. Do a merge of them. - baseArray[index] = helpers.configMerge(baseArray[index], valueObj); - } else { - // Just overwrite in this case since there is nothing to merge - baseArray[index] = valueObj; - } - } else { - baseArray.push(valueObj); // nothing to merge - } - }); - - } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { - // If we are overwriting an object with an object, do a merge of the properties. - base[key] = helpers.configMerge(base[key], value); - - } else { - // can just overwrite the value in this case - base[key] = value; - } - } - }); - }); - - return base; - }; - helpers.extendDeep = function(_base) { - return _extendDeep.apply(this, arguments); - - function _extendDeep(dst) { - helpers.each(arguments, function(obj) { - if (obj !== dst) { - helpers.each(obj, function(value, key) { - if (dst[key] && dst[key].constructor && dst[key].constructor === Object) { - _extendDeep(dst[key], value); - } else { - dst[key] = value; - } - }); - } - }); - return dst; - } - }; - helpers.scaleMerge = function(_base, extension) { - var base = helpers.clone(_base); - - helpers.each(extension, function(value, key) { - if (extension.hasOwnProperty(key)) { - if (key === 'xAxes' || key === 'yAxes') { - // These properties are arrays of items - if (base.hasOwnProperty(key)) { - helpers.each(value, function(valueObj, index) { - var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); - var axisDefaults = Chart.scaleService.getScaleDefaults(axisType); - if (index >= base[key].length || !base[key][index].type) { - base[key].push(helpers.configMerge(axisDefaults, valueObj)); - } else if (valueObj.type && valueObj.type !== base[key][index].type) { - // Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults - base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj); - } else { - // Type is the same - base[key][index] = helpers.configMerge(base[key][index], valueObj); - } - }); - } else { - base[key] = []; - helpers.each(value, function(valueObj) { - var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); - base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj)); - }); - } - } else if (base.hasOwnProperty(key) && typeof base[key] === "object" && base[key] !== null && typeof value === "object") { - // If we are overwriting an object with an object, do a merge of the properties. - base[key] = helpers.configMerge(base[key], value); - - } else { - // can just overwrite the value in this case - base[key] = value; - } - } - }); - - return base; - }; - helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) { - if (value === undefined || value === null) { - return defaultValue; - } - - if (helpers.isArray(value)) { - return index < value.length ? value[index] : defaultValue; - } - - return value; - }; - helpers.getValueOrDefault = function(value, defaultValue) { - return value === undefined ? defaultValue : value; - }; - helpers.indexOf = function(arrayToSearch, item) { - if (Array.prototype.indexOf) { - return arrayToSearch.indexOf(item); - } else { - for (var i = 0; i < arrayToSearch.length; i++) { - if (arrayToSearch[i] === item) - return i; - } - return -1; - } - }; - helpers.where = function(collection, filterCallback) { - var filtered = []; - - helpers.each(collection, function(item) { - if (filterCallback(item)) { - filtered.push(item); - } - }); - - return filtered; - }; - helpers.findIndex = function(arrayToSearch, callback, thisArg) { - var index = -1; - if (Array.prototype.findIndex) { - index = arrayToSearch.findIndex(callback, thisArg); - } else { - for (var i = 0; i < arrayToSearch.length; ++i) { - thisArg = thisArg !== undefined ? thisArg : arrayToSearch; - - if (callback.call(thisArg, arrayToSearch[i], i, arrayToSearch)) { - index = i; - break; - } - } - } - - return index; - }; - helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to start of the array - if (startIndex === undefined || startIndex === null) { - startIndex = -1; - } - for (var i = startIndex + 1; i < arrayToSearch.length; i++) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { - // Default to end of the array - if (startIndex === undefined || startIndex === null) { - startIndex = arrayToSearch.length; - } - for (var i = startIndex - 1; i >= 0; i--) { - var currentItem = arrayToSearch[i]; - if (filterCallback(currentItem)) { - return currentItem; - } - } - }; - helpers.inherits = function(extensions) { - //Basic javascript inheritance based on the model created in Backbone.js - var parent = this; - var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function() { - return parent.apply(this, arguments); - }; - - var Surrogate = function() { - this.constructor = ChartElement; - }; - Surrogate.prototype = parent.prototype; - ChartElement.prototype = new Surrogate(); - - ChartElement.extend = helpers.inherits; - - if (extensions) { - helpers.extend(ChartElement.prototype, extensions); - } - - ChartElement.__super__ = parent.prototype; - - return ChartElement; - }; - helpers.noop = function() {}; - helpers.uid = (function() { - var id = 0; - return function() { - return "chart-" + id++; - }; - })(); - helpers.warn = function(str) { - //Method for warning of errors - if (console && typeof console.warn === "function") { - console.warn(str); - } - }; - //-- Math methods - helpers.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - helpers.almostEquals = function(x, y, epsilon) { - return Math.abs(x - y) < epsilon; - }; - helpers.max = function(array) { - return array.reduce(function(max, value) { - if (!isNaN(value)) { - return Math.max(max, value); - } else { - return max; - } - }, Number.NEGATIVE_INFINITY); - }; - helpers.min = function(array) { - return array.reduce(function(min, value) { - if (!isNaN(value)) { - return Math.min(min, value); - } else { - return min; - } - }, Number.POSITIVE_INFINITY); - }; - helpers.sign = function(x) { - if (Math.sign) { - return Math.sign(x); - } else { - x = +x; // convert to a number - if (x === 0 || isNaN(x)) { - return x; - } - return x > 0 ? 1 : -1; - } - }; - helpers.log10 = function(x) { - if (Math.log10) { - return Math.log10(x); - } else { - return Math.log(x) / Math.LN10; - } - }; - helpers.toRadians = function(degrees) { - return degrees * (Math.PI / 180); - }; - helpers.toDegrees = function(radians) { - return radians * (180 / Math.PI); - }; - // Gets the angle from vertical upright to the point about a centre. - helpers.getAngleFromPoint = function(centrePoint, anglePoint) { - var distanceFromXCenter = anglePoint.x - centrePoint.x, - distanceFromYCenter = anglePoint.y - centrePoint.y, - radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); - - var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); - - if (angle < (-0.5 * Math.PI)) { - angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] - } - - return { - angle: angle, - distance: radialDistanceFromCenter - }; - }; - helpers.aliasPixel = function(pixelWidth) { - return (pixelWidth % 2 === 0) ? 0 : 0.5; - }; - helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { - //Props to Rob Spencer at scaled innovation for his post on splining between points - //http://scaledinnovation.com/analytics/splines/aboutSplines.html - - // This function must also respect "skipped" points - - var previous = firstPoint.skip ? middlePoint : firstPoint, - current = middlePoint, - next = afterPoint.skip ? middlePoint : afterPoint; - - var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); - var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); - - var s01 = d01 / (d01 + d12); - var s12 = d12 / (d01 + d12); - - // If all points are the same, s01 & s02 will be inf - s01 = isNaN(s01) ? 0 : s01; - s12 = isNaN(s12) ? 0 : s12; - - var fa = t * s01; // scaling factor for triangle Ta - var fb = t * s12; - - return { - previous: { - x: current.x - fa * (next.x - previous.x), - y: current.y - fa * (next.y - previous.y) - }, - next: { - x: current.x + fb * (next.x - previous.x), - y: current.y + fb * (next.y - previous.y) - } - }; - }; - helpers.nextItem = function(collection, index, loop) { - if (loop) { - return index >= collection.length - 1 ? collection[0] : collection[index + 1]; - } - - return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; - }; - helpers.previousItem = function(collection, index, loop) { - if (loop) { - return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; - } - return index <= 0 ? collection[0] : collection[index - 1]; - }; - // Implementation of the nice number algorithm used in determining where axis labels will go - helpers.niceNum = function(range, round) { - var exponent = Math.floor(helpers.log10(range)); - var fraction = range / Math.pow(10, exponent); - var niceFraction; - - if (round) { - if (fraction < 1.5) { - niceFraction = 1; - } else if (fraction < 3) { - niceFraction = 2; - } else if (fraction < 7) { - niceFraction = 5; - } else { - niceFraction = 10; - } - } else { - if (fraction <= 1.0) { - niceFraction = 1; - } else if (fraction <= 2) { - niceFraction = 2; - } else if (fraction <= 5) { - niceFraction = 5; - } else { - niceFraction = 10; - } - } - - return niceFraction * Math.pow(10, exponent); - }; - //Easing functions adapted from Robert Penner's easing equations - //http://www.robertpenner.com/easing/ - var easingEffects = helpers.easingEffects = { - linear: function(t) { - return t; - }, - easeInQuad: function(t) { - return t * t; - }, - easeOutQuad: function(t) { - return -1 * t * (t - 2); - }, - easeInOutQuad: function(t) { - if ((t /= 1 / 2) < 1) { - return 1 / 2 * t * t; - } - return -1 / 2 * ((--t) * (t - 2) - 1); - }, - easeInCubic: function(t) { - return t * t * t; - }, - easeOutCubic: function(t) { - return 1 * ((t = t / 1 - 1) * t * t + 1); - }, - easeInOutCubic: function(t) { - if ((t /= 1 / 2) < 1) { - return 1 / 2 * t * t * t; - } - return 1 / 2 * ((t -= 2) * t * t + 2); - }, - easeInQuart: function(t) { - return t * t * t * t; - }, - easeOutQuart: function(t) { - return -1 * ((t = t / 1 - 1) * t * t * t - 1); - }, - easeInOutQuart: function(t) { - if ((t /= 1 / 2) < 1) { - return 1 / 2 * t * t * t * t; - } - return -1 / 2 * ((t -= 2) * t * t * t - 2); - }, - easeInQuint: function(t) { - return 1 * (t /= 1) * t * t * t * t; - }, - easeOutQuint: function(t) { - return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); - }, - easeInOutQuint: function(t) { - if ((t /= 1 / 2) < 1) { - return 1 / 2 * t * t * t * t * t; - } - return 1 / 2 * ((t -= 2) * t * t * t * t + 2); - }, - easeInSine: function(t) { - return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; - }, - easeOutSine: function(t) { - return 1 * Math.sin(t / 1 * (Math.PI / 2)); - }, - easeInOutSine: function(t) { - return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); - }, - easeInExpo: function(t) { - return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); - }, - easeOutExpo: function(t) { - return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); - }, - easeInOutExpo: function(t) { - if (t === 0) { - return 0; - } - if (t === 1) { - return 1; - } - if ((t /= 1 / 2) < 1) { - return 1 / 2 * Math.pow(2, 10 * (t - 1)); - } - return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); - }, - easeInCirc: function(t) { - if (t >= 1) { - return t; - } - return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); - }, - easeOutCirc: function(t) { - return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); - }, - easeInOutCirc: function(t) { - if ((t /= 1 / 2) < 1) { - return -1 / 2 * (Math.sqrt(1 - t * t) - 1); - } - return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); - }, - easeInElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if ((t /= 1) === 1) { - return 1; - } - if (!p) { - p = 1 * 0.3; - } - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - }, - easeOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if ((t /= 1) === 1) { - return 1; - } - if (!p) { - p = 1 * 0.3; - } - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; - }, - easeInOutElastic: function(t) { - var s = 1.70158; - var p = 0; - var a = 1; - if (t === 0) { - return 0; - } - if ((t /= 1 / 2) === 2) { - return 1; - } - if (!p) { - p = 1 * (0.3 * 1.5); - } - if (a < Math.abs(1)) { - a = 1; - s = p / 4; - } else { - s = p / (2 * Math.PI) * Math.asin(1 / a); - } - if (t < 1) { - return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); - } - return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; - }, - easeInBack: function(t) { - var s = 1.70158; - return 1 * (t /= 1) * t * ((s + 1) * t - s); - }, - easeOutBack: function(t) { - var s = 1.70158; - return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); - }, - easeInOutBack: function(t) { - var s = 1.70158; - if ((t /= 1 / 2) < 1) { - return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); - } - return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); - }, - easeInBounce: function(t) { - return 1 - easingEffects.easeOutBounce(1 - t); - }, - easeOutBounce: function(t) { - if ((t /= 1) < (1 / 2.75)) { - return 1 * (7.5625 * t * t); - } else if (t < (2 / 2.75)) { - return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); - } else if (t < (2.5 / 2.75)) { - return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); - } else { - return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); - } - }, - easeInOutBounce: function(t) { - if (t < 1 / 2) { - return easingEffects.easeInBounce(t * 2) * 0.5; - } - return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; - } - }; - //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ - helpers.requestAnimFrame = (function() { - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(callback) { - return window.setTimeout(callback, 1000 / 60); - }; - })(); - helpers.cancelAnimFrame = (function() { - return window.cancelAnimationFrame || - window.webkitCancelAnimationFrame || - window.mozCancelAnimationFrame || - window.oCancelAnimationFrame || - window.msCancelAnimationFrame || - function(callback) { - return window.clearTimeout(callback, 1000 / 60); - }; - })(); - //-- DOM methods - helpers.getRelativePosition = function(evt, chart) { - var mouseX, mouseY; - var e = evt.originalEvent || evt, - canvas = evt.currentTarget || evt.srcElement, - boundingRect = canvas.getBoundingClientRect(); - - if (e.touches && e.touches.length > 0) { - mouseX = e.touches[0].clientX; - mouseY = e.touches[0].clientY; - - } else { - mouseX = e.clientX; - mouseY = e.clientY; - } - - // Scale mouse coordinates into canvas coordinates - // by following the pattern laid out by 'jerryj' in the comments of - // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ - var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); - var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); - var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); - var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); - var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; - var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; - - // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However - // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here - mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); - mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); - - return { - x: mouseX, - y: mouseY - }; - - }; - helpers.addEvent = function(node, eventType, method) { - if (node.addEventListener) { - node.addEventListener(eventType, method); - } else if (node.attachEvent) { - node.attachEvent("on" + eventType, method); - } else { - node["on" + eventType] = method; - } - }; - helpers.removeEvent = function(node, eventType, handler) { - if (node.removeEventListener) { - node.removeEventListener(eventType, handler, false); - } else if (node.detachEvent) { - node.detachEvent("on" + eventType, handler); - } else { - node["on" + eventType] = helpers.noop; - } - }; - helpers.bindEvents = function(chartInstance, arrayOfEvents, handler) { - // Create the events object if it's not already present - if (!chartInstance.events) - chartInstance.events = {}; - - helpers.each(arrayOfEvents, function(eventName) { - chartInstance.events[eventName] = function() { - handler.apply(chartInstance, arguments); - }; - helpers.addEvent(chartInstance.chart.canvas, eventName, chartInstance.events[eventName]); - }); - }; - helpers.unbindEvents = function(chartInstance, arrayOfEvents) { - helpers.each(arrayOfEvents, function(handler, eventName) { - helpers.removeEvent(chartInstance.chart.canvas, eventName, handler); - }); - }; - - // Private helper function to convert max-width/max-height values that may be percentages into a number - function parseMaxStyle(styleValue, node, parentProperty) { - var valueInPixels; - if (typeof(styleValue) === 'string') { - valueInPixels = parseInt(styleValue, 10); - - if (styleValue.indexOf('%') != -1) { - // percentage * size in dimension - valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; - } - } else { - valueInPixels = styleValue; - } - - return valueInPixels; - } - - // Private helper to get a constraint dimension - // @param domNode : the node to check the constraint on - // @param maxStyle : the style that defines the maximum for the direction we are using (max-width / max-height) - // @param percentageProperty : property of parent to use when calculating width as a percentage - function getConstraintDimension(domNode, maxStyle, percentageProperty) { - var constrainedDimension; - var constrainedNode = document.defaultView.getComputedStyle(domNode)[maxStyle]; - var constrainedContainer = document.defaultView.getComputedStyle(domNode.parentNode)[maxStyle]; - var hasCNode = constrainedNode !== null && constrainedNode !== "none"; - var hasCContainer = constrainedContainer !== null && constrainedContainer !== "none"; - - if (hasCNode || hasCContainer) { - constrainedDimension = Math.min((hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : Number.POSITIVE_INFINITY), (hasCContainer ? parseMaxStyle(constrainedContainer, domNode.parentNode, percentageProperty) : Number.POSITIVE_INFINITY)); - } - return constrainedDimension; - } - // returns Number or undefined if no constraint - helpers.getConstraintWidth = function(domNode) { - return getConstraintDimension(domNode, 'max-width', 'clientWidth'); - }; - // returns Number or undefined if no constraint - helpers.getConstraintHeight = function(domNode) { - return getConstraintDimension(domNode, 'max-height', 'clientHeight'); - }; - helpers.getMaximumWidth = function(domNode) { - var container = domNode.parentNode; - var padding = parseInt(helpers.getStyle(container, 'padding-left')) + parseInt(helpers.getStyle(container, 'padding-right')); - - var w = container.clientWidth - padding; - var cw = helpers.getConstraintWidth(domNode); - if (cw !== undefined) { - w = Math.min(w, cw); - } - - return w; - }; - helpers.getMaximumHeight = function(domNode) { - var container = domNode.parentNode; - var padding = parseInt(helpers.getStyle(container, 'padding-top')) + parseInt(helpers.getStyle(container, 'padding-bottom')); - - var h = container.clientHeight - padding; - var ch = helpers.getConstraintHeight(domNode); - if (ch !== undefined) { - h = Math.min(h, ch); - } - - return h; - }; - helpers.getStyle = function(el, property) { - return el.currentStyle ? - el.currentStyle[property] : - document.defaultView.getComputedStyle(el, null).getPropertyValue(property); - }; - helpers.retinaScale = function(chart) { - var ctx = chart.ctx; - var width = chart.canvas.width; - var height = chart.canvas.height; - var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1; - - if (pixelRatio !== 1) { - ctx.canvas.height = height * pixelRatio; - ctx.canvas.width = width * pixelRatio; - ctx.scale(pixelRatio, pixelRatio); - - // Store the device pixel ratio so that we can go backwards in `destroy`. - // The devicePixelRatio changes with zoom, so there are no guarantees that it is the same - // when destroy is called - chart.originalDevicePixelRatio = chart.originalDevicePixelRatio || pixelRatio; - } - - ctx.canvas.style.width = width + 'px'; - ctx.canvas.style.height = height + 'px'; - }; - //-- Canvas methods - helpers.clear = function(chart) { - chart.ctx.clearRect(0, 0, chart.width, chart.height); - }; - helpers.fontString = function(pixelSize, fontStyle, fontFamily) { - return fontStyle + " " + pixelSize + "px " + fontFamily; - }; - helpers.longestText = function(ctx, font, arrayOfStrings, cache) { - cache = cache || {}; - cache.data = cache.data || {}; - cache.garbageCollect = cache.garbageCollect || []; - - if (cache.font !== font) { - cache.data = {}; - cache.garbageCollect = []; - cache.font = font; - } - - ctx.font = font; - var longest = 0; - helpers.each(arrayOfStrings, function(string) { - // Undefined strings should not be measured - if (string !== undefined && string !== null) { - var textWidth = cache.data[string]; - if (!textWidth) { - textWidth = cache.data[string] = ctx.measureText(string).width; - cache.garbageCollect.push(string); - } - - if (textWidth > longest) { - longest = textWidth; - } - } - }); - - var gcLen = cache.garbageCollect.length / 2; - if (gcLen > arrayOfStrings.length) { - for (var i = 0; i < gcLen; i++) { - delete cache.data[cache.garbageCollect[i]]; - } - cache.garbageCollect.splice(0, gcLen); - } - - return longest; - }; - helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) { - ctx.beginPath(); - ctx.moveTo(x + radius, y); - ctx.lineTo(x + width - radius, y); - ctx.quadraticCurveTo(x + width, y, x + width, y + radius); - ctx.lineTo(x + width, y + height - radius); - ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); - ctx.lineTo(x + radius, y + height); - ctx.quadraticCurveTo(x, y + height, x, y + height - radius); - ctx.lineTo(x, y + radius); - ctx.quadraticCurveTo(x, y, x + radius, y); - ctx.closePath(); - }; - helpers.color = function(c) { - if (!color) { - console.log('Color.js not found!'); - return c; - } - - /* global CanvasGradient */ - if (c instanceof CanvasGradient) { - return color(Chart.defaults.global.defaultColor); - } - - return color(c); - }; - helpers.addResizeListener = function(node, callback) { - // Hide an iframe before the node - var hiddenIframe = document.createElement('iframe'); - var hiddenIframeClass = 'chartjs-hidden-iframe'; - - if (hiddenIframe.classlist) { - // can use classlist - hiddenIframe.classlist.add(hiddenIframeClass); - } else { - hiddenIframe.setAttribute('class', hiddenIframeClass); - } - - // Set the style - hiddenIframe.style.width = '100%'; - hiddenIframe.style.display = 'block'; - hiddenIframe.style.border = 0; - hiddenIframe.style.height = 0; - hiddenIframe.style.margin = 0; - hiddenIframe.style.position = 'absolute'; - hiddenIframe.style.left = 0; - hiddenIframe.style.right = 0; - hiddenIframe.style.top = 0; - hiddenIframe.style.bottom = 0; - - // Insert the iframe so that contentWindow is available - node.insertBefore(hiddenIframe, node.firstChild); - - (hiddenIframe.contentWindow || hiddenIframe).onresize = function() { - if (callback) { - callback(); - } - }; - }; - helpers.removeResizeListener = function(node) { - var hiddenIframe = node.querySelector('.chartjs-hidden-iframe'); - - // Remove the resize detect iframe - if (hiddenIframe) { - hiddenIframe.parentNode.removeChild(hiddenIframe); - } - }; - helpers.isArray = function(obj) { - if (!Array.isArray) { - return Object.prototype.toString.call(obj) === '[object Array]'; - } - return Array.isArray(obj); - }; - helpers.pushAllIfDefined = function(element, array) { - if (typeof element === "undefined") { - return; - } - - if (helpers.isArray(element)) { - array.push.apply(array, element); - } else { - array.push(element); - } - }; - helpers.isDatasetVisible = function(dataset) { - return !dataset.hidden; - }; - helpers.callCallback = function(fn, args, _tArg) { - if (fn && typeof fn.call === 'function') { - fn.apply(_tArg, args); - } - }; - -}; - -},{"chartjs-color":2}],26:[function(require,module,exports){ -"use strict"; - -module.exports = function() { - - //Occupy the global variable of Chart, and create a simple base class - var Chart = function(context, config) { - this.config = config; - - // Support a jQuery'd canvas element - if (context.length && context[0].getContext) { - context = context[0]; - } - - // Support a canvas domnode - if (context.getContext) { - context = context.getContext("2d"); - } - - this.ctx = context; - this.canvas = context.canvas; - - // Figure out what the size of the chart will be. - // If the canvas has a specified width and height, we use those else - // we look to see if the canvas node has a CSS width and height. - // If there is still no height, fill the parent container - this.width = context.canvas.width || parseInt(Chart.helpers.getStyle(context.canvas, 'width')) || Chart.helpers.getMaximumWidth(context.canvas); - this.height = context.canvas.height || parseInt(Chart.helpers.getStyle(context.canvas, 'height')) || Chart.helpers.getMaximumHeight(context.canvas); - - this.aspectRatio = this.width / this.height; - - if (isNaN(this.aspectRatio) || isFinite(this.aspectRatio) === false) { - // If the canvas has no size, try and figure out what the aspect ratio will be. - // Some charts prefer square canvases (pie, radar, etc). If that is specified, use that - // else use the canvas default ratio of 2 - this.aspectRatio = config.aspectRatio !== undefined ? config.aspectRatio : 2; - } - - // Store the original style of the element so we can set it back - this.originalCanvasStyleWidth = context.canvas.style.width; - this.originalCanvasStyleHeight = context.canvas.style.height; - - // High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. - Chart.helpers.retinaScale(this); - - if (config) { - this.controller = new Chart.Controller(this); - } - - // Always bind this so that if the responsive state changes we still work - var _this = this; - Chart.helpers.addResizeListener(context.canvas.parentNode, function() { - if (_this.controller && _this.controller.config.options.responsive) { - _this.controller.resize(); - } - }); - - return this.controller ? this.controller : this; - - }; - - //Globally expose the defaults to allow for user updating/changing - Chart.defaults = { - global: { - responsive: true, - responsiveAnimationDuration: 0, - maintainAspectRatio: true, - events: ["mousemove", "mouseout", "click", "touchstart", "touchmove"], - hover: { - onHover: null, - mode: 'single', - animationDuration: 400 - }, - onClick: null, - defaultColor: 'rgba(0,0,0,0.1)', - defaultFontColor: '#666', - defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", - defaultFontSize: 12, - defaultFontStyle: 'normal', - showLines: true, - - // Element defaults defined in element extensions - elements: {}, - - // Legend callback string - legendCallback: function(chart) { - var text = []; - text.push(''); - - return text.join(""); - } - } - }; - - return Chart; - -}; - -},{}],27:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - // The layout service is very self explanatory. It's responsible for the layout within a chart. - // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need - // It is this service's responsibility of carrying out that layout. - Chart.layoutService = { - defaults: {}, - - // Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins. - addBox: function(chartInstance, box) { - if (!chartInstance.boxes) { - chartInstance.boxes = []; - } - chartInstance.boxes.push(box); - }, - - removeBox: function(chartInstance, box) { - if (!chartInstance.boxes) { - return; - } - chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1); - }, - - // The most important function - update: function(chartInstance, width, height) { - - if (!chartInstance) { - return; - } - - var xPadding = 0; - var yPadding = 0; - - var leftBoxes = helpers.where(chartInstance.boxes, function(box) { - return box.options.position === "left"; - }); - var rightBoxes = helpers.where(chartInstance.boxes, function(box) { - return box.options.position === "right"; - }); - var topBoxes = helpers.where(chartInstance.boxes, function(box) { - return box.options.position === "top"; - }); - var bottomBoxes = helpers.where(chartInstance.boxes, function(box) { - return box.options.position === "bottom"; - }); - - // Boxes that overlay the chartarea such as the radialLinear scale - var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) { - return box.options.position === "chartArea"; - }); - - // Ensure that full width boxes are at the very top / bottom - topBoxes.sort(function(a, b) { - return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0); - }); - bottomBoxes.sort(function(a, b) { - return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0); - }); - - // Essentially we now have any number of boxes on each of the 4 sides. - // Our canvas looks like the following. - // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and - // B1 is the bottom axis - // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays - // These locations are single-box locations only, when trying to register a chartArea location that is already taken, - // an error will be thrown. - // - // |----------------------------------------------------| - // | T1 (Full Width) | - // |----------------------------------------------------| - // | | | T2 | | - // | |----|-------------------------------------|----| - // | | | C1 | | C2 | | - // | | |----| |----| | - // | | | | | - // | L1 | L2 | ChartArea (C0) | R1 | - // | | | | | - // | | |----| |----| | - // | | | C3 | | C4 | | - // | |----|-------------------------------------|----| - // | | | B1 | | - // |----------------------------------------------------| - // | B2 (Full Width) | - // |----------------------------------------------------| - // - // What we do to find the best sizing, we do the following - // 1. Determine the minimum size of the chart area. - // 2. Split the remaining width equally between each vertical axis - // 3. Split the remaining height equally between each horizontal axis - // 4. Give each layout the maximum size it can be. The layout will return it's minimum size - // 5. Adjust the sizes of each axis based on it's minimum reported size. - // 6. Refit each axis - // 7. Position each axis in the final location - // 8. Tell the chart the final location of the chart area - // 9. Tell any axes that overlay the chart area the positions of the chart area - - // Step 1 - var chartWidth = width - (2 * xPadding); - var chartHeight = height - (2 * yPadding); - var chartAreaWidth = chartWidth / 2; // min 50% - var chartAreaHeight = chartHeight / 2; // min 50% - - // Step 2 - var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); - - // Step 3 - var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); - - // Step 4 - var maxChartAreaWidth = chartWidth; - var maxChartAreaHeight = chartHeight; - var minBoxSizes = []; - - helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); - - function getMinimumBoxSize(box) { - var minSize; - var isHorizontal = box.isHorizontal(); - - if (isHorizontal) { - minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); - maxChartAreaHeight -= minSize.height; - } else { - minSize = box.update(verticalBoxWidth, chartAreaHeight); - maxChartAreaWidth -= minSize.width; - } - - minBoxSizes.push({ - horizontal: isHorizontal, - minSize: minSize, - box: box - }); - } - - // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could - // be if the axes are drawn at their minimum sizes. - - // Steps 5 & 6 - var totalLeftBoxesWidth = xPadding; - var totalRightBoxesWidth = xPadding; - var totalTopBoxesHeight = yPadding; - var totalBottomBoxesHeight = yPadding; - - // Update, and calculate the left and right margins for the horizontal boxes - helpers.each(leftBoxes.concat(rightBoxes), fitBox); - - helpers.each(leftBoxes, function(box) { - totalLeftBoxesWidth += box.width; - }); - - helpers.each(rightBoxes, function(box) { - totalRightBoxesWidth += box.width; - }); - - // Set the Left and Right margins for the horizontal boxes - helpers.each(topBoxes.concat(bottomBoxes), fitBox); - - // Function to fit a box - function fitBox(box) { - var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) { - return minBoxSize.box === box; - }); - - if (minBoxSize) { - if (box.isHorizontal()) { - var scaleMargin = { - left: totalLeftBoxesWidth, - right: totalRightBoxesWidth, - top: 0, - bottom: 0 - }; - - // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends - // on the margin. Sometimes they need to increase in size slightly - box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); - } else { - box.update(minBoxSize.minSize.width, maxChartAreaHeight); - } - } - } - - // Figure out how much margin is on the top and bottom of the vertical boxes - helpers.each(topBoxes, function(box) { - totalTopBoxesHeight += box.height; - }); - - helpers.each(bottomBoxes, function(box) { - totalBottomBoxesHeight += box.height; - }); - - // Let the left layout know the final margin - helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); - - function finalFitVerticalBox(box) { - var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) { - return minBoxSize.box === box; - }); - - var scaleMargin = { - left: 0, - right: 0, - top: totalTopBoxesHeight, - bottom: totalBottomBoxesHeight - }; - - if (minBoxSize) { - box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); - } - } - - // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) - totalLeftBoxesWidth = xPadding; - totalRightBoxesWidth = xPadding; - totalTopBoxesHeight = yPadding; - totalBottomBoxesHeight = yPadding; - - helpers.each(leftBoxes, function(box) { - totalLeftBoxesWidth += box.width; - }); - - helpers.each(rightBoxes, function(box) { - totalRightBoxesWidth += box.width; - }); - - helpers.each(topBoxes, function(box) { - totalTopBoxesHeight += box.height; - }); - helpers.each(bottomBoxes, function(box) { - totalBottomBoxesHeight += box.height; - }); - - // Figure out if our chart area changed. This would occur if the dataset layout label rotation - // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do - // without calling `fit` again - var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; - var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; - - if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { - helpers.each(leftBoxes, function(box) { - box.height = newMaxChartAreaHeight; - }); - - helpers.each(rightBoxes, function(box) { - box.height = newMaxChartAreaHeight; - }); - - helpers.each(topBoxes, function(box) { - if (!box.options.fullWidth) { - box.width = newMaxChartAreaWidth; - } - }); - - helpers.each(bottomBoxes, function(box) { - if (!box.options.fullWidth) { - box.width = newMaxChartAreaWidth; - } - }); - - maxChartAreaHeight = newMaxChartAreaHeight; - maxChartAreaWidth = newMaxChartAreaWidth; - } - - // Step 7 - Position the boxes - var left = xPadding; - var top = yPadding; - var right = 0; - var bottom = 0; - - helpers.each(leftBoxes.concat(topBoxes), placeBox); - - // Account for chart width and height - left += maxChartAreaWidth; - top += maxChartAreaHeight; - - helpers.each(rightBoxes, placeBox); - helpers.each(bottomBoxes, placeBox); - - function placeBox(box) { - if (box.isHorizontal()) { - box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth; - box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth; - box.top = top; - box.bottom = top + box.height; - - // Move to next point - top = box.bottom; - - } else { - - box.left = left; - box.right = left + box.width; - box.top = totalTopBoxesHeight; - box.bottom = totalTopBoxesHeight + maxChartAreaHeight; - - // Move to next point - left = box.right; - } - } - - // Step 8 - chartInstance.chartArea = { - left: totalLeftBoxesWidth, - top: totalTopBoxesHeight, - right: totalLeftBoxesWidth + maxChartAreaWidth, - bottom: totalTopBoxesHeight + maxChartAreaHeight - }; - - // Step 9 - helpers.each(chartAreaBoxes, function(box) { - box.left = chartInstance.chartArea.left; - box.top = chartInstance.chartArea.top; - box.right = chartInstance.chartArea.right; - box.bottom = chartInstance.chartArea.bottom; - - box.update(maxChartAreaWidth, maxChartAreaHeight); - }); - } - }; -}; - -},{}],28:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.legend = { - - display: true, - position: 'top', - fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes) - reverse: false, - - // a callback that will handle - onClick: function(e, legendItem) { - var dataset = this.chart.data.datasets[legendItem.datasetIndex]; - dataset.hidden = !dataset.hidden; - - // We hid a dataset ... rerender the chart - this.chart.update(); - }, - - labels: { - boxWidth: 40, - padding: 10, - // Generates labels shown in the legend - // Valid properties to return: - // text : text to display - // fillStyle : fill of coloured box - // strokeStyle: stroke of coloured box - // hidden : if this legend item refers to a hidden item - // lineCap : cap style for line - // lineDash - // lineDashOffset : - // lineJoin : - // lineWidth : - generateLabels: function(data) { - return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { - return { - text: dataset.label, - fillStyle: dataset.backgroundColor, - hidden: dataset.hidden, - lineCap: dataset.borderCapStyle, - lineDash: dataset.borderDash, - lineDashOffset: dataset.borderDashOffset, - lineJoin: dataset.borderJoinStyle, - lineWidth: dataset.borderWidth, - strokeStyle: dataset.borderColor, - - // Below is extra data used for toggling the datasets - datasetIndex: i - }; - }, this) : []; - } - } - }; - - Chart.Legend = Chart.Element.extend({ - - initialize: function(config) { - helpers.extend(this, config); - - // Contains hit boxes for each dataset (in dataset order) - this.legendHitBoxes = []; - - // Are we in doughnut mode which has a different data type - this.doughnutMode = false; - }, - - // These methods are ordered by lifecyle. Utilities then follow. - // Any function defined here is inherited by all legend types. - // Any function can be extended by the legend type - - beforeUpdate: helpers.noop, - update: function(maxWidth, maxHeight, margins) { - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - this.beforeUpdate(); - - // Absorb the master measurements - this.maxWidth = maxWidth; - this.maxHeight = maxHeight; - this.margins = margins; - - // Dimensions - this.beforeSetDimensions(); - this.setDimensions(); - this.afterSetDimensions(); - // Labels - this.beforeBuildLabels(); - this.buildLabels(); - this.afterBuildLabels(); - - // Fit - this.beforeFit(); - this.fit(); - this.afterFit(); - // - this.afterUpdate(); - - return this.minSize; - }, - afterUpdate: helpers.noop, - - // - - beforeSetDimensions: helpers.noop, - setDimensions: function() { - // Set the unconstrained dimension before label rotation - if (this.isHorizontal()) { - // Reset position before calculating rotation - this.width = this.maxWidth; - this.left = 0; - this.right = this.width; - } else { - this.height = this.maxHeight; - - // Reset position before calculating rotation - this.top = 0; - this.bottom = this.height; - } - - // Reset padding - this.paddingLeft = 0; - this.paddingTop = 0; - this.paddingRight = 0; - this.paddingBottom = 0; - - // Reset minSize - this.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: helpers.noop, - - // - - beforeBuildLabels: helpers.noop, - buildLabels: function() { - this.legendItems = this.options.labels.generateLabels.call(this, this.chart.data); - if(this.options.reverse){ - this.legendItems.reverse(); - } - }, - afterBuildLabels: helpers.noop, - - // - - beforeFit: helpers.noop, - fit: function() { - - var ctx = this.ctx; - var fontSize = helpers.getValueOrDefault(this.options.labels.fontSize, Chart.defaults.global.defaultFontSize); - var fontStyle = helpers.getValueOrDefault(this.options.labels.fontStyle, Chart.defaults.global.defaultFontStyle); - var fontFamily = helpers.getValueOrDefault(this.options.labels.fontFamily, Chart.defaults.global.defaultFontFamily); - var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); - - // Reset hit boxes - this.legendHitBoxes = []; - - // Width - if (this.isHorizontal()) { - this.minSize.width = this.maxWidth; // fill all the width - } else { - this.minSize.width = this.options.display ? 10 : 0; - } - - // height - if (this.isHorizontal()) { - this.minSize.height = this.options.display ? 10 : 0; - } else { - this.minSize.height = this.maxHeight; // fill all the height - } - - // Increase sizes here - if (this.options.display) { - if (this.isHorizontal()) { - // Labels - - // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one - this.lineWidths = [0]; - var totalHeight = this.legendItems.length ? fontSize + (this.options.labels.padding) : 0; - - ctx.textAlign = "left"; - ctx.textBaseline = 'top'; - ctx.font = labelFont; - - helpers.each(this.legendItems, function(legendItem, i) { - var width = this.options.labels.boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; - if (this.lineWidths[this.lineWidths.length - 1] + width + this.options.labels.padding >= this.width) { - totalHeight += fontSize + (this.options.labels.padding); - this.lineWidths[this.lineWidths.length] = this.left; - } - - // Store the hitbox width and height here. Final position will be updated in `draw` - this.legendHitBoxes[i] = { - left: 0, - top: 0, - width: width, - height: fontSize - }; - - this.lineWidths[this.lineWidths.length - 1] += width + this.options.labels.padding; - }, this); - - this.minSize.height += totalHeight; - - } else { - // TODO vertical - } - } - - this.width = this.minSize.width; - this.height = this.minSize.height; - - }, - afterFit: helpers.noop, - - // Shared Methods - isHorizontal: function() { - return this.options.position === "top" || this.options.position === "bottom"; - }, - - // Actualy draw the legend on the canvas - draw: function() { - if (this.options.display) { - var ctx = this.ctx; - var cursor = { - x: this.left + ((this.width - this.lineWidths[0]) / 2), - y: this.top + this.options.labels.padding, - line: 0 - }; - - var fontColor = helpers.getValueOrDefault(this.options.labels.fontColor, Chart.defaults.global.defaultFontColor); - var fontSize = helpers.getValueOrDefault(this.options.labels.fontSize, Chart.defaults.global.defaultFontSize); - var fontStyle = helpers.getValueOrDefault(this.options.labels.fontStyle, Chart.defaults.global.defaultFontStyle); - var fontFamily = helpers.getValueOrDefault(this.options.labels.fontFamily, Chart.defaults.global.defaultFontFamily); - var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); - - // Horizontal - if (this.isHorizontal()) { - // Labels - ctx.textAlign = "left"; - ctx.textBaseline = 'top'; - ctx.lineWidth = 0.5; - ctx.strokeStyle = fontColor; // for strikethrough effect - ctx.fillStyle = fontColor; // render in correct colour - ctx.font = labelFont; - - helpers.each(this.legendItems, function(legendItem, i) { - var textWidth = ctx.measureText(legendItem.text).width; - var width = this.options.labels.boxWidth + (fontSize / 2) + textWidth; - - if (cursor.x + width >= this.width) { - cursor.y += fontSize + (this.options.labels.padding); - cursor.line++; - cursor.x = this.left + ((this.width - this.lineWidths[cursor.line]) / 2); - } - - // Set the ctx for the box - ctx.save(); - - var itemOrDefault = function(item, defaulVal) { - return item !== undefined ? item : defaulVal; - }; - - ctx.fillStyle = itemOrDefault(legendItem.fillStyle, Chart.defaults.global.defaultColor); - ctx.lineCap = itemOrDefault(legendItem.lineCap, Chart.defaults.global.elements.line.borderCapStyle); - ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, Chart.defaults.global.elements.line.borderDashOffset); - ctx.lineJoin = itemOrDefault(legendItem.lineJoin, Chart.defaults.global.elements.line.borderJoinStyle); - ctx.lineWidth = itemOrDefault(legendItem.lineWidth, Chart.defaults.global.elements.line.borderWidth); - ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, Chart.defaults.global.defaultColor); - - if (ctx.setLineDash) { - // IE 9 and 10 do not support line dash - ctx.setLineDash(itemOrDefault(legendItem.lineDash, Chart.defaults.global.elements.line.borderDash)); - } - - // Draw the box - ctx.strokeRect(cursor.x, cursor.y, this.options.labels.boxWidth, fontSize); - ctx.fillRect(cursor.x, cursor.y, this.options.labels.boxWidth, fontSize); - - ctx.restore(); - - this.legendHitBoxes[i].left = cursor.x; - this.legendHitBoxes[i].top = cursor.y; - - // Fill the actual label - ctx.fillText(legendItem.text, this.options.labels.boxWidth + (fontSize / 2) + cursor.x, cursor.y); - - if (legendItem.hidden) { - // Strikethrough the text if hidden - ctx.beginPath(); - ctx.lineWidth = 2; - ctx.moveTo(this.options.labels.boxWidth + (fontSize / 2) + cursor.x, cursor.y + (fontSize / 2)); - ctx.lineTo(this.options.labels.boxWidth + (fontSize / 2) + cursor.x + textWidth, cursor.y + (fontSize / 2)); - ctx.stroke(); - } - - cursor.x += width + (this.options.labels.padding); - }, this); - } else { - - } - } - }, - - // Handle an event - handleEvent: function(e) { - var position = helpers.getRelativePosition(e, this.chart.chart); - - if (position.x >= this.left && position.x <= this.right && position.y >= this.top && position.y <= this.bottom) { - // See if we are touching one of the dataset boxes - for (var i = 0; i < this.legendHitBoxes.length; ++i) { - var hitBox = this.legendHitBoxes[i]; - - if (position.x >= hitBox.left && position.x <= hitBox.left + hitBox.width && position.y >= hitBox.top && position.y <= hitBox.top + hitBox.height) { - // Touching an element - if (this.options.onClick) { - this.options.onClick.call(this, e, this.legendItems[i]); - } - break; - } - } - } - } - }); - -}; - -},{}],29:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - var helpers = Chart.helpers; - - // Plugins are stored here - Chart.plugins = []; - Chart.pluginService = { - // Register a new plugin - register: function(plugin) { - if (Chart.plugins.indexOf(plugin) === -1) { - Chart.plugins.push(plugin); - } - }, - - // Remove a registered plugin - remove: function(plugin) { - var idx = Chart.plugins.indexOf(plugin); - if (idx !== -1) { - Chart.plugins.splice(idx, 1); - } - }, - - // Iterate over all plugins - notifyPlugins: function(method, args, scope) { - helpers.each(Chart.plugins, function(plugin) { - if (plugin[method] && typeof plugin[method] === 'function') { - plugin[method].apply(scope, args); - } - }, scope); - } - }; - - Chart.PluginBase = Chart.Element.extend({ - // Plugin methods. All functions are passed the chart instance - - // Called at start of chart init - beforeInit: helpers.noop, - - // Called at end of chart init - afterInit: helpers.noop, - - // Called at start of update - beforeUpdate: helpers.noop, - - // Called at end of update - afterUpdate: helpers.noop, - - // Called at start of draw - beforeDraw: helpers.noop, - - // Called at end of draw - afterDraw: helpers.noop, - - // Called during destroy - destroy: helpers.noop, - }); -}; - -},{}],30:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.scale = { - display: true, - - // grid line settings - gridLines: { - display: true, - color: "rgba(0, 0, 0, 0.1)", - lineWidth: 1, - drawOnChartArea: true, - drawTicks: true, - tickMarkLength: 10, - zeroLineWidth: 1, - zeroLineColor: "rgba(0,0,0,0.25)", - offsetGridLines: false - }, - - // scale label - scaleLabel: { - // actual label - labelString: '', - - // display property - display: false - }, - - // label settings - ticks: { - beginAtZero: false, - maxRotation: 50, - mirror: false, - padding: 10, - reverse: false, - display: true, - autoSkip: true, - autoSkipPadding: 0, - callback: function(value) { - return '' + value; - } - } - }; - - Chart.Scale = Chart.Element.extend({ - - // These methods are ordered by lifecyle. Utilities then follow. - // Any function defined here is inherited by all scale types. - // Any function can be extended by the scale type - - beforeUpdate: function() { - helpers.callCallback(this.options.beforeUpdate, [this]); - }, - update: function(maxWidth, maxHeight, margins) { - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - this.beforeUpdate(); - - // Absorb the master measurements - this.maxWidth = maxWidth; - this.maxHeight = maxHeight; - this.margins = helpers.extend({ - left: 0, - right: 0, - top: 0, - bottom: 0 - }, margins); - - // Dimensions - this.beforeSetDimensions(); - this.setDimensions(); - this.afterSetDimensions(); - - // Data min/max - this.beforeDataLimits(); - this.determineDataLimits(); - this.afterDataLimits(); - - // Ticks - this.beforeBuildTicks(); - this.buildTicks(); - this.afterBuildTicks(); - - this.beforeTickToLabelConversion(); - this.convertTicksToLabels(); - this.afterTickToLabelConversion(); - - // Tick Rotation - this.beforeCalculateTickRotation(); - this.calculateTickRotation(); - this.afterCalculateTickRotation(); - // Fit - this.beforeFit(); - this.fit(); - this.afterFit(); - // - this.afterUpdate(); - - return this.minSize; - - }, - afterUpdate: function() { - helpers.callCallback(this.options.afterUpdate, [this]); - }, - - // - - beforeSetDimensions: function() { - helpers.callCallback(this.options.beforeSetDimensions, [this]); - }, - setDimensions: function() { - // Set the unconstrained dimension before label rotation - if (this.isHorizontal()) { - // Reset position before calculating rotation - this.width = this.maxWidth; - this.left = 0; - this.right = this.width; - } else { - this.height = this.maxHeight; - - // Reset position before calculating rotation - this.top = 0; - this.bottom = this.height; - } - - // Reset padding - this.paddingLeft = 0; - this.paddingTop = 0; - this.paddingRight = 0; - this.paddingBottom = 0; - }, - afterSetDimensions: function() { - helpers.callCallback(this.options.afterSetDimensions, [this]); - }, - - // Data limits - beforeDataLimits: function() { - helpers.callCallback(this.options.beforeDataLimits, [this]); - }, - determineDataLimits: helpers.noop, - afterDataLimits: function() { - helpers.callCallback(this.options.afterDataLimits, [this]); - }, - - // - beforeBuildTicks: function() { - helpers.callCallback(this.options.beforeBuildTicks, [this]); - }, - buildTicks: helpers.noop, - afterBuildTicks: function() { - helpers.callCallback(this.options.afterBuildTicks, [this]); - }, - - beforeTickToLabelConversion: function() { - helpers.callCallback(this.options.beforeTickToLabelConversion, [this]); - }, - convertTicksToLabels: function() { - // Convert ticks to strings - this.ticks = this.ticks.map(function(numericalTick, index, ticks) { - if (this.options.ticks.userCallback) { - return this.options.ticks.userCallback(numericalTick, index, ticks); - } - return this.options.ticks.callback(numericalTick, index, ticks); - }, - this); - }, - afterTickToLabelConversion: function() { - helpers.callCallback(this.options.afterTickToLabelConversion, [this]); - }, - - // - - beforeCalculateTickRotation: function() { - helpers.callCallback(this.options.beforeCalculateTickRotation, [this]); - }, - calculateTickRotation: function() { - //Get the width of each grid by calculating the difference - //between x offsets between 0 and 1. - var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); - var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle); - var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily); - var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); - this.ctx.font = tickLabelFont; - - var firstWidth = this.ctx.measureText(this.ticks[0]).width; - var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width; - var firstRotated; - - this.labelRotation = 0; - this.paddingRight = 0; - this.paddingLeft = 0; - - if (this.options.display) { - if (this.isHorizontal()) { - this.paddingRight = lastWidth / 2 + 3; - this.paddingLeft = firstWidth / 2 + 3; - - if (!this.longestTextCache) { - this.longestTextCache = {}; - } - var originalLabelWidth = helpers.longestText(this.ctx, tickLabelFont, this.ticks, this.longestTextCache); - var labelWidth = originalLabelWidth; - var cosRotation; - var sinRotation; - - // Allow 3 pixels x2 padding either side for label readability - // only the index matters for a dataset scale, but we want a consistent interface between scales - var tickWidth = this.getPixelForTick(1) - this.getPixelForTick(0) - 6; - - //Max label rotation can be set or default to 90 - also act as a loop counter - while (labelWidth > tickWidth && this.labelRotation < this.options.ticks.maxRotation) { - cosRotation = Math.cos(helpers.toRadians(this.labelRotation)); - sinRotation = Math.sin(helpers.toRadians(this.labelRotation)); - - firstRotated = cosRotation * firstWidth; - - // We're right aligning the text now. - if (firstRotated + tickFontSize / 2 > this.yLabelWidth) { - this.paddingLeft = firstRotated + tickFontSize / 2; - } - - this.paddingRight = tickFontSize / 2; - - if (sinRotation * originalLabelWidth > this.maxHeight) { - // go back one step - this.labelRotation--; - break; - } - - this.labelRotation++; - labelWidth = cosRotation * originalLabelWidth; - } - } - } - - if (this.margins) { - this.paddingLeft = Math.max(this.paddingLeft - this.margins.left, 0); - this.paddingRight = Math.max(this.paddingRight - this.margins.right, 0); - } - }, - afterCalculateTickRotation: function() { - helpers.callCallback(this.options.afterCalculateTickRotation, [this]); - }, - - // - - beforeFit: function() { - helpers.callCallback(this.options.beforeFit, [this]); - }, - fit: function() { - - this.minSize = { - width: 0, - height: 0 - }; - - var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); - var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle); - var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily); - var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); - - var scaleLabelFontSize = helpers.getValueOrDefault(this.options.scaleLabel.fontSize, Chart.defaults.global.defaultFontSize); - var scaleLabelFontStyle = helpers.getValueOrDefault(this.options.scaleLabel.fontStyle, Chart.defaults.global.defaultFontStyle); - var scaleLabelFontFamily = helpers.getValueOrDefault(this.options.scaleLabel.fontFamily, Chart.defaults.global.defaultFontFamily); - var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily); - - // Width - if (this.isHorizontal()) { - // subtract the margins to line up with the chartArea if we are a full width scale - this.minSize.width = this.isFullWidth() ? this.maxWidth - this.margins.left - this.margins.right : this.maxWidth; - } else { - this.minSize.width = this.options.gridLines.tickMarkLength; - } - - // height - if (this.isHorizontal()) { - this.minSize.height = this.options.gridLines.tickMarkLength; - } else { - this.minSize.height = this.maxHeight; // fill all the height - } - - // Are we showing a title for the scale? - if (this.options.scaleLabel.display) { - if (this.isHorizontal()) { - this.minSize.height += (scaleLabelFontSize * 1.5); - } else { - this.minSize.width += (scaleLabelFontSize * 1.5); - } - } - - if (this.options.ticks.display && this.options.display) { - // Don't bother fitting the ticks if we are not showing them - if (!this.longestTextCache) { - this.longestTextCache = {}; - } - - var largestTextWidth = helpers.longestText(this.ctx, tickLabelFont, this.ticks, this.longestTextCache); - - if (this.isHorizontal()) { - // A horizontal axis is more constrained by the height. - this.longestLabelWidth = largestTextWidth; - - // TODO - improve this calculation - var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * this.longestLabelWidth) + 1.5 * tickFontSize; - - this.minSize.height = Math.min(this.maxHeight, this.minSize.height + labelHeight); - this.ctx.font = tickLabelFont; - - var firstLabelWidth = this.ctx.measureText(this.ticks[0]).width; - var lastLabelWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width; - - // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated - // by the font height - var cosRotation = Math.cos(helpers.toRadians(this.labelRotation)); - var sinRotation = Math.sin(helpers.toRadians(this.labelRotation)); - this.paddingLeft = this.labelRotation !== 0 ? (cosRotation * firstLabelWidth) + 3 : firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges - this.paddingRight = this.labelRotation !== 0 ? (sinRotation * (tickFontSize / 2)) + 3 : lastLabelWidth / 2 + 3; // when rotated - } else { - // A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first - var maxLabelWidth = this.maxWidth - this.minSize.width; - - // Account for padding - if (!this.options.ticks.mirror) { - largestTextWidth += this.options.ticks.padding; - } - - if (largestTextWidth < maxLabelWidth) { - // We don't need all the room - this.minSize.width += largestTextWidth; - } else { - // Expand to max size - this.minSize.width = this.maxWidth; - } - - this.paddingTop = tickFontSize / 2; - this.paddingBottom = tickFontSize / 2; - } - } - - if (this.margins) { - this.paddingLeft = Math.max(this.paddingLeft - this.margins.left, 0); - this.paddingTop = Math.max(this.paddingTop - this.margins.top, 0); - this.paddingRight = Math.max(this.paddingRight - this.margins.right, 0); - this.paddingBottom = Math.max(this.paddingBottom - this.margins.bottom, 0); - } - - this.width = this.minSize.width; - this.height = this.minSize.height; - - }, - afterFit: function() { - helpers.callCallback(this.options.afterFit, [this]); - }, - - // Shared Methods - isHorizontal: function() { - return this.options.position === "top" || this.options.position === "bottom"; - }, - isFullWidth: function() { - return (this.options.fullWidth); - }, - - // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not - getRightValue: function getRightValue(rawValue) { - // Null and undefined values first - if (rawValue === null || typeof(rawValue) === 'undefined') { - return NaN; - } - // isNaN(object) returns true, so make sure NaN is checking for a number - if (typeof(rawValue) === 'number' && isNaN(rawValue)) { - return NaN; - } - // If it is in fact an object, dive in one more level - if (typeof(rawValue) === "object") { - if (rawValue instanceof Date) { - return rawValue; - } else { - return getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y); - } - } - - // Value is good, return it - return rawValue; - }, - - // Used to get the value to display in the tooltip for the data at the given index - // function getLabelForIndex(index, datasetIndex) - getLabelForIndex: helpers.noop, - - // Used to get data value locations. Value can either be an index or a numerical value - getPixelForValue: helpers.noop, - - // Used for tick location, should - getPixelForTick: function(index, includeOffset) { - if (this.isHorizontal()) { - var innerWidth = this.width - (this.paddingLeft + this.paddingRight); - var tickWidth = innerWidth / Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1); - var pixel = (tickWidth * index) + this.paddingLeft; - - if (includeOffset) { - pixel += tickWidth / 2; - } - - var finalVal = this.left + Math.round(pixel); - finalVal += this.isFullWidth() ? this.margins.left : 0; - return finalVal; - } else { - var innerHeight = this.height - (this.paddingTop + this.paddingBottom); - return this.top + (index * (innerHeight / (this.ticks.length - 1))); - } - }, - - // Utility for getting the pixel location of a percentage of scale - getPixelForDecimal: function(decimal /*, includeOffset*/ ) { - if (this.isHorizontal()) { - var innerWidth = this.width - (this.paddingLeft + this.paddingRight); - var valueOffset = (innerWidth * decimal) + this.paddingLeft; - - var finalVal = this.left + Math.round(valueOffset); - finalVal += this.isFullWidth() ? this.margins.left : 0; - return finalVal; - } else { - return this.top + (decimal * this.height); - } - }, - - // Actualy draw the scale on the canvas - // @param {rectangle} chartArea : the area of the chart to draw full grid lines on - draw: function(chartArea) { - if (this.options.display) { - - var setContextLineSettings; - var isRotated = this.labelRotation !== 0; - var skipRatio; - var scaleLabelX; - var scaleLabelY; - var useAutoskipper = this.options.ticks.autoSkip; - - - // figure out the maximum number of gridlines to show - var maxTicks; - - if (this.options.ticks.maxTicksLimit) { - maxTicks = this.options.ticks.maxTicksLimit; - } - - var tickFontColor = helpers.getValueOrDefault(this.options.ticks.fontColor, Chart.defaults.global.defaultFontColor); - var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); - var tickFontStyle = helpers.getValueOrDefault(this.options.ticks.fontStyle, Chart.defaults.global.defaultFontStyle); - var tickFontFamily = helpers.getValueOrDefault(this.options.ticks.fontFamily, Chart.defaults.global.defaultFontFamily); - var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); - var tl = this.options.gridLines.tickMarkLength; - - var scaleLabelFontColor = helpers.getValueOrDefault(this.options.scaleLabel.fontColor, Chart.defaults.global.defaultFontColor); - var scaleLabelFontSize = helpers.getValueOrDefault(this.options.scaleLabel.fontSize, Chart.defaults.global.defaultFontSize); - var scaleLabelFontStyle = helpers.getValueOrDefault(this.options.scaleLabel.fontStyle, Chart.defaults.global.defaultFontStyle); - var scaleLabelFontFamily = helpers.getValueOrDefault(this.options.scaleLabel.fontFamily, Chart.defaults.global.defaultFontFamily); - var scaleLabelFont = helpers.fontString(scaleLabelFontSize, scaleLabelFontStyle, scaleLabelFontFamily); - - var cosRotation = Math.cos(helpers.toRadians(this.labelRotation)); - var sinRotation = Math.sin(helpers.toRadians(this.labelRotation)); - var longestRotatedLabel = this.longestLabelWidth * cosRotation; - var rotatedLabelHeight = tickFontSize * sinRotation; - - // Make sure we draw text in the correct color and font - this.ctx.fillStyle = tickFontColor; - - if (this.isHorizontal()) { - setContextLineSettings = true; - var yTickStart = this.options.position === "bottom" ? this.top : this.bottom - tl; - var yTickEnd = this.options.position === "bottom" ? this.top + tl : this.bottom; - skipRatio = false; - - if (((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) { - skipRatio = 1 + Math.floor((((longestRotatedLabel / 2) + this.options.ticks.autoSkipPadding) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight))); - } - - // if they defined a max number of ticks, - // increase skipRatio until that number is met - if (maxTicks && this.ticks.length > maxTicks) { - while (!skipRatio || this.ticks.length / (skipRatio || 1) > maxTicks) { - if (!skipRatio) { - skipRatio = 1; - } - skipRatio += 1; - } - } - - if (!useAutoskipper) { - skipRatio = false; - } - - helpers.each(this.ticks, function(label, index) { - // Blank ticks - var isLastTick = this.ticks.length === index + 1; - - // Since we always show the last tick,we need may need to hide the last shown one before - var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio > this.ticks.length); - if (shouldSkip && !isLastTick || (label === undefined || label === null)) { - return; - } - var xLineValue = this.getPixelForTick(index); // xvalues for grid lines - var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option) - - if (this.options.gridLines.display) { - if (index === (typeof this.zeroLineIndex !== 'undefined' ? this.zeroLineIndex : 0)) { - // Draw the first index specially - this.ctx.lineWidth = this.options.gridLines.zeroLineWidth; - this.ctx.strokeStyle = this.options.gridLines.zeroLineColor; - setContextLineSettings = true; // reset next time - } else if (setContextLineSettings) { - this.ctx.lineWidth = this.options.gridLines.lineWidth; - this.ctx.strokeStyle = this.options.gridLines.color; - setContextLineSettings = false; - } - - xLineValue += helpers.aliasPixel(this.ctx.lineWidth); - - // Draw the label area - this.ctx.beginPath(); - - if (this.options.gridLines.drawTicks) { - this.ctx.moveTo(xLineValue, yTickStart); - this.ctx.lineTo(xLineValue, yTickEnd); - } - - // Draw the chart area - if (this.options.gridLines.drawOnChartArea) { - this.ctx.moveTo(xLineValue, chartArea.top); - this.ctx.lineTo(xLineValue, chartArea.bottom); - } - - // Need to stroke in the loop because we are potentially changing line widths & colours - this.ctx.stroke(); - } - - if (this.options.ticks.display) { - this.ctx.save(); - this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.options.position === "top" ? this.bottom - tl : this.top + tl); - this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1); - this.ctx.font = tickLabelFont; - this.ctx.textAlign = (isRotated) ? "right" : "center"; - this.ctx.textBaseline = (isRotated) ? "middle" : this.options.position === "top" ? "bottom" : "top"; - this.ctx.fillText(label, 0, 0); - this.ctx.restore(); - } - }, this); - - if (this.options.scaleLabel.display) { - // Draw the scale label - this.ctx.textAlign = "center"; - this.ctx.textBaseline = 'middle'; - this.ctx.fillStyle = scaleLabelFontColor; // render in correct colour - this.ctx.font = scaleLabelFont; - - scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width - scaleLabelY = this.options.position === 'bottom' ? this.bottom - (scaleLabelFontSize / 2) : this.top + (scaleLabelFontSize / 2); - - this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY); - } - - } else { - setContextLineSettings = true; - var xTickStart = this.options.position === "right" ? this.left : this.right - 5; - var xTickEnd = this.options.position === "right" ? this.left + 5 : this.right; - - helpers.each(this.ticks, function(label, index) { - // If the callback returned a null or undefined value, do not draw this line - if (label === undefined || label === null) { - return; - } - - var yLineValue = this.getPixelForTick(index); // xvalues for grid lines - - if (this.options.gridLines.display) { - if (index === (typeof this.zeroLineIndex !== 'undefined' ? this.zeroLineIndex : 0)) { - // Draw the first index specially - this.ctx.lineWidth = this.options.gridLines.zeroLineWidth; - this.ctx.strokeStyle = this.options.gridLines.zeroLineColor; - setContextLineSettings = true; // reset next time - } else if (setContextLineSettings) { - this.ctx.lineWidth = this.options.gridLines.lineWidth; - this.ctx.strokeStyle = this.options.gridLines.color; - setContextLineSettings = false; - } - - yLineValue += helpers.aliasPixel(this.ctx.lineWidth); - - // Draw the label area - this.ctx.beginPath(); - - if (this.options.gridLines.drawTicks) { - this.ctx.moveTo(xTickStart, yLineValue); - this.ctx.lineTo(xTickEnd, yLineValue); - } - - // Draw the chart area - if (this.options.gridLines.drawOnChartArea) { - this.ctx.moveTo(chartArea.left, yLineValue); - this.ctx.lineTo(chartArea.right, yLineValue); - } - - // Need to stroke in the loop because we are potentially changing line widths & colours - this.ctx.stroke(); - } - - if (this.options.ticks.display) { - var xLabelValue; - var yLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option) - - this.ctx.save(); - - if (this.options.position === "left") { - if (this.options.ticks.mirror) { - xLabelValue = this.right + this.options.ticks.padding; - this.ctx.textAlign = "left"; - } else { - xLabelValue = this.right - this.options.ticks.padding; - this.ctx.textAlign = "right"; - } - } else { - // right side - if (this.options.ticks.mirror) { - xLabelValue = this.left - this.options.ticks.padding; - this.ctx.textAlign = "right"; - } else { - xLabelValue = this.left + this.options.ticks.padding; - this.ctx.textAlign = "left"; - } - } - - this.ctx.translate(xLabelValue, yLabelValue); - this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1); - this.ctx.font = tickLabelFont; - this.ctx.textBaseline = "middle"; - this.ctx.fillText(label, 0, 0); - this.ctx.restore(); - } - }, this); - - if (this.options.scaleLabel.display) { - // Draw the scale label - scaleLabelX = this.options.position === 'left' ? this.left + (scaleLabelFontSize / 2) : this.right - (scaleLabelFontSize / 2); - scaleLabelY = this.top + ((this.bottom - this.top) / 2); - var rotation = this.options.position === 'left' ? -0.5 * Math.PI : 0.5 * Math.PI; - - this.ctx.save(); - this.ctx.translate(scaleLabelX, scaleLabelY); - this.ctx.rotate(rotation); - this.ctx.textAlign = "center"; - this.ctx.fillStyle =scaleLabelFontColor; // render in correct colour - this.ctx.font = scaleLabelFont; - this.ctx.textBaseline = 'middle'; - this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0); - this.ctx.restore(); - } - } - - // Draw the line at the edge of the axis - this.ctx.lineWidth = this.options.gridLines.lineWidth; - this.ctx.strokeStyle = this.options.gridLines.color; - var x1 = this.left, - x2 = this.right, - y1 = this.top, - y2 = this.bottom; - - if (this.isHorizontal()) { - y1 = y2 = this.options.position === 'top' ? this.bottom : this.top; - y1 += helpers.aliasPixel(this.ctx.lineWidth); - y2 += helpers.aliasPixel(this.ctx.lineWidth); - } else { - x1 = x2 = this.options.position === 'left' ? this.right : this.left; - x1 += helpers.aliasPixel(this.ctx.lineWidth); - x2 += helpers.aliasPixel(this.ctx.lineWidth); - } - - this.ctx.beginPath(); - this.ctx.moveTo(x1, y1); - this.ctx.lineTo(x2, y2); - this.ctx.stroke(); - } - } - }); -}; + "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(28)(); + +require(26)(Chart); +require(22)(Chart); +require(25)(Chart); +require(21)(Chart); +require(23)(Chart); +require(24)(Chart); +require(29)(Chart); +require(33)(Chart); +require(31)(Chart); +require(34)(Chart); +require(32)(Chart); +require(35)(Chart); +require(30)(Chart); +require(27)(Chart); +require(36)(Chart); + +require(37)(Chart); +require(38)(Chart); +require(39)(Chart); +require(40)(Chart); + +require(43)(Chart); +require(41)(Chart); +require(42)(Chart); +require(44)(Chart); +require(45)(Chart); +require(46)(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(8)(Chart); +require(9)(Chart); +require(10)(Chart); +require(11)(Chart); +require(12)(Chart); +require(13)(Chart); +require(14)(Chart); + +window.Chart = module.exports = Chart; + +},{"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,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"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); + }; -},{}],31:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.scaleService = { - // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then - // use the new chart options to grab the correct scale - constructors: {}, - // Use a registration function so that we can move to an ES6 map when we no longer need to support - // old browsers - - // Scale config defaults - defaults: {}, - registerScaleType: function(type, scaleConstructor, defaults) { - this.constructors[type] = scaleConstructor; - this.defaults[type] = helpers.clone(defaults); - }, - getScaleConstructor: function(type) { - return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; - }, - getScaleDefaults: function(type) { - // Return the scale defaults merged with the global settings so that we always use the latest ones - return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {}; - }, - addScalesToLayout: function(chartInstance) { - // Adds each scale to the chart.boxes array to be sized accordingly - helpers.each(chartInstance.scales, function(scale) { - Chart.layoutService.addBox(chartInstance, scale); - }); - } - }; }; -},{}],32:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.title = { - display: false, - position: 'top', - fullWidth: true, // marks that this box should take the full width of the canvas (pushing down other boxes) - - fontStyle: 'bold', - padding: 10, - - // actual title - text: '' - }; - - Chart.Title = Chart.Element.extend({ - - initialize: function(config) { - helpers.extend(this, config); - this.options = helpers.configMerge(Chart.defaults.global.title, config.options); - - // Contains hit boxes for each dataset (in dataset order) - this.legendHitBoxes = []; - }, - - // These methods are ordered by lifecyle. Utilities then follow. - - beforeUpdate: helpers.noop, - update: function(maxWidth, maxHeight, margins) { - - // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) - this.beforeUpdate(); - - // Absorb the master measurements - this.maxWidth = maxWidth; - this.maxHeight = maxHeight; - this.margins = margins; - - // Dimensions - this.beforeSetDimensions(); - this.setDimensions(); - this.afterSetDimensions(); - // Labels - this.beforeBuildLabels(); - this.buildLabels(); - this.afterBuildLabels(); - - // Fit - this.beforeFit(); - this.fit(); - this.afterFit(); - // - this.afterUpdate(); - - return this.minSize; - - }, - afterUpdate: helpers.noop, - - // - - beforeSetDimensions: helpers.noop, - setDimensions: function() { - // Set the unconstrained dimension before label rotation - if (this.isHorizontal()) { - // Reset position before calculating rotation - this.width = this.maxWidth; - this.left = 0; - this.right = this.width; - } else { - this.height = this.maxHeight; - - // Reset position before calculating rotation - this.top = 0; - this.bottom = this.height; - } - - // Reset padding - this.paddingLeft = 0; - this.paddingTop = 0; - this.paddingRight = 0; - this.paddingBottom = 0; - - // Reset minSize - this.minSize = { - width: 0, - height: 0 - }; - }, - afterSetDimensions: helpers.noop, - - // - - beforeBuildLabels: helpers.noop, - buildLabels: helpers.noop, - afterBuildLabels: helpers.noop, - - // - - beforeFit: helpers.noop, - fit: function() { - - var ctx = this.ctx; - var fontSize = helpers.getValueOrDefault(this.options.fontSize, Chart.defaults.global.defaultFontSize); - var fontStyle = helpers.getValueOrDefault(this.options.fontStyle, Chart.defaults.global.defaultFontStyle); - var fontFamily = helpers.getValueOrDefault(this.options.fontFamily, Chart.defaults.global.defaultFontFamily); - var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); - - // Width - if (this.isHorizontal()) { - this.minSize.width = this.maxWidth; // fill all the width - } else { - this.minSize.width = 0; - } - - // height - if (this.isHorizontal()) { - this.minSize.height = 0; - } else { - this.minSize.height = this.maxHeight; // fill all the height - } - - // Increase sizes here - if (this.isHorizontal()) { - - // Title - if (this.options.display) { - this.minSize.height += fontSize + (this.options.padding * 2); - } - } else { - if (this.options.display) { - this.minSize.width += fontSize + (this.options.padding * 2); - } - } - - this.width = this.minSize.width; - this.height = this.minSize.height; - - }, - afterFit: helpers.noop, - - // Shared Methods - isHorizontal: function() { - return this.options.position === "top" || this.options.position === "bottom"; - }, - - // Actualy draw the title block on the canvas - draw: function() { - if (this.options.display) { - var ctx = this.ctx; - var titleX, titleY; - - var fontColor = helpers.getValueOrDefault(this.options.fontColor, Chart.defaults.global.defaultFontColor); - var fontSize = helpers.getValueOrDefault(this.options.fontSize, Chart.defaults.global.defaultFontSize); - var fontStyle = helpers.getValueOrDefault(this.options.fontStyle, Chart.defaults.global.defaultFontStyle); - var fontFamily = helpers.getValueOrDefault(this.options.fontFamily, Chart.defaults.global.defaultFontFamily); - var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); - - ctx.fillStyle = fontColor; // render in correct colour - ctx.font = titleFont; - - // Horizontal - if (this.isHorizontal()) { - // Title - ctx.textAlign = "center"; - ctx.textBaseline = 'middle'; - - titleX = this.left + ((this.right - this.left) / 2); // midpoint of the width - titleY = this.top + ((this.bottom - this.top) / 2); // midpoint of the height - - ctx.fillText(this.options.text, titleX, titleY); - } else { - - // Title - titleX = this.options.position === 'left' ? this.left + (fontSize / 2) : this.right - (fontSize / 2); - titleY = this.top + ((this.bottom - this.top) / 2); - var rotation = this.options.position === 'left' ? -0.5 * Math.PI : 0.5 * Math.PI; - - ctx.save(); - ctx.translate(titleX, titleY); - ctx.rotate(rotation); - ctx.textAlign = "center"; - ctx.textBaseline = 'middle'; - ctx.fillText(this.options.text, 0, 0); - ctx.restore(); - } - } - } - }); + +},{}],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); + }; + }; -},{}],33:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.tooltips = { - enabled: true, - custom: null, - mode: 'single', - backgroundColor: "rgba(0,0,0,0.8)", - titleFontStyle: "bold", - titleSpacing: 2, - titleMarginBottom: 6, - titleColor: "#fff", - titleAlign: "left", - bodySpacing: 2, - bodyColor: "#fff", - bodyAlign: "left", - footerFontStyle: "bold", - footerSpacing: 2, - footerMarginTop: 6, - footerColor: "#fff", - footerAlign: "left", - yPadding: 6, - xPadding: 6, - yAlign : 'center', - xAlign : 'center', - caretSize: 5, - cornerRadius: 6, - multiKeyBackground: '#fff', - callbacks: { - // Args are: (tooltipItems, data) - beforeTitle: helpers.noop, - title: function(tooltipItems, data) { - // Pick first xLabel for now - var title = ''; - - if (tooltipItems.length > 0) { - if (tooltipItems[0].xLabel) { - title = tooltipItems[0].xLabel; - } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { - title = data.labels[tooltipItems[0].index]; - } - } - - return title; - }, - afterTitle: helpers.noop, - - // Args are: (tooltipItems, data) - beforeBody: helpers.noop, - - // Args are: (tooltipItem, data) - beforeLabel: helpers.noop, - label: function(tooltipItem, data) { - var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; - return datasetLabel + ': ' + tooltipItem.yLabel; - }, - afterLabel: helpers.noop, - - // Args are: (tooltipItems, data) - afterBody: helpers.noop, - - // Args are: (tooltipItems, data) - beforeFooter: helpers.noop, - footer: helpers.noop, - afterFooter: helpers.noop - } - }; - - // Helper to push or concat based on if the 2nd parameter is an array or not - function pushOrConcat(base, toPush) { - if (toPush) { - if (helpers.isArray(toPush)) { - base = base.concat(toPush); - } else { - base.push(toPush); - } - } - - return base; - } - - Chart.Tooltip = Chart.Element.extend({ - initialize: function() { - var options = this._options; - helpers.extend(this, { - _model: { - // Positioning - xPadding: options.tooltips.xPadding, - yPadding: options.tooltips.yPadding, - xAlign : options.tooltips.yAlign, - yAlign : options.tooltips.xAlign, - - // Body - bodyColor: options.tooltips.bodyColor, - _bodyFontFamily: helpers.getValueOrDefault(options.tooltips.bodyFontFamily, Chart.defaults.global.defaultFontFamily), - _bodyFontStyle: helpers.getValueOrDefault(options.tooltips.bodyFontStyle, Chart.defaults.global.defaultFontStyle), - _bodyAlign: options.tooltips.bodyAlign, - bodyFontSize: helpers.getValueOrDefault(options.tooltips.bodyFontSize, Chart.defaults.global.defaultFontSize), - bodySpacing: options.tooltips.bodySpacing, - - // Title - titleColor: options.tooltips.titleColor, - _titleFontFamily: helpers.getValueOrDefault(options.tooltips.titleFontFamily, Chart.defaults.global.defaultFontFamily), - _titleFontStyle: helpers.getValueOrDefault(options.tooltips.titleFontStyle, Chart.defaults.global.defaultFontStyle), - titleFontSize: helpers.getValueOrDefault(options.tooltips.titleFontSize, Chart.defaults.global.defaultFontSize), - _titleAlign: options.tooltips.titleAlign, - titleSpacing: options.tooltips.titleSpacing, - titleMarginBottom: options.tooltips.titleMarginBottom, - - // Footer - footerColor: options.tooltips.footerColor, - _footerFontFamily: helpers.getValueOrDefault(options.tooltips.footerFontFamily, Chart.defaults.global.defaultFontFamily), - _footerFontStyle: helpers.getValueOrDefault(options.tooltips.footerFontStyle, Chart.defaults.global.defaultFontStyle), - footerFontSize: helpers.getValueOrDefault(options.tooltips.footerFontSize, Chart.defaults.global.defaultFontSize), - _footerAlign: options.tooltips.footerAlign, - footerSpacing: options.tooltips.footerSpacing, - footerMarginTop: options.tooltips.footerMarginTop, - - // Appearance - caretSize: options.tooltips.caretSize, - cornerRadius: options.tooltips.cornerRadius, - backgroundColor: options.tooltips.backgroundColor, - opacity: 0, - legendColorBackground: options.tooltips.multiKeyBackground - } - }); - }, - - // Get the title - // Args are: (tooltipItem, data) - getTitle: function() { - var beforeTitle = this._options.tooltips.callbacks.beforeTitle.apply(this, arguments), - title = this._options.tooltips.callbacks.title.apply(this, arguments), - afterTitle = this._options.tooltips.callbacks.afterTitle.apply(this, arguments); - - var lines = []; - lines = pushOrConcat(lines, beforeTitle); - lines = pushOrConcat(lines, title); - lines = pushOrConcat(lines, afterTitle); - - return lines; - }, - - // Args are: (tooltipItem, data) - getBeforeBody: function() { - var lines = this._options.tooltips.callbacks.beforeBody.apply(this, arguments); - return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; - }, - - // Args are: (tooltipItem, data) - getBody: function(tooltipItems, data) { - var lines = []; - - helpers.each(tooltipItems, function(bodyItem) { - helpers.pushAllIfDefined(this._options.tooltips.callbacks.beforeLabel.call(this, bodyItem, data), lines); - helpers.pushAllIfDefined(this._options.tooltips.callbacks.label.call(this, bodyItem, data), lines); - helpers.pushAllIfDefined(this._options.tooltips.callbacks.afterLabel.call(this, bodyItem, data), lines); - }, this); - - return lines; - }, - - // Args are: (tooltipItem, data) - getAfterBody: function() { - var lines = this._options.tooltips.callbacks.afterBody.apply(this, arguments); - return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; - }, - - // Get the footer and beforeFooter and afterFooter lines - // Args are: (tooltipItem, data) - getFooter: function() { - var beforeFooter = this._options.tooltips.callbacks.beforeFooter.apply(this, arguments); - var footer = this._options.tooltips.callbacks.footer.apply(this, arguments); - var afterFooter = this._options.tooltips.callbacks.afterFooter.apply(this, arguments); - - var lines = []; - lines = pushOrConcat(lines, beforeFooter); - lines = pushOrConcat(lines, footer); - lines = pushOrConcat(lines, afterFooter); - - return lines; - }, - - getAveragePosition: function(elements) { - - if (!elements.length) { - return false; - } - - var xPositions = []; - var yPositions = []; - - helpers.each(elements, function(el) { - if (el) { - var pos = el.tooltipPosition(); - xPositions.push(pos.x); - yPositions.push(pos.y); - } - }); - - var x = 0, - y = 0; - for (var i = 0; i < xPositions.length; i++) { - x += xPositions[i]; - y += yPositions[i]; - } - - return { - x: Math.round(x / xPositions.length), - y: Math.round(y / xPositions.length) - }; - - }, - - update: function(changed) { - if (this._active.length) { - this._model.opacity = 1; - - var element = this._active[0], - labelColors = [], - tooltipPosition; - - var tooltipItems = []; - - if (this._options.tooltips.mode === 'single') { - var yScale = element._yScale || element._scale; // handle radar || polarArea charts - tooltipItems.push({ - xLabel: element._xScale ? element._xScale.getLabelForIndex(element._index, element._datasetIndex) : '', - yLabel: yScale ? yScale.getLabelForIndex(element._index, element._datasetIndex) : '', - index: element._index, - datasetIndex: element._datasetIndex - }); - tooltipPosition = this.getAveragePosition(this._active); - } else { - helpers.each(this._data.datasets, function(dataset, datasetIndex) { - if (!helpers.isDatasetVisible(dataset)) { - return; - } - var currentElement = dataset.metaData[element._index]; - if (currentElement) { - var yScale = element._yScale || element._scale; // handle radar || polarArea charts - - tooltipItems.push({ - xLabel: currentElement._xScale ? currentElement._xScale.getLabelForIndex(currentElement._index, currentElement._datasetIndex) : '', - yLabel: yScale ? yScale.getLabelForIndex(currentElement._index, currentElement._datasetIndex) : '', - index: element._index, - datasetIndex: datasetIndex - }); - } - }, null); - - helpers.each(this._active, function(active) { - if (active) { - labelColors.push({ - borderColor: active._view.borderColor, - backgroundColor: active._view.backgroundColor - }); - } - }, null); - - tooltipPosition = this.getAveragePosition(this._active); - } - - // Build the Text Lines - helpers.extend(this._model, { - title: this.getTitle(tooltipItems, this._data), - beforeBody: this.getBeforeBody(tooltipItems, this._data), - body: this.getBody(tooltipItems, this._data), - afterBody: this.getAfterBody(tooltipItems, this._data), - footer: this.getFooter(tooltipItems, this._data) - }); - - helpers.extend(this._model, { - x: Math.round(tooltipPosition.x), - y: Math.round(tooltipPosition.y), - caretPadding: helpers.getValueOrDefault(tooltipPosition.padding, 2), - labelColors: labelColors - }); - - // We need to determine alignment of - var tooltipSize = this.getTooltipSize(this._model); - this.determineAlignment(tooltipSize); // Smart Tooltip placement to stay on the canvas - - helpers.extend(this._model, this.getBackgroundPoint(this._model, tooltipSize)); - } else { - this._model.opacity = 0; - } - - if (changed && this._options.tooltips.custom) { - this._options.tooltips.custom.call(this, this._model); - } - - return this; - }, - getTooltipSize: function getTooltipSize(vm) { - var ctx = this._chart.ctx; - - var size = { - height: vm.yPadding * 2, // Tooltip Padding - width: 0 - }; - var combinedBodyLength = vm.body.length + vm.beforeBody.length + vm.afterBody.length; - - size.height += vm.title.length * vm.titleFontSize; // Title Lines - size.height += (vm.title.length - 1) * vm.titleSpacing; // Title Line Spacing - size.height += vm.title.length ? vm.titleMarginBottom : 0; // Title's bottom Margin - size.height += combinedBodyLength * vm.bodyFontSize; // Body Lines - size.height += combinedBodyLength ? (combinedBodyLength - 1) * vm.bodySpacing : 0; // Body Line Spacing - size.height += vm.footer.length ? vm.footerMarginTop : 0; // Footer Margin - size.height += vm.footer.length * (vm.footerFontSize); // Footer Lines - size.height += vm.footer.length ? (vm.footer.length - 1) * vm.footerSpacing : 0; // Footer Line Spacing - - // Width - ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily); - helpers.each(vm.title, function(line) { - size.width = Math.max(size.width, ctx.measureText(line).width); - }); - - ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); - helpers.each(vm.beforeBody.concat(vm.afterBody), function(line) { - size.width = Math.max(size.width, ctx.measureText(line).width); - }); - helpers.each(vm.body, function(line) { - size.width = Math.max(size.width, ctx.measureText(line).width + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0)); - }, this); - - ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); - helpers.each(vm.footer, function(line) { - size.width = Math.max(size.width, ctx.measureText(line).width); - }); - size.width += 2 * vm.xPadding; - - return size; - }, - determineAlignment: function determineAlignment(size) { - if (this._model.y < size.height) { - this._model.yAlign = 'top'; - } else if (this._model.y > (this._chart.height - size.height)) { - this._model.yAlign = 'bottom'; - } - - var lf, rf; // functions to determine left, right alignment - var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart - var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges - var _this = this; - var midX = (this._chartInstance.chartArea.left + this._chartInstance.chartArea.right) / 2; - var midY = (this._chartInstance.chartArea.top + this._chartInstance.chartArea.bottom) / 2; - - if (this._model.yAlign === 'center') { - lf = function(x) { - return x <= midX; - }; - rf = function(x) { - return x > midX; - }; - } else { - lf = function(x) { - return x <= (size.width / 2); - }; - rf = function(x) { - return x >= (_this._chart.width - (size.width / 2)); - }; - } - - olf = function(x) { - return x + size.width > _this._chart.width; - }; - orf = function(x) { - return x - size.width < 0; - }; - yf = function(y) { - return y <= midY ? 'top' : 'bottom'; - }; - - if (lf(this._model.x)) { - this._model.xAlign = 'left'; - - // Is tooltip too wide and goes over the right side of the chart.? - if (olf(this._model.x)) { - this._model.xAlign = 'center'; - this._model.yAlign = yf(this._model.y); - } - } else if (rf(this._model.x)) { - this._model.xAlign = 'right'; - - // Is tooltip too wide and goes outside left edge of canvas? - if (orf(this._model.x)) { - this._model.xAlign = 'center'; - this._model.yAlign = yf(this._model.y); - } - } - }, - getBackgroundPoint: function getBackgroundPoint(vm, size) { - // Background Position - var pt = { - x: vm.x, - y: vm.y - }; - - if (vm.xAlign === 'right') { - pt.x -= size.width; - } else if (vm.xAlign === 'center') { - pt.x -= (size.width / 2); - } - - if (vm.yAlign === 'top') { - pt.y += vm.caretPadding + vm.caretSize; - } else if (vm.yAlign === 'bottom') { - pt.y -= size.height + vm.caretPadding + vm.caretSize; - } else { - pt.y -= (size.height / 2); - } - - if (vm.yAlign === 'center') { - if (vm.xAlign === 'left') { - pt.x += vm.caretPadding + vm.caretSize; - } else if (vm.xAlign === 'right') { - pt.x -= vm.caretPadding + vm.caretSize; - } - } else { - if (vm.xAlign === 'left') { - pt.x -= vm.cornerRadius + vm.caretPadding; - } else if (vm.xAlign === 'right') { - pt.x += vm.cornerRadius + vm.caretPadding; - } - } - - return pt; - }, - drawCaret: function drawCaret(tooltipPoint, size, opacity, caretPadding) { - var vm = this._view; - var ctx = this._chart.ctx; - var x1, x2, x3; - var y1, y2, y3; - - if (vm.yAlign === 'center') { - // Left or right side - if (vm.xAlign === 'left') { - x1 = tooltipPoint.x; - x2 = x1 - vm.caretSize; - x3 = x1; - } else { - x1 = tooltipPoint.x + size.width; - x2 = x1 + vm.caretSize; - x3 = x1; - } - - y2 = tooltipPoint.y + (size.height / 2); - y1 = y2 - vm.caretSize; - y3 = y2 + vm.caretSize; - } else { - if (vm.xAlign === 'left') { - x1 = tooltipPoint.x + vm.cornerRadius; - x2 = x1 + vm.caretSize; - x3 = x2 + vm.caretSize; - } else if (vm.xAlign === 'right') { - x1 = tooltipPoint.x + size.width - vm.cornerRadius; - x2 = x1 - vm.caretSize; - x3 = x2 - vm.caretSize; - } else { - x2 = tooltipPoint.x + (size.width / 2); - x1 = x2 - vm.caretSize; - x3 = x2 + vm.caretSize; - } - - if (vm.yAlign === 'top') { - y1 = tooltipPoint.y; - y2 = y1 - vm.caretSize; - y3 = y1; - } else { - y1 = tooltipPoint.y + size.height; - y2 = y1 + vm.caretSize; - y3 = y1; - } - } - - var bgColor = helpers.color(vm.backgroundColor); - ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.lineTo(x3, y3); - ctx.closePath(); - ctx.fill(); - }, - drawTitle: function drawTitle(pt, vm, ctx, opacity) { - if (vm.title.length) { - ctx.textAlign = vm._titleAlign; - ctx.textBaseline = "top"; - - var titleColor = helpers.color(vm.titleColor); - ctx.fillStyle = titleColor.alpha(opacity * titleColor.alpha()).rgbString(); - ctx.font = helpers.fontString(vm.titleFontSize, vm._titleFontStyle, vm._titleFontFamily); - - helpers.each(vm.title, function(title, i) { - ctx.fillText(title, pt.x, pt.y); - pt.y += vm.titleFontSize + vm.titleSpacing; // Line Height and spacing - - if (i + 1 === vm.title.length) { - pt.y += vm.titleMarginBottom - vm.titleSpacing; // If Last, add margin, remove spacing - } - }); - } - }, - drawBody: function drawBody(pt, vm, ctx, opacity) { - ctx.textAlign = vm._bodyAlign; - ctx.textBaseline = "top"; - - var bodyColor = helpers.color(vm.bodyColor); - ctx.fillStyle = bodyColor.alpha(opacity * bodyColor.alpha()).rgbString(); - ctx.font = helpers.fontString(vm.bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); - - // Before Body - helpers.each(vm.beforeBody, function(beforeBody) { - ctx.fillText(beforeBody, pt.x, pt.y); - pt.y += vm.bodyFontSize + vm.bodySpacing; - }); - - helpers.each(vm.body, function(body, i) { - // Draw Legend-like boxes if needed - if (this._options.tooltips.mode !== 'single') { - // Fill a white rect so that colours merge nicely if the opacity is < 1 - ctx.fillStyle = helpers.color(vm.legendColorBackground).alpha(opacity).rgbaString(); - ctx.fillRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize); - - // Border - ctx.strokeStyle = helpers.color(vm.labelColors[i].borderColor).alpha(opacity).rgbaString(); - ctx.strokeRect(pt.x, pt.y, vm.bodyFontSize, vm.bodyFontSize); - - // Inner square - ctx.fillStyle = helpers.color(vm.labelColors[i].backgroundColor).alpha(opacity).rgbaString(); - ctx.fillRect(pt.x + 1, pt.y + 1, vm.bodyFontSize - 2, vm.bodyFontSize - 2); - - ctx.fillStyle = helpers.color(vm.bodyColor).alpha(opacity).rgbaString(); // Return fill style for text - } - - // Body Line - ctx.fillText(body, pt.x + (this._options.tooltips.mode !== 'single' ? (vm.bodyFontSize + 2) : 0), pt.y); - - pt.y += vm.bodyFontSize + vm.bodySpacing; - }, this); - - // After Body - helpers.each(vm.afterBody, function(afterBody) { - ctx.fillText(afterBody, pt.x, pt.y); - pt.y += vm.bodyFontSize; - }); - - pt.y -= vm.bodySpacing; // Remove last body spacing - }, - drawFooter: function drawFooter(pt, vm, ctx, opacity) { - if (vm.footer.length) { - pt.y += vm.footerMarginTop; - - ctx.textAlign = vm._footerAlign; - ctx.textBaseline = "top"; - - var footerColor = helpers.color(vm.footerColor); - ctx.fillStyle = footerColor.alpha(opacity * footerColor.alpha()).rgbString(); - ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); - - helpers.each(vm.footer, function(footer) { - ctx.fillText(footer, pt.x, pt.y); - pt.y += vm.footerFontSize + vm.footerSpacing; - }); - } - }, - draw: function draw() { - var ctx = this._chart.ctx; - var vm = this._view; - - if (vm.opacity === 0) { - return; - } - - var caretPadding = vm.caretPadding; - var tooltipSize = this.getTooltipSize(vm); - var pt = { - x: vm.x, - y: vm.y - }; - - // IE11/Edge does not like very small opacities, so snap to 0 - var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; - - if (this._options.tooltips.enabled) { - // Draw Background - var bgColor = helpers.color(vm.backgroundColor); - ctx.fillStyle = bgColor.alpha(opacity * bgColor.alpha()).rgbString(); - helpers.drawRoundedRectangle(ctx, pt.x, pt.y, tooltipSize.width, tooltipSize.height, vm.cornerRadius); - ctx.fill(); - - // Draw Caret - this.drawCaret(pt, tooltipSize, opacity, caretPadding); - - // Draw Title, Body, and Footer - pt.x += vm.xPadding; - pt.y += vm.yPadding; - - // Titles - this.drawTitle(pt, vm, ctx, opacity); - - // Body - this.drawBody(pt, vm, ctx, opacity); - - // Footer - this.drawFooter(pt, vm, ctx, opacity); - } - } - }); -}; -},{}],34:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart, moment) { - - var helpers = Chart.helpers; - - Chart.defaults.global.elements.arc = { - backgroundColor: Chart.defaults.global.defaultColor, - borderColor: "#fff", - borderWidth: 2 - }; - - Chart.elements.Arc = Chart.Element.extend({ - inLabelRange: function(mouseX) { - var vm = this._view; - - if (vm) { - return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); - } else { - return false; - } - }, - inRange: function(chartX, chartY) { - - var vm = this._view; - - if (vm) { - var pointRelativePosition = helpers.getAngleFromPoint(vm, { - x: chartX, - y: chartY - }); - - //Sanitise angle range - var startAngle = vm.startAngle; - var endAngle = vm.endAngle; - while (endAngle < startAngle) { - endAngle += 2.0 * Math.PI; - } - while (pointRelativePosition.angle > endAngle) { - pointRelativePosition.angle -= 2.0 * Math.PI; - } - while (pointRelativePosition.angle < startAngle) { - pointRelativePosition.angle += 2.0 * Math.PI; - } - - //Check if within the range of the open/close angle - var betweenAngles = (pointRelativePosition.angle >= startAngle && pointRelativePosition.angle <= endAngle), - withinRadius = (pointRelativePosition.distance >= vm.innerRadius && pointRelativePosition.distance <= vm.outerRadius); - - return (betweenAngles && withinRadius); - } else { - return false; - } - }, - tooltipPosition: function() { - var vm = this._view; - - var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2), - rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; - return { - x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), - y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) - }; - }, - draw: function() { - - var ctx = this._chart.ctx; - var vm = this._view; - - ctx.beginPath(); - - ctx.arc(vm.x, vm.y, vm.outerRadius, vm.startAngle, vm.endAngle); - - ctx.arc(vm.x, vm.y, vm.innerRadius, vm.endAngle, vm.startAngle, true); - - ctx.closePath(); - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = vm.borderWidth; - - ctx.fillStyle = vm.backgroundColor; - - ctx.fill(); - ctx.lineJoin = 'bevel'; - - if (vm.borderWidth) { - ctx.stroke(); - } - } - }); -}; +},{}],13:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Radar = function(context, config) { + config.type = 'radar'; + + return new Chart(context, config); + }; -},{}],35:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.elements.line = { - tension: 0.4, - backgroundColor: Chart.defaults.global.defaultColor, - borderWidth: 3, - borderColor: Chart.defaults.global.defaultColor, - borderCapStyle: 'butt', - borderDash: [], - borderDashOffset: 0.0, - borderJoinStyle: 'miter', - fill: true // do we fill in the area between the line and its base axis - }; - - Chart.elements.Line = Chart.Element.extend({ - lineToNextPoint: function(previousPoint, point, nextPoint, skipHandler, previousSkipHandler) { - var ctx = this._chart.ctx; - - if (point._view.skip) { - skipHandler.call(this, previousPoint, point, nextPoint); - } else if (previousPoint._view.skip) { - previousSkipHandler.call(this, previousPoint, point, nextPoint); - } else if (point._view.tension === 0) { - ctx.lineTo(point._view.x, point._view.y); - } else { - // Line between points - ctx.bezierCurveTo( - previousPoint._view.controlPointNextX, - previousPoint._view.controlPointNextY, - point._view.controlPointPreviousX, - point._view.controlPointPreviousY, - point._view.x, - point._view.y - ); - } - }, - - draw: function() { - var _this = this; - - var vm = this._view; - var ctx = this._chart.ctx; - var first = this._children[0]; - var last = this._children[this._children.length - 1]; - - function loopBackToStart(drawLineToCenter) { - if (!first._view.skip && !last._view.skip) { - // Draw a bezier line from last to first - ctx.bezierCurveTo( - last._view.controlPointNextX, - last._view.controlPointNextY, - first._view.controlPointPreviousX, - first._view.controlPointPreviousY, - first._view.x, - first._view.y - ); - } else if (drawLineToCenter) { - // Go to center - ctx.lineTo(_this._view.scaleZero.x, _this._view.scaleZero.y); - } - } - - ctx.save(); - - // If we had points and want to fill this line, do so. - if (this._children.length > 0 && vm.fill) { - // Draw the background first (so the border is always on top) - ctx.beginPath(); - - helpers.each(this._children, function(point, index) { - var previous = helpers.previousItem(this._children, index); - var next = helpers.nextItem(this._children, index); - - // First point moves to it's starting position no matter what - if (index === 0) { - if (this._loop) { - ctx.moveTo(vm.scaleZero.x, vm.scaleZero.y); - } else { - ctx.moveTo(point._view.x, vm.scaleZero); - } - - if (point._view.skip) { - if (!this._loop) { - ctx.moveTo(next._view.x, this._view.scaleZero); - } - } else { - ctx.lineTo(point._view.x, point._view.y); - } - } else { - this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) { - if (this._loop) { - // Go to center - ctx.lineTo(this._view.scaleZero.x, this._view.scaleZero.y); - } else { - ctx.lineTo(previousPoint._view.x, this._view.scaleZero); - ctx.moveTo(nextPoint._view.x, this._view.scaleZero); - } - }, function(previousPoint, point) { - // If we skipped the last point, draw a line to ourselves so that the fill is nice - ctx.lineTo(point._view.x, point._view.y); - }); - } - }, this); - - // For radial scales, loop back around to the first point - if (this._loop) { - loopBackToStart(true); - } else { - //Round off the line by going to the base of the chart, back to the start, then fill. - ctx.lineTo(this._children[this._children.length - 1]._view.x, vm.scaleZero); - ctx.lineTo(this._children[0]._view.x, vm.scaleZero); - } - - ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor; - ctx.closePath(); - ctx.fill(); - } - - // Now draw the line between all the points with any borders - ctx.lineCap = vm.borderCapStyle || Chart.defaults.global.elements.line.borderCapStyle; - - // IE 9 and 10 do not support line dash - if (ctx.setLineDash) { - ctx.setLineDash(vm.borderDash || Chart.defaults.global.elements.line.borderDash); - } - - ctx.lineDashOffset = vm.borderDashOffset || Chart.defaults.global.elements.line.borderDashOffset; - ctx.lineJoin = vm.borderJoinStyle || Chart.defaults.global.elements.line.borderJoinStyle; - ctx.lineWidth = vm.borderWidth || Chart.defaults.global.elements.line.borderWidth; - ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor; - ctx.beginPath(); - - helpers.each(this._children, function(point, index) { - var previous = helpers.previousItem(this._children, index); - var next = helpers.nextItem(this._children, index); - - if (index === 0) { - ctx.moveTo(point._view.x, point._view.y); - } else { - this.lineToNextPoint(previous, point, next, function(previousPoint, point, nextPoint) { - ctx.moveTo(nextPoint._view.x, nextPoint._view.y); - }, function(previousPoint, point) { - // If we skipped the last point, move up to our point preventing a line from being drawn - ctx.moveTo(point._view.x, point._view.y); - }); - } - }, this); - - if (this._loop && this._children.length > 0) { - loopBackToStart(); - } - - ctx.stroke(); - ctx.restore(); - } - }); }; -},{}],36:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.elements.point = { - radius: 3, - pointStyle: 'circle', - backgroundColor: Chart.defaults.global.defaultColor, - borderWidth: 1, - borderColor: Chart.defaults.global.defaultColor, - // Hover - hitRadius: 1, - hoverRadius: 4, - hoverBorderWidth: 1 - }; - - - Chart.elements.Point = Chart.Element.extend({ - inRange: function(mouseX, mouseY) { - var vm = this._view; - - if (vm) { - var hoverRange = vm.hitRadius + vm.radius; - return ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(hoverRange, 2)); - } else { - return false; - } - }, - inLabelRange: function(mouseX) { - var vm = this._view; - - if (vm) { - return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)); - } else { - return false; - } - }, - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y, - padding: vm.radius + vm.borderWidth - }; - }, - draw: function() { - - var vm = this._view; - var ctx = this._chart.ctx; - - - if (vm.skip) { - return; - } - - if (typeof vm.pointStyle === 'object' && ((vm.pointStyle.toString() === '[object HTMLImageElement]') || (vm.pointStyle.toString() === '[object HTMLCanvasElement]'))) { - ctx.drawImage(vm.pointStyle, vm.x - vm.pointStyle.width / 2, vm.y - vm.pointStyle.height / 2); - return; - } - - if (!isNaN(vm.radius) && vm.radius > 0) { - - ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor; - ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, Chart.defaults.global.elements.point.borderWidth); - - ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor; - - var radius = vm.radius; - - var xOffset; - var yOffset; - - switch (vm.pointStyle) { - // Default includes circle - default: ctx.beginPath(); - ctx.arc(vm.x, vm.y, radius, 0, Math.PI * 2); - ctx.closePath(); - ctx.fill(); - break; - case 'triangle': - ctx.beginPath(); - var edgeLength = 3 * radius / Math.sqrt(3); - var height = edgeLength * Math.sqrt(3) / 2; - ctx.moveTo(vm.x - edgeLength / 2, vm.y + height / 3); - ctx.lineTo(vm.x + edgeLength / 2, vm.y + height / 3); - ctx.lineTo(vm.x, vm.y - 2 * height / 3); - ctx.closePath(); - ctx.fill(); - break; - case 'rect': - ctx.fillRect(vm.x - 1 / Math.SQRT2 * radius, vm.y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius); - ctx.strokeRect(vm.x - 1 / Math.SQRT2 * radius, vm.y - 1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius); - break; - case 'rectRot': - ctx.translate(vm.x, vm.y); - ctx.rotate(Math.PI / 4); - ctx.fillRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius); - ctx.strokeRect(-1 / Math.SQRT2 * radius, -1 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius, 2 / Math.SQRT2 * radius); - ctx.setTransform(1, 0, 0, 1, 0, 0); - break; - case 'cross': - ctx.beginPath(); - ctx.moveTo(vm.x, vm.y + radius); - ctx.lineTo(vm.x, vm.y - radius); - ctx.moveTo(vm.x - radius, vm.y); - ctx.lineTo(vm.x + radius, vm.y); - ctx.closePath(); - break; - case 'crossRot': - ctx.beginPath(); - xOffset = Math.cos(Math.PI / 4) * radius; - yOffset = Math.sin(Math.PI / 4) * radius; - ctx.moveTo(vm.x - xOffset, vm.y - yOffset); - ctx.lineTo(vm.x + xOffset, vm.y + yOffset); - ctx.moveTo(vm.x - xOffset, vm.y + yOffset); - ctx.lineTo(vm.x + xOffset, vm.y - yOffset); - ctx.closePath(); - break; - case 'star': - ctx.beginPath(); - ctx.moveTo(vm.x, vm.y + radius); - ctx.lineTo(vm.x, vm.y - radius); - ctx.moveTo(vm.x - radius, vm.y); - ctx.lineTo(vm.x + radius, vm.y); - xOffset = Math.cos(Math.PI / 4) * radius; - yOffset = Math.sin(Math.PI / 4) * radius; - ctx.moveTo(vm.x - xOffset, vm.y - yOffset); - ctx.lineTo(vm.x + xOffset, vm.y + yOffset); - ctx.moveTo(vm.x - xOffset, vm.y + yOffset); - ctx.lineTo(vm.x + xOffset, vm.y - yOffset); - ctx.closePath(); - break; - case 'line': - ctx.beginPath(); - ctx.moveTo(vm.x - radius, vm.y); - ctx.lineTo(vm.x + radius, vm.y); - ctx.closePath(); - break; - case 'dash': - ctx.beginPath(); - ctx.moveTo(vm.x, vm.y); - ctx.lineTo(vm.x + radius, vm.y); - ctx.closePath(); - break; - } - - ctx.stroke(); - } - } - }); + +},{}],14:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var defaultConfig = { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // scatter should not use a category axis + position: 'bottom', + id: 'x-axis-1' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-1' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(tooltipItem) { + return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; + } + } + } + }; + + // Register the default config for this type + Chart.defaults.scatter = defaultConfig; + + // Scatter charts use line controllers + Chart.controllers.scatter = Chart.controllers.line; + + Chart.Scatter = function(context, config) { + config.type = 'scatter'; + return new Chart(context, config); + }; + }; -},{}],37:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - Chart.defaults.global.elements.rectangle = { - backgroundColor: Chart.defaults.global.defaultColor, - borderWidth: 0, - borderColor: Chart.defaults.global.defaultColor, - borderSkipped: 'bottom' - }; - - Chart.elements.Rectangle = Chart.Element.extend({ - draw: function() { - - var ctx = this._chart.ctx; - var vm = this._view; - - var halfWidth = vm.width / 2, - leftX = vm.x - halfWidth, - rightX = vm.x + halfWidth, - top = vm.base - (vm.base - vm.y), - halfStroke = vm.borderWidth / 2; - - // Canvas doesn't allow us to stroke inside the width so we can - // adjust the sizes to fit if we're setting a stroke on the line - if (vm.borderWidth) { - leftX += halfStroke; - rightX -= halfStroke; - top += halfStroke; - } - - ctx.beginPath(); - - ctx.fillStyle = vm.backgroundColor; - ctx.strokeStyle = vm.borderColor; - ctx.lineWidth = vm.borderWidth; - - // Corner points, from bottom-left to bottom-right clockwise - // | 1 2 | - // | 0 3 | - var corners = [ - [leftX, vm.base], - [leftX, top], - [rightX, top], - [rightX, vm.base] - ]; - - // Find first (starting) corner with fallback to 'bottom' - var borders = ['bottom', 'left', 'top', 'right']; - var startCorner = borders.indexOf(vm.borderSkipped, 0); - if (startCorner === -1) - startCorner = 0; - - function cornerAt(index) { - return corners[(startCorner + index) % 4]; - } - - // Draw rectangle from 'startCorner' - ctx.moveTo.apply(ctx, cornerAt(0)); - for (var i = 1; i < 4; i++) - ctx.lineTo.apply(ctx, cornerAt(i)); - - ctx.fill(); - if (vm.borderWidth) { - ctx.stroke(); - } - }, - height: function() { - var vm = this._view; - return vm.base - vm.y; - }, - inRange: function(mouseX, mouseY) { - var vm = this._view; - var inRange = false; - - if (vm) { - if (vm.y < vm.base) { - inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.y && mouseY <= vm.base); - } else { - inRange = (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2) && (mouseY >= vm.base && mouseY <= vm.y); - } - } - - return inRange; - }, - inLabelRange: function(mouseX) { - var vm = this._view; - - if (vm) { - return (mouseX >= vm.x - vm.width / 2 && mouseX <= vm.x + vm.width / 2); - } else { - return false; - } - }, - tooltipPosition: function() { - var vm = this._view; - return { - x: vm.x, - y: vm.y - }; - } - }); - + +},{}],15:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.bar = { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + + // Specific to Bar Controller + categoryPercentage: 0.8, + barPercentage: 0.9, + + // grid line settings + gridLines: { + offsetGridLines: true + } + }], + yAxes: [{ + type: 'linear' + }] + } + }; + + Chart.controllers.bar = Chart.DatasetController.extend({ + + dataElementType: Chart.elements.Rectangle, + + initialize: function(chart, datasetIndex) { + Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex); + + // Use this to indicate that this is a bar dataset. + this.getMeta().bar = true; + }, + + // Get the number of datasets that display bars. We use this to correctly calculate the bar width + getBarCount: function() { + var me = this; + var barCount = 0; + helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) { + var meta = me.chart.getDatasetMeta(datasetIndex); + if (meta.bar && me.chart.isDatasetVisible(datasetIndex)) { + ++barCount; + } + }, me); + return barCount; + }, + + update: function(reset) { + var me = this; + helpers.each(me.getMeta().data, function(rectangle, index) { + me.updateElement(rectangle, index, reset); + }, me); + }, + + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var scaleBase = yScale.getBasePixel(); + var rectangleElementOptions = me.chart.options.elements.rectangle; + var custom = rectangle.custom || {}; + var dataset = me.getDataset(); + + rectangle._xScale = xScale; + rectangle._yScale = yScale; + rectangle._datasetIndex = me.index; + rectangle._index = index; + + var ruler = me.getRuler(index); + rectangle._model = { + x: me.calculateBarX(index, me.index, ruler), + y: reset ? scaleBase : me.calculateBarY(index, me.index), + + // Tooltip + label: me.chart.data.labels[index], + datasetLabel: dataset.label, + + // Appearance + base: reset ? scaleBase : me.calculateBarBase(me.index, index), + width: me.calculateBarWidth(ruler), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), + borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, + borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) + }; + + rectangle.pivot(); + }, + + calculateBarBase: function(datasetIndex, index) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var base = 0; + + if (yScale.options.stacked) { + var chart = me.chart; + var datasets = chart.data.datasets; + var value = Number(datasets[datasetIndex].data[index]); + + for (var i = 0; i < datasetIndex; i++) { + var currentDs = datasets[i]; + var currentDsMeta = chart.getDatasetMeta(i); + if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { + var currentVal = Number(currentDs.data[index]); + base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); + } + } + + return yScale.getPixelForValue(base); + } + + return yScale.getBasePixel(); + }, + + getRuler: function(index) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var datasetCount = me.getBarCount(); + + var tickWidth; + + if (xScale.options.type === 'category') { + tickWidth = xScale.getPixelForTick(index + 1) - xScale.getPixelForTick(index); + } else { + // Average width + tickWidth = xScale.width / xScale.ticks.length; + } + var categoryWidth = tickWidth * xScale.options.categoryPercentage; + var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2; + var fullBarWidth = categoryWidth / datasetCount; + + if (xScale.ticks.length !== me.chart.data.labels.length) { + var perc = xScale.ticks.length / me.chart.data.labels.length; + fullBarWidth = fullBarWidth * perc; + } + + var barWidth = fullBarWidth * xScale.options.barPercentage; + var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage); + + return { + datasetCount: datasetCount, + tickWidth: tickWidth, + categoryWidth: categoryWidth, + categorySpacing: categorySpacing, + fullBarWidth: fullBarWidth, + barWidth: barWidth, + barSpacing: barSpacing + }; + }, + + calculateBarWidth: function(ruler) { + var xScale = this.getScaleForId(this.getMeta().xAxisID); + if (xScale.options.barThickness) { + return xScale.options.barThickness; + } + return xScale.options.stacked ? ruler.categoryWidth : ruler.barWidth; + }, + + // Get bar index from the given dataset index accounting for the fact that not all bars are visible + getBarIndex: function(datasetIndex) { + var barIndex = 0; + var meta, j; + + for (j = 0; j < datasetIndex; ++j) { + meta = this.chart.getDatasetMeta(j); + if (meta.bar && this.chart.isDatasetVisible(j)) { + ++barIndex; + } + } + + return barIndex; + }, + + calculateBarX: function(index, datasetIndex, ruler) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var barIndex = me.getBarIndex(datasetIndex); + var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); + leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0; + + if (xScale.options.stacked) { + return leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing; + } + + return leftTick + + (ruler.barWidth / 2) + + ruler.categorySpacing + + (ruler.barWidth * barIndex) + + (ruler.barSpacing / 2) + + (ruler.barSpacing * barIndex); + }, + + calculateBarY: function(index, datasetIndex) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var value = Number(me.getDataset().data[index]); + + if (yScale.options.stacked) { + + var sumPos = 0, + sumNeg = 0; + + for (var i = 0; i < datasetIndex; i++) { + var ds = me.chart.data.datasets[i]; + var dsMeta = me.chart.getDatasetMeta(i); + if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i)) { + var stackedVal = Number(ds.data[index]); + if (stackedVal < 0) { + sumNeg += stackedVal || 0; + } else { + sumPos += stackedVal || 0; + } + } + } + + if (value < 0) { + return yScale.getPixelForValue(sumNeg + value); + } + return yScale.getPixelForValue(sumPos + value); + } + + return yScale.getPixelForValue(value); + }, + + draw: function(ease) { + var me = this; + var easingDecimal = ease || 1; + var metaData = me.getMeta().data; + var dataset = me.getDataset(); + var i, len; + + for (i = 0, len = metaData.length; i < len; ++i) { + var d = dataset.data[i]; + if (d !== null && d !== undefined && !isNaN(d)) { + metaData[i].transition(easingDecimal).draw(); + } + } + }, + + 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.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(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.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); + } + + }); + + + // including horizontalBar in the bar file, instead of a file of its own + // it extends bar (like pie extends doughnut) + Chart.defaults.horizontalBar = { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom' + }], + yAxes: [{ + position: 'left', + type: 'category', + + // Specific to Horizontal Bar Controller + categoryPercentage: 0.8, + barPercentage: 0.9, + + // grid line settings + gridLines: { + offsetGridLines: true + } + }] + }, + elements: { + rectangle: { + borderSkipped: 'left' + } + }, + tooltips: { + callbacks: { + title: function(tooltipItems, data) { + // Pick first xLabel for now + var title = ''; + + if (tooltipItems.length > 0) { + if (tooltipItems[0].yLabel) { + title = tooltipItems[0].yLabel; + } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { + title = data.labels[tooltipItems[0].index]; + } + } + + return title; + }, + label: function(tooltipItem, data) { + var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; + return datasetLabel + ': ' + tooltipItem.xLabel; + } + } + } + }; + + Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ + updateElement: function(rectangle, index, reset) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var scaleBase = xScale.getBasePixel(); + var custom = rectangle.custom || {}; + var dataset = me.getDataset(); + var rectangleElementOptions = me.chart.options.elements.rectangle; + + rectangle._xScale = xScale; + rectangle._yScale = yScale; + rectangle._datasetIndex = me.index; + rectangle._index = index; + + var ruler = me.getRuler(index); + rectangle._model = { + x: reset ? scaleBase : me.calculateBarX(index, me.index), + y: me.calculateBarY(index, me.index, ruler), + + // Tooltip + label: me.chart.data.labels[index], + datasetLabel: dataset.label, + + // Appearance + base: reset ? scaleBase : me.calculateBarBase(me.index, index), + height: me.calculateBarHeight(ruler), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor), + borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped, + borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth) + }; + rectangle.draw = function() { + var ctx = this._chart.ctx; + var vm = this._view; + + var halfHeight = vm.height / 2, + topY = vm.y - halfHeight, + bottomY = vm.y + halfHeight, + right = vm.base - (vm.base - vm.x), + halfStroke = vm.borderWidth / 2; + + // Canvas doesn't allow us to stroke inside the width so we can + // adjust the sizes to fit if we're setting a stroke on the line + if (vm.borderWidth) { + topY += halfStroke; + bottomY -= halfStroke; + right += halfStroke; + } + + ctx.beginPath(); + + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; + + // Corner points, from bottom-left to bottom-right clockwise + // | 1 2 | + // | 0 3 | + var corners = [ + [vm.base, bottomY], + [vm.base, topY], + [right, topY], + [right, bottomY] + ]; + + // Find first (starting) corner with fallback to 'bottom' + var borders = ['bottom', 'left', 'top', 'right']; + var startCorner = borders.indexOf(vm.borderSkipped, 0); + if (startCorner === -1) { + startCorner = 0; + } + + function cornerAt(cornerIndex) { + return corners[(startCorner + cornerIndex) % 4]; + } + + // Draw rectangle from 'startCorner' + ctx.moveTo.apply(ctx, cornerAt(0)); + for (var i = 1; i < 4; i++) { + ctx.lineTo.apply(ctx, cornerAt(i)); + } + + ctx.fill(); + if (vm.borderWidth) { + ctx.stroke(); + } + }; + + rectangle.pivot(); + }, + + calculateBarBase: function(datasetIndex, index) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var base = 0; + + if (xScale.options.stacked) { + var chart = me.chart; + var datasets = chart.data.datasets; + var value = Number(datasets[datasetIndex].data[index]); + + for (var i = 0; i < datasetIndex; i++) { + var currentDs = datasets[i]; + var currentDsMeta = chart.getDatasetMeta(i); + if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i)) { + var currentVal = Number(currentDs.data[index]); + base += value < 0 ? Math.min(currentVal, 0) : Math.max(currentVal, 0); + } + } + + return xScale.getPixelForValue(base); + } + + return xScale.getBasePixel(); + }, + + getRuler: function(index) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var datasetCount = me.getBarCount(); + + var tickHeight; + if (yScale.options.type === 'category') { + tickHeight = yScale.getPixelForTick(index + 1) - yScale.getPixelForTick(index); + } else { + // Average width + tickHeight = yScale.width / yScale.ticks.length; + } + var categoryHeight = tickHeight * yScale.options.categoryPercentage; + var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2; + var fullBarHeight = categoryHeight / datasetCount; + + if (yScale.ticks.length !== me.chart.data.labels.length) { + var perc = yScale.ticks.length / me.chart.data.labels.length; + fullBarHeight = fullBarHeight * perc; + } + + var barHeight = fullBarHeight * yScale.options.barPercentage; + var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage); + + return { + datasetCount: datasetCount, + tickHeight: tickHeight, + categoryHeight: categoryHeight, + categorySpacing: categorySpacing, + fullBarHeight: fullBarHeight, + barHeight: barHeight, + barSpacing: barSpacing + }; + }, + + calculateBarHeight: function(ruler) { + var me = this; + var yScale = me.getScaleForId(me.getMeta().yAxisID); + if (yScale.options.barThickness) { + return yScale.options.barThickness; + } + return yScale.options.stacked ? ruler.categoryHeight : ruler.barHeight; + }, + + calculateBarX: function(index, datasetIndex) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var value = Number(me.getDataset().data[index]); + + if (xScale.options.stacked) { + + var sumPos = 0, + sumNeg = 0; + + for (var i = 0; i < datasetIndex; i++) { + var ds = me.chart.data.datasets[i]; + var dsMeta = me.chart.getDatasetMeta(i); + if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i)) { + var stackedVal = Number(ds.data[index]); + if (stackedVal < 0) { + sumNeg += stackedVal || 0; + } else { + sumPos += stackedVal || 0; + } + } + } + + if (value < 0) { + return xScale.getPixelForValue(sumNeg + value); + } + return xScale.getPixelForValue(sumPos + value); + } + + return xScale.getPixelForValue(value); + }, + + calculateBarY: function(index, datasetIndex, ruler) { + var me = this; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var barIndex = me.getBarIndex(datasetIndex); + var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo); + topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0; + + if (yScale.options.stacked) { + return topTick + (ruler.categoryHeight / 2) + ruler.categorySpacing; + } + + return topTick + + (ruler.barHeight / 2) + + ruler.categorySpacing + + (ruler.barHeight * barIndex) + + (ruler.barSpacing / 2) + + (ruler.barSpacing * barIndex); + } + }); }; -},{}],38:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - // Default config for a category scale - var defaultConfig = { - position: "bottom" - }; - - var DatasetScale = Chart.Scale.extend({ - // Implement this so that - determineDataLimits: function() { - this.minIndex = 0; - this.maxIndex = this.chart.data.labels.length; - var findIndex; - - if (this.options.ticks.min !== undefined) { - // user specified min value - findIndex = helpers.indexOf(this.chart.data.labels, this.options.ticks.min); - this.minIndex = findIndex !== -1 ? findIndex : this.minIndex; - } - - if (this.options.ticks.max !== undefined) { - // user specified max value - findIndex = helpers.indexOf(this.chart.data.labels, this.options.ticks.max); - this.maxIndex = findIndex !== -1 ? findIndex : this.maxIndex; - } - - this.min = this.chart.data.labels[this.minIndex]; - this.max = this.chart.data.labels[this.maxIndex]; - }, - - buildTicks: function(index) { - // If we are viewing some subset of labels, slice the original array - this.ticks = (this.minIndex === 0 && this.maxIndex === this.chart.data.labels.length) ? this.chart.data.labels : this.chart.data.labels.slice(this.minIndex, this.maxIndex + 1); - }, - - getLabelForIndex: function(index, datasetIndex) { - return this.ticks[index]; - }, - - // Used to get data value locations. Value can either be an index or a numerical value - getPixelForValue: function(value, index, datasetIndex, includeOffset) { - // 1 is added because we need the length but we have the indexes - var offsetAmt = Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1); - - if (this.isHorizontal()) { - var innerWidth = this.width - (this.paddingLeft + this.paddingRight); - var valueWidth = innerWidth / offsetAmt; - var widthOffset = (valueWidth * (index - this.minIndex)) + this.paddingLeft; - - if (this.options.gridLines.offsetGridLines && includeOffset) { - widthOffset += (valueWidth / 2); - } - - return this.left + Math.round(widthOffset); - } else { - var innerHeight = this.height - (this.paddingTop + this.paddingBottom); - var valueHeight = innerHeight / offsetAmt; - var heightOffset = (valueHeight * (index - this.minIndex)) + this.paddingTop; - - if (this.options.gridLines.offsetGridLines && includeOffset) { - heightOffset += (valueHeight / 2); - } - - return this.top + Math.round(heightOffset); - } - }, - getPixelForTick: function(index, includeOffset) { - return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset); - } - }); - - Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig); - + +},{}],16:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.bubble = { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(tooltipItem, data) { + var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; + var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')'; + } + } + } + }; + + Chart.controllers.bubble = Chart.DatasetController.extend({ + + dataElementType: Chart.elements.Point, + + 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); + }); + }, + + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + + var custom = point.custom || {}; + var dataset = me.getDataset(); + var data = dataset.data[index]; + var pointElementOptions = me.chart.options.elements.point; + var dsIndex = me.index; + + helpers.extend(point, { + // Utility + _xScale: xScale, + _yScale: yScale, + _datasetIndex: dsIndex, + _index: index, + + // Desired view properties + _model: { + x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), + y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), + // Appearance + radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), + + // Tooltip + hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) + } + }); + + // Trick to reset the styles of the point + Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); + + var model = point._model; + model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); + + point.pivot(); + }, + + getRadius: function(value) { + return value.r || this.chart.options.elements.point.radius; + }, + + setHoverStyle: function(point) { + var me = this; + Chart.DatasetController.prototype.setHoverStyle.call(me, point); + + // Radius + var dataset = me.chart.data.datasets[point._datasetIndex]; + var index = point._index; + var custom = point.custom || {}; + var model = point._model; + model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); + }, + + removeHoverStyle: function(point) { + var me = this; + Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); + + var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; + var custom = point.custom || {}; + var model = point._model; + + model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); + } + }); }; -},{}],39:[function(require,module,exports){ -"use strict"; - -module.exports = function(Chart) { - - var helpers = Chart.helpers; - - var defaultConfig = { - position: "left", - ticks: { - callback: function(tickValue, index, ticks) { - // If we have lots of ticks, don't use the ones - var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; - - // If we have a number like 2.5 as the delta, figure out how many decimal places we need - if (Math.abs(delta) > 1) { - if (tickValue !== Math.floor(tickValue)) { - // not an integer - delta = tickValue - Math.floor(tickValue); - } - } - - var logDelta = helpers.log10(Math.abs(delta)); - var tickString = ''; - - if (tickValue !== 0) { - var numDecimal = -1 * Math.floor(logDelta); - numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places - tickString = tickValue.toFixed(numDecimal); - } else { - tickString = '0'; // never show decimal places for 0 - } - - return tickString; - } - } - }; - - var LinearScale = Chart.Scale.extend({ - determineDataLimits: function() { - // First Calculate the range - this.min = null; - this.max = null; - - if (this.options.stacked) { - var valuesPerType = {}; - var hasPositiveValues = false; - var hasNegativeValues = false; - - helpers.each(this.chart.data.datasets, function(dataset) { - if (valuesPerType[dataset.type] === undefined) { - valuesPerType[dataset.type] = { - positiveValues: [], - negativeValues: [] - }; - } - - // Store these per type - var positiveValues = valuesPerType[dataset.type].positiveValues; - var negativeValues = valuesPerType[dataset.type].negativeValues; - - if (helpers.isDatasetVisible(dataset) && (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id)) { - helpers.each(dataset.data, function(rawValue, index) { - - var value = +this.getRightValue(rawValue); - if (isNaN(value)) { - return; - } - - positiveValues[index] = positiveValues[index] || 0; - negativeValues[index] = negativeValues[index] || 0; - - if (this.options.relativePoints) { - positiveValues[index] = 100; - } else { - if (value < 0) { - hasNegativeValues = true; - negativeValues[index] += value; - } else { - hasPositiveValues = true; - positiveValues[index] += value; - } - } - }, this); - } - }, this); - - helpers.each(valuesPerType, function(valuesForType) { - var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); - var minVal = helpers.min(values); - var maxVal = helpers.max(values); - this.min = this.min === null ? minVal : Math.min(this.min, minVal); - this.max = this.max === null ? maxVal : Math.max(this.max, maxVal); - }, this); - - } else { - helpers.each(this.chart.data.datasets, function(dataset) { - if (helpers.isDatasetVisible(dataset) && (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id)) { - helpers.each(dataset.data, function(rawValue, index) { - var value = +this.getRightValue(rawValue); - if (isNaN(value)) { - return; - } - - if (this.min === null) { - this.min = value; - } else if (value < this.min) { - this.min = value; - } - - if (this.max === null) { - this.max = value; - } else if (value > this.max) { - this.max = value; - } - }, this); - } - }, this); - } - - // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, - // do nothing since that would make the chart weird. If the user really wants a weird chart - // axis, they can manually override it - if (this.options.ticks.beginAtZero) { - var minSign = helpers.sign(this.min); - var maxSign = helpers.sign(this.max); - - if (minSign < 0 && maxSign < 0) { - // move the top up to 0 - this.max = 0; - } else if (minSign > 0 && maxSign > 0) { - // move the botttom down to 0 - this.min = 0; - } - } - - if (this.options.ticks.min !== undefined) { - this.min = this.options.ticks.min; - } else if (this.options.ticks.suggestedMin !== undefined) { - this.min = Math.min(this.min, this.options.ticks.suggestedMin); - } - - if (this.options.ticks.max !== undefined) { - this.max = this.options.ticks.max; - } else if (this.options.ticks.suggestedMax !== undefined) { - this.max = Math.max(this.max, this.options.ticks.suggestedMax); - } - - if (this.min === this.max) { - this.min--; - this.max++; - } - }, - buildTicks: function() { - - // Then calulate the ticks - this.ticks = []; - - // Figure out what the max number of ticks we can support it is based on the size of - // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 - // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on - // the graph - - var maxTicks; - - if (this.isHorizontal()) { - maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11, Math.ceil(this.width / 50)); - } else { - // The factor of 2 used to scale the font size has been experimentally determined. - var tickFontSize = helpers.getValueOrDefault(this.options.ticks.fontSize, Chart.defaults.global.defaultFontSize); - maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11, Math.ceil(this.height / (2 * tickFontSize))); - } - - // Make sure we always have at least 2 ticks - maxTicks = Math.max(2, maxTicks); - - // To get a "nice" value for the tick spacing, we will use the appropriately named - // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks - // for details. - - var spacing; - var fixedStepSizeSet = (this.options.ticks.fixedStepSize && this.options.ticks.fixedStepSize > 0) || (this.options.ticks.stepSize && this.options.ticks.stepSize > 0); - if (fixedStepSizeSet) { - spacing = helpers.getValueOrDefault(this.options.ticks.fixedStepSize, this.options.ticks.stepSize); - } else { - var niceRange = helpers.niceNum(this.max - this.min, false); - spacing = helpers.niceNum(niceRange / (maxTicks - 1), true); - } - var niceMin = Math.floor(this.min / spacing) * spacing; - var niceMax = Math.ceil(this.max / spacing) * spacing; - var numSpaces = (niceMax - niceMin) / spacing; - - // If very close to our rounded value, use it. - if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { - numSpaces = Math.round(numSpaces); - } else { - numSpaces = Math.ceil(numSpaces); - } - - // Put the values into the ticks array - this.ticks.push(this.options.ticks.min !== undefined ? this.options.ticks.min : niceMin); - for (var j = 1; j < numSpaces; ++j) { - this.ticks.push(niceMin + (j * spacing)); - } - this.ticks.push(this.options.ticks.max !== undefined ? this.options.ticks.max : niceMax); - - if (this.options.position === "left" || this.options.position === "right") { - // We are in a vertical orientation. The top value is the highest. So reverse the array - this.ticks.reverse(); - } - - // At this point, we need to update our max and min given the tick values since we have expanded the - // range of the scale - this.max = helpers.max(this.ticks); - this.min = helpers.min(this.ticks); - - if (this.options.ticks.reverse) { - this.ticks.reverse(); - - this.start = this.max; - this.end = this.min; - } else { - this.start = this.min; - this.end = this.max; - } - }, - getLabelForIndex: function(index, datasetIndex) { - return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); - }, - convertTicksToLabels: function() { - this.ticksAsNumbers = this.ticks.slice(); - this.zeroLineIndex = this.ticks.indexOf(0); - - Chart.Scale.prototype.convertTicksToLabels.call(this); - }, - // Utils - getPixelForValue: function(value, index, datasetIndex, includeOffset) { - // This must be called after fit has been run so that - // this.left, this.top, this.right, and this.bottom have been defined - var rightValue = +this.getRightValue(value); - var pixel; - var range = this.end - this.start; - - if (this.isHorizontal()) { - var innerWidth = this.width - (this.paddingLeft + this.paddingRight); - pixel = this.left + (innerWidth / range * (rightValue - this.start)); - return Math.round(pixel + this.paddingLeft); - } else { - var innerHeight = this.height - (this.paddingTop + this.paddingBottom); - pixel = (this.bottom - this.paddingBottom) - (innerHeight / range * (rightValue - this.start)); - return Math.round(pixel); - } - }, - getPixelForTick: function(index, includeOffset) { - return this.getPixelForValue(this.ticksAsNumbers[index], null, null, includeOffset); - } - }); - Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig); - + +},{}],17:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers, + defaults = Chart.defaults; + + defaults.doughnut = { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + aspectRatio: 1, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var text = []; + text.push(''); + 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 getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(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.pie = helpers.clone(defaults.doughnut); + helpers.extend(defaults.pie, { + cutoutPercentage: 0 + }); + + + Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ + + dataElementType: Chart.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, + chartArea = chart.chartArea, + opts = chart.options, + arcOpts = opts.elements.arc, + availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, + availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, + minSize = Math.min(availableWidth, availableHeight), + offset = { + x: 0, + y: 0 + }, + meta = me.getMeta(), + cutoutPercentage = opts.cutoutPercentage, + 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 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); + var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); + var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); + var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); + var cutout = 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) : 1, 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 = me.outerRadius - chart.radiusLength; + + helpers.each(meta.data, function(arc, index) { + me.updateElement(arc, index, reset); + }); + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart, + chartArea = chart.chartArea, + opts = chart.options, + animationOpts = opts.animation, + centerX = (chartArea.left + chartArea.right) / 2, + centerY = (chartArea.top + chartArea.bottom) / 2, + startAngle = opts.rotation, // non reset case handled later + endAngle = opts.rotation, // non reset case handled later + dataset = me.getDataset(), + circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), + innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, + outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, + valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + + 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(elements) { + var max = 0, + index = this.index, + length = elements.length, + borderWidth, + hoverWidth; + + for (var i = 0; i < length; i++) { + borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; + hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + return max; + } + }); +}; + +},{}],18:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.line = { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } + }; + + function lineEnabled(dataset, options) { + return helpers.getValueOrDefault(dataset.showLine, options.showLines); + } + + Chart.controllers.line = Chart.DatasetController.extend({ + + datasetElementType: Chart.elements.Line, + + dataElementType: Chart.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.getValueOrDefault(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.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), + cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), + // Scale + scaleTop: scale.top, + scaleBottom: scale.bottom, + scaleZero: scale.getBasePixel() + }; + + line.pivot(); + } + + // Update Points + for (i=0, ilen=points.length; i'); + + 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('
  • '); + if (labels[i]) { + text.push(labels[i]); + } + text.push('
  • '); + } + } + + text.push(''); + 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 getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(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(tooltipItem, data) { + return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; + } + } + } + }; + + Chart.controllers.polarArea = Chart.DatasetController.extend({ + + dataElementType: Chart.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 getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; + 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: getValueAtIndexOrDefault(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; + } + }); +}; + +},{}],20:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.radar = { + aspectRatio: 1, + scale: { + type: 'radialLinear' + }, + elements: { + line: { + tension: 0 // no bezier in radar + } + } + }; + + Chart.controllers.radar = Chart.DatasetController.extend({ + + datasetElementType: Chart.elements.Line, + + dataElementType: Chart.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, + // Data + _children: points, + _loop: true, + // Model + _model: { + // Appearance + tension: custom.tension ? custom.tension : helpers.getValueOrDefault(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), + + // Scale + scaleTop: scale.top, + scaleBottom: scale.bottom, + scaleZero: scale.getBasePosition() + } + }); + + 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]); + + 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.getValueOrDefault(dataset.tension, me.chart.options.elements.line.tension), + radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), + borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), + pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), + + // Tooltip + hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, 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(); + }); + }, + + draw: function(ease) { + var meta = this.getMeta(); + var easingDecimal = ease || 1; + + // Transition Point Locations + helpers.each(meta.data, function(point) { + point.transition(easingDecimal); + }); + + // Transition and Draw the line + meta.dataset.transition(easingDecimal).draw(); + + // Draw the points + helpers.each(meta.data, function(point) { + point.draw(); + }); + }, + + 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.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(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.getValueAtIndexOrDefault(dataset.radius, index, pointElementOptions.radius); + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); + } + }); +}; + +},{}],21:[function(require,module,exports){ +/* global window: false */ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + Chart.defaults.global.animation = { + duration: 1000, + easing: 'easeOutQuart', + onProgress: helpers.noop, + onComplete: helpers.noop + }; + + Chart.Animation = Chart.Element.extend({ + currentStep: null, // the current animation step + numSteps: 60, // default number of steps + easing: '', // the easing to use for this animation + render: null, // render function used by the animation service + + onAnimationProgress: null, // user specified callback to fire on each step of the animation + onAnimationComplete: null // user specified callback to fire when the animation finishes + }); + + Chart.animationService = { + frameDuration: 17, + animations: [], + dropFrames: 0, + request: null, + + /** + * @function Chart.animationService.addAnimation + * @param chartInstance {ChartController} the chart to animate + * @param animationObject {IAnimation} the animation that we will animate + * @param duration {Number} length of animation in ms + * @param lazy {Boolean} if true, the chart is not marked as animating to enable more responsive interactions + */ + addAnimation: function(chartInstance, animationObject, duration, lazy) { + var me = this; + + if (!lazy) { + chartInstance.animating = true; + } + + for (var index = 0; index < me.animations.length; ++index) { + if (me.animations[index].chartInstance === chartInstance) { + // replacing an in progress animation + me.animations[index].animationObject = animationObject; + return; + } + } + + me.animations.push({ + chartInstance: chartInstance, + animationObject: animationObject + }); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if (me.animations.length === 1) { + me.requestAnimationFrame(); + } + }, + // Cancel the animation for a given chart instance + cancelAnimation: function(chartInstance) { + var index = helpers.findIndex(this.animations, function(animationWrapper) { + return animationWrapper.chartInstance === chartInstance; + }); + + if (index !== -1) { + this.animations.splice(index, 1); + chartInstance.animating = false; + } + }, + requestAnimationFrame: function() { + var me = this; + if (me.request === null) { + // Skip animation frame requests until the active one is executed. + // This can happen when processing mouse events, e.g. 'mousemove' + // and 'mouseout' events will trigger multiple renders. + me.request = helpers.requestAnimFrame.call(window, function() { + me.request = null; + me.startDigest(); + }); + } + }, + startDigest: function() { + var me = this; + + var startTime = Date.now(); + var framesToDrop = 0; + + if (me.dropFrames > 1) { + framesToDrop = Math.floor(me.dropFrames); + me.dropFrames = me.dropFrames % 1; + } + + var i = 0; + while (i < me.animations.length) { + if (me.animations[i].animationObject.currentStep === null) { + me.animations[i].animationObject.currentStep = 0; + } + + me.animations[i].animationObject.currentStep += 1 + framesToDrop; + + if (me.animations[i].animationObject.currentStep > me.animations[i].animationObject.numSteps) { + me.animations[i].animationObject.currentStep = me.animations[i].animationObject.numSteps; + } + + me.animations[i].animationObject.render(me.animations[i].chartInstance, me.animations[i].animationObject); + if (me.animations[i].animationObject.onAnimationProgress && me.animations[i].animationObject.onAnimationProgress.call) { + me.animations[i].animationObject.onAnimationProgress.call(me.animations[i].chartInstance, me.animations[i]); + } + + if (me.animations[i].animationObject.currentStep === me.animations[i].animationObject.numSteps) { + if (me.animations[i].animationObject.onAnimationComplete && me.animations[i].animationObject.onAnimationComplete.call) { + me.animations[i].animationObject.onAnimationComplete.call(me.animations[i].chartInstance, me.animations[i]); + } + + // executed the last frame. Remove the animation. + me.animations[i].chartInstance.animating = false; + + me.animations.splice(i, 1); + } else { + ++i; + } + } + + var endTime = Date.now(); + var dropFrames = (endTime - startTime) / me.frameDuration; + + me.dropFrames += dropFrames; + + // Do we have more stuff to animate? + if (me.animations.length > 0) { + me.requestAnimationFrame(); + } + } + }; +}; + +},{}],22:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + // Global Chart canvas helpers object for drawing items to canvas + var helpers = Chart.canvasHelpers = {}; + + helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { + var type, edgeLength, xOffset, yOffset, height, size; + + if (typeof pointStyle === 'object') { + type = pointStyle.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + switch (pointStyle) { + // Default includes circle + default: + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.closePath(); + ctx.fill(); + break; + case 'triangle': + ctx.beginPath(); + edgeLength = 3 * radius / Math.sqrt(3); + height = edgeLength * Math.sqrt(3) / 2; + ctx.moveTo(x - edgeLength / 2, y + height / 3); + ctx.lineTo(x + edgeLength / 2, y + height / 3); + ctx.lineTo(x, y - 2 * height / 3); + ctx.closePath(); + ctx.fill(); + break; + case 'rect': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.fillRect(x - size, y - size, 2 * size, 2 * size); + ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); + break; + case 'rectRot': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y - size); + ctx.closePath(); + ctx.fill(); + break; + case 'cross': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'crossRot': + ctx.beginPath(); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'star': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'line': + ctx.beginPath(); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'dash': + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + } + + ctx.stroke(); + }; +}; + +},{}],23:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + var helpers = Chart.helpers; + + // Create a dictionary of chart types, to allow for extension of existing types + Chart.types = {}; + + // Store a reference to each instance - allowing us to globally resize chart instances on window resize. + // Destroy method on the chart will remove the instance of the chart from this reference. + Chart.instances = {}; + + // Controllers available for dataset visualization eg. bar, line, slice, etc. + Chart.controllers = {}; + + /** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * TODO(SB) Move this method in the upcoming core.platform class. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns {Number} Size in pixels or undefined if unknown. + */ + function readUsedSize(element, property) { + var value = helpers.getStyle(element, property); + var matches = value && value.match(/(\d+)px/); + return matches? Number(matches[1]) : undefined; + } + + /** + * Initializes the canvas style and render size without modifying the canvas display size, + * since responsiveness is handled by the controller.resize() method. The config is used + * to determine the aspect ratio to apply in case no explicit height has been specified. + * TODO(SB) Move this method in the upcoming core.platform class. + */ + function initCanvas(canvas, config) { + var style = canvas.style; + + // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it + // returns null or '' if no explicit value has been set to the canvas attribute. + var renderHeight = canvas.getAttribute('height'); + var renderWidth = canvas.getAttribute('width'); + + // Chart.js modifies some canvas values that we want to restore on destroy + canvas._chartjs = { + initial: { + height: renderHeight, + width: renderWidth, + style: { + display: style.display, + height: style.height, + width: style.width + } + } + }; + + // Force canvas to display as block to avoid extra space caused by inline + // elements, which would interfere with the responsive resize process. + // https://github.com/chartjs/Chart.js/issues/2538 + style.display = style.display || 'block'; + + if (renderWidth === null || renderWidth === '') { + var displayWidth = readUsedSize(canvas, 'width'); + if (displayWidth !== undefined) { + canvas.width = displayWidth; + } + } + + if (renderHeight === null || renderHeight === '') { + if (canvas.style.height === '') { + // If no explicit render height and style height, let's apply the aspect ratio, + // which one can be specified by the user but also by charts as default option + // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. + canvas.height = canvas.width / (config.options.aspectRatio || 2); + } else { + var displayHeight = readUsedSize(canvas, 'height'); + if (displayWidth !== undefined) { + canvas.height = displayHeight; + } + } + } + + return canvas; + } + + /** + * Restores the canvas initial state, such as render/display sizes and style. + * TODO(SB) Move this method in the upcoming core.platform class. + */ + function releaseCanvas(canvas) { + if (!canvas._chartjs) { + return; + } + + var initial = canvas._chartjs.initial; + ['height', 'width'].forEach(function(prop) { + var value = initial[prop]; + if (value === undefined || value === null) { + canvas.removeAttribute(prop); + } else { + canvas.setAttribute(prop, value); + } + }); + + helpers.each(initial.style || {}, function(value, key) { + canvas.style[key] = value; + }); + + // The canvas render size might have been changed (and thus the state stack discarded), + // we can't use save() and restore() to restore the initial state. So make sure that at + // least the canvas context is reset to the default state by setting the canvas width. + // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html + canvas.width = canvas.width; + + delete canvas._chartjs; + } + + /** + * TODO(SB) Move this method in the upcoming core.platform class. + */ + function acquireContext(item, config) { + if (typeof item === 'string') { + item = document.getElementById(item); + } else if (item.length) { + // Support for array based queries (such as jQuery) + item = item[0]; + } + + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + if (item instanceof HTMLCanvasElement) { + // To prevent canvas fingerprinting, some add-ons undefine the getContext + // method, for example: https://github.com/kkapsner/CanvasBlocker + // https://github.com/chartjs/Chart.js/issues/2807 + var context = item.getContext && item.getContext('2d'); + if (context instanceof CanvasRenderingContext2D) { + initCanvas(item, config); + return context; + } + } + + return null; + } + + /** + * 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( + Chart.defaults.global, + Chart.defaults[config.type], + config.options || {}); + + return config; + } + + /** + * @class Chart.Controller + * The main controller of a chart. + */ + Chart.Controller = function(item, config, instance) { + var me = this; + + config = initConfig(config); + + var context = acquireContext(item, config); + var canvas = context && context.canvas; + var height = canvas && canvas.height; + var width = canvas && canvas.width; + + instance.ctx = context; + instance.canvas = canvas; + instance.config = config; + instance.width = width; + instance.height = height; + instance.aspectRatio = height? width / height : null; + + me.id = helpers.uid(); + me.chart = instance; + me.config = config; + me.options = config.options; + me._bufferedRender = false; + + // Add the chart instance to the global namespace + Chart.instances[me.id] = me; + + Object.defineProperty(me, 'data', { + get: function() { + return me.config.data; + } + }); + + 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; + } + + helpers.retinaScale(instance); + + // Responsiveness is currently based on the use of an iframe, however this method causes + // performance issues and could be troublesome when used with ad blockers. So make sure + // that the user is still able to create a chart without iframe when responsive is false. + // See https://github.com/chartjs/Chart.js/issues/2210 + if (me.options.responsive) { + helpers.addResizeListener(canvas.parentNode, function() { + me.resize(); + }); + + // Initial resize before chart draws (must be silent to preserve initial animations). + me.resize(true); + } + + me.initialize(); + + return me; + }; + + helpers.extend(Chart.Controller.prototype, /** @lends Chart.Controller */ { + initialize: function() { + var me = this; + + // Before init plugin notification + Chart.plugins.notify('beforeInit', [me]); + + me.bindEvents(); + + // Make sure controllers are built first so that each dataset is bound to an axis before the scales + // are built + me.ensureScalesHaveIDs(); + me.buildOrUpdateControllers(); + me.buildScales(); + me.updateLayout(); + me.resetElements(); + me.initToolTip(); + me.update(); + + // After init plugin notification + Chart.plugins.notify('afterInit', [me]); + + return me; + }, + + clear: function() { + helpers.clear(this.chart); + return this; + }, + + stop: function() { + // Stops any current animation loop occurring + Chart.animationService.cancelAnimation(this); + return this; + }, + + resize: function(silent) { + var me = this; + var chart = me.chart; + var options = me.options; + var canvas = chart.canvas; + var aspectRatio = (options.maintainAspectRatio && chart.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. + var newWidth = Math.floor(helpers.getMaximumWidth(canvas)); + var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)); + + if (chart.width === newWidth && chart.height === newHeight) { + return; + } + + canvas.width = chart.width = newWidth; + canvas.height = chart.height = newHeight; + canvas.style.width = newWidth + 'px'; + canvas.style.height = newHeight + 'px'; + + helpers.retinaScale(chart); + + // Notify any plugins about the resize + var newSize = {width: newWidth, height: newHeight}; + Chart.plugins.notify('resize', [me, newSize]); + + // Notify of resize + if (me.options.onResize) { + me.options.onResize(me, newSize); + } + + if (!silent) { + 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'}; + }), + (options.scales.yAxes || []).map(function(yAxisOptions) { + return {options: yAxisOptions, dtype: 'linear'}; + }) + ); + } + + if (options.scale) { + items.push({options: options.scale, dtype: 'radialLinear', isDefault: true}); + } + + helpers.each(items, function(item) { + var scaleOptions = item.options; + var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); + var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + + var scale = new scaleClass({ + id: scaleOptions.id, + options: scaleOptions, + ctx: me.chart.ctx, + chart: me + }); + + scales[scale.id] = scale; + + // 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); + }, + + updateLayout: function() { + Chart.layoutService.update(this, this.chart.width, this.chart.height); + }, + + buildOrUpdateControllers: function() { + var me = this; + var types = []; + var newControllers = []; + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + var meta = me.getDatasetMeta(datasetIndex); + if (!meta.type) { + meta.type = dataset.type || me.config.type; + } + + types.push(meta.type); + + if (meta.controller) { + meta.controller.updateIndex(datasetIndex); + } else { + meta.controller = new Chart.controllers[meta.type](me, datasetIndex); + newControllers.push(meta.controller); + } + }, me); + + if (types.length > 1) { + for (var i = 1; i < types.length; i++) { + if (types[i] !== types[i - 1]) { + me.isCombo = true; + break; + } + } + } + + return newControllers; + }, + + /** + * Reset the elements of all datasets + * @method resetElements + * @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 + * @method reset + */ + reset: function() { + this.resetElements(); + this.tooltip.initialize(); + }, + + update: function(animationDuration, lazy) { + var me = this; + Chart.plugins.notify('beforeUpdate', [me]); + + // 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); + + Chart.layoutService.update(me, me.chart.width, me.chart.height); + + // Apply changes to the datasets that require the scales to have been calculated i.e BorderColor changes + Chart.plugins.notify('afterScaleUpdate', [me]); + + // 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 + Chart.plugins.notify('afterUpdate', [me]); + + if (me._bufferedRender) { + me._bufferedRequest = { + lazy: lazy, + duration: animationDuration + }; + } else { + me.render(animationDuration, lazy); + } + }, + + /** + * @method beforeDatasetsUpdate + * @description Called before all datasets are updated. If a plugin returns false, + * the datasets update will be cancelled until another chart update is triggered. + * @param {Object} instance the chart instance being updated. + * @returns {Boolean} false to cancel the datasets update. + * @memberof Chart.PluginBase + * @since version 2.1.5 + * @instance + */ + + /** + * @method afterDatasetsUpdate + * @description Called after all datasets have been updated. Note that this + * extension will not be called if the datasets update has been cancelled. + * @param {Object} instance the chart instance being updated. + * @memberof Chart.PluginBase + * @since version 2.1.5 + * @instance + */ + + /** + * Updates all datasets unless a plugin returns false to the beforeDatasetsUpdate + * extension, in which case no datasets will be updated and the afterDatasetsUpdate + * notification will be skipped. + * @protected + * @instance + */ + updateDatasets: function() { + var me = this; + var i, ilen; + + if (Chart.plugins.notify('beforeDatasetsUpdate', [me])) { + for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.getDatasetMeta(i).controller.update(); + } + + Chart.plugins.notify('afterDatasetsUpdate', [me]); + } + }, + + render: function(duration, lazy) { + var me = this; + Chart.plugins.notify('beforeRender', [me]); + + var animationOptions = me.options.animation; + if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { + var animation = new Chart.Animation(); + animation.numSteps = (duration || animationOptions.duration) / 16.66; // 60 fps + animation.easing = animationOptions.easing; + + // render function + animation.render = function(chartInstance, animationObject) { + var easingFunction = helpers.easingEffects[animationObject.easing]; + var stepDecimal = animationObject.currentStep / animationObject.numSteps; + var easeDecimal = easingFunction(stepDecimal); + + chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep); + }; + + // user events + animation.onAnimationProgress = animationOptions.onProgress; + animation.onAnimationComplete = animationOptions.onComplete; + + Chart.animationService.addAnimation(me, animation, duration, lazy); + } else { + me.draw(); + if (animationOptions && animationOptions.onComplete && animationOptions.onComplete.call) { + animationOptions.onComplete.call(me); + } + } + return me; + }, + + draw: function(ease) { + var me = this; + var easingDecimal = ease || 1; + me.clear(); + + Chart.plugins.notify('beforeDraw', [me, easingDecimal]); + + // Draw all the scales + helpers.each(me.boxes, function(box) { + box.draw(me.chartArea); + }, me); + if (me.scale) { + me.scale.draw(); + } + + Chart.plugins.notify('beforeDatasetsDraw', [me, easingDecimal]); + + // Draw each dataset via its respective controller (reversed to support proper line stacking) + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + if (me.isDatasetVisible(datasetIndex)) { + me.getDatasetMeta(datasetIndex).controller.draw(ease); + } + }, me, true); + + Chart.plugins.notify('afterDatasetsDraw', [me, easingDecimal]); + + // Finally draw the tooltip + me.tooltip.transition(easingDecimal).draw(); + + Chart.plugins.notify('afterDraw', [me, easingDecimal]); + }, + + // 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 Chart.Interaction.modes.single(this, e); + }, + + getElementsAtEvent: function(e) { + return Chart.Interaction.modes.label(this, e, {intersect: true}); + }, + + getElementsAtXAxis: function(e) { + return Chart.Interaction.modes['x-axis'](this, e, {intersect: true}); + }, + + getElementsAtEventForMode: function(e, mode, options) { + var method = Chart.Interaction.modes[mode]; + if (typeof method === 'function') { + return method(this, e, options); + } + + return []; + }, + + getDatasetAtEvent: function(e) { + return Chart.Interaction.modes.dataset(this, e); + }, + + 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