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