From e64b504e7e4144f2d29d55c4193875472e9dd8a7 Mon Sep 17 00:00:00 2001 From: Marvin Borner Date: Wed, 5 Feb 2020 18:47:28 +0100 Subject: Started some kind of rewrite --- assets/js/chart.js | 7431 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 4450 insertions(+), 2981 deletions(-) (limited to 'assets/js/chart.js') diff --git a/assets/js/chart.js b/assets/js/chart.js index d3c7232..c7c25bf 100755 --- a/assets/js/chart.js +++ b/assets/js/chart.js @@ -1,5 +1,5 @@ /*! - * Chart.js v2.8.0 + * Chart.js v2.9.3 * https://www.chartjs.org * (c) 2019 Chart.js Contributors * Released under the MIT License @@ -7,825 +7,1271 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global.Chart = factory()); + (global = global || self, global.Chart = factory()); }(this, (function () { 'use strict'; - /* MIT license */ + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function commonjsRequire() { + throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); + } + + function createCommonjsModule(fn, module) { + return module = {exports: {}}, fn(module, module.exports), module.exports; + } - var conversions = { - rgb2hsl: rgb2hsl, - rgb2hsv: rgb2hsv, - rgb2hwb: rgb2hwb, - rgb2cmyk: rgb2cmyk, - rgb2keyword: rgb2keyword, - rgb2xyz: rgb2xyz, - rgb2lab: rgb2lab, - rgb2lch: rgb2lch, - - hsl2rgb: hsl2rgb, - hsl2hsv: hsl2hsv, - hsl2hwb: hsl2hwb, - hsl2cmyk: hsl2cmyk, - hsl2keyword: hsl2keyword, - - hsv2rgb: hsv2rgb, - hsv2hsl: hsv2hsl, - hsv2hwb: hsv2hwb, - hsv2cmyk: hsv2cmyk, - hsv2keyword: hsv2keyword, - - hwb2rgb: hwb2rgb, - hwb2hsl: hwb2hsl, - hwb2hsv: hwb2hsv, - hwb2cmyk: hwb2cmyk, - hwb2keyword: hwb2keyword, - - cmyk2rgb: cmyk2rgb, - cmyk2hsl: cmyk2hsl, - cmyk2hsv: cmyk2hsv, - cmyk2hwb: cmyk2hwb, - cmyk2keyword: cmyk2keyword, - - keyword2rgb: keyword2rgb, - keyword2hsl: keyword2hsl, - keyword2hsv: keyword2hsv, - keyword2hwb: keyword2hwb, - keyword2cmyk: keyword2cmyk, - keyword2lab: keyword2lab, - keyword2xyz: keyword2xyz, - - xyz2rgb: xyz2rgb, - xyz2lab: xyz2lab, - xyz2lch: xyz2lch, - - lab2xyz: lab2xyz, - lab2rgb: lab2rgb, - lab2lch: lab2lch, - - lch2lab: lch2lab, - lch2xyz: lch2xyz, - lch2rgb: lch2rgb + function getCjsExportFromNamespace(n) { + return n && n['default'] || n; + } + + var colorName = { + "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 conversions = createCommonjsModule(function (module) { + /* MIT license */ - function rgb2hsl(rgb) { - var r = rgb[0] / 255, - g = rgb[1] / 255, - b = rgb[2] / 255, - min = Math.min(r, g, b), - max = Math.max(r, g, b), - delta = max - min, - h, s, l; - if (max == min) - h = 0; - else if (r == max) - h = (g - b) / delta; - else if (g == max) - h = 2 + (b - r) / delta; - else if (b == max) - h = 4 + (r - g) / delta; +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) - h = Math.min(h * 60, 360); + var reverseKeywords = {}; + for (var key in colorName) { + if (colorName.hasOwnProperty(key)) { + reverseKeywords[colorName[key]] = key; + } + } - if (h < 0) - h += 360; + var convert = module.exports = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} + }; - l = (min + max) / 2; +// hide .channels and .labels properties + for (var model in convert) { + if (convert.hasOwnProperty(model)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } - if (max == min) - s = 0; - else if (l <= 0.5) - s = delta / (max + min); - else - s = delta / (2 - max - min); + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } - return [h, s * 100, l * 100]; - } + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } - function rgb2hsv(rgb) { - var r = rgb[0], - g = rgb[1], - b = rgb[2], - min = Math.min(r, g, b), - max = Math.max(r, g, b), - delta = max - min, - h, s, v; - - if (max == 0) - s = 0; - else - s = (delta / max * 1000) / 10; - - if (max == min) - h = 0; - else if (r == max) - h = (g - b) / delta; - else if (g == max) - h = 2 + (b - r) / delta; - else if (b == max) - h = 4 + (r - g) / delta; - - h = Math.min(h * 60, 360); - - if (h < 0) - h += 360; - - v = ((max / 255) * 1000) / 10; - - return [h, s, v]; - } + var channels = convert[model].channels; + var labels = convert[model].labels; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); + } + } - function rgb2hwb(rgb) { - var r = rgb[0], - g = rgb[1], - b = rgb[2], - h = rgb2hsl(rgb)[0], - w = 1 / 255 * Math.min(r, Math.min(g, b)), - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + convert.rgb.hsl = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; - return [h, w * 100, b * 100]; - } + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } - function rgb2cmyk(rgb) { - var r = rgb[0] / 255, - g = rgb[1] / 255, - b = rgb[2] / 255, - c, m, y, k; - - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; - return [c * 100, m * 100, y * 100, k * 100]; - } + h = Math.min(h * 60, 360); - function rgb2keyword(rgb) { - return reverseKeywords[JSON.stringify(rgb)]; - } + if (h < 0) { + h += 360; + } - function rgb2xyz(rgb) { - var r = rgb[0] / 255, - g = rgb[1] / 255, - b = rgb[2] / 255; + l = (min + max) / 2; - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } - 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 [h, s * 100, l * 100]; + }; - return [x * 100, y * 100, z * 100]; - } + convert.rgb.hsv = function (rgb) { + var rdif; + var gdif; + var bdif; + var h; + var s; + + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var v = Math.max(r, g, b); + var diff = v - Math.min(r, g, b); + var diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; - function rgb2lab(rgb) { - var xyz = rgb2xyz(rgb), - x = xyz[0], - y = xyz[1], - z = xyz[2], - l, a, b; + if (diff === 0) { + h = s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); - x /= 95.047; - y /= 100; - z /= 108.883; + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } - 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); + return [ + h * 360, + s * 100, + v * 100 + ]; + }; - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + convert.rgb.hwb = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); - return [l, a, b]; - } + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); - function rgb2lch(args) { - return lab2lch(rgb2lab(args)); - } + return [h, w * 100, b * 100]; + }; - function hsl2rgb(hsl) { - var h = hsl[0] / 360, - s = hsl[1] / 100, - l = hsl[2] / 100, - t1, t2, t3, rgb, val; + convert.rgb.cmyk = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; + }; - if (s == 0) { - val = l * 255; - return [val, val, val]; + /** + * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + * */ + function comparativeDistance(x, y) { + return ( + Math.pow(x[0] - y[0], 2) + + Math.pow(x[1] - y[1], 2) + + Math.pow(x[2] - y[2], 2) + ); } - 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; - } + convert.rgb.keyword = function (rgb) { + var reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } - return rgb; - } + var currentClosestDistance = Infinity; + var currentClosestKeyword; - function hsl2hsv(hsl) { - var h = hsl[0], - s = hsl[1] / 100, - l = hsl[2] / 100, - sv, v; + for (var keyword in colorName) { + if (colorName.hasOwnProperty(keyword)) { + var value = colorName[keyword]; - if (l === 0) { - // no need to do calc on black - // also avoids divide by 0 error - return [0, 0, 0]; - } + // Compute comparative distance + var distance = comparativeDistance(rgb, value); - l *= 2; - s *= (l <= 1) ? l : 2 - l; - v = (l + s) / 2; - sv = (2 * s) / (l + s); - return [h, sv * 100, v * 100]; - } + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + } - function hsl2hwb(args) { - return rgb2hwb(hsl2rgb(args)); - } + return currentClosestKeyword; + }; - function hsl2cmyk(args) { - return rgb2cmyk(hsl2rgb(args)); - } + convert.keyword.rgb = function (keyword) { + return colorName[keyword]; + }; - function hsl2keyword(args) { - return rgb2keyword(hsl2rgb(args)); - } + convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - 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]; - } - } + 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); - 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]; - } + return [x * 100, y * 100, z * 100]; + }; - function hsv2hwb(args) { - return rgb2hwb(hsv2rgb(args)) - } + convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; - function hsv2cmyk(args) { - return rgb2cmyk(hsv2rgb(args)); - } + x /= 95.047; + y /= 100; + z /= 108.883; - function hsv2keyword(args) { - return rgb2keyword(hsv2rgb(args)); - } + 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]; + }; + + convert.hsl.rgb = function (hsl) { + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var 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); + if (t3 < 0) { + t3++; + } + if (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; + }; + + convert.hsl.hsv = function (hsl) { + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var smin = s; + var lmin = Math.max(l, 0.01); + var sv; + var v; + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + v = (l + s) / 2; + sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; + }; + + convert.hsv.rgb = function (hsv) { + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; + + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + 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]; + } + }; + + convert.hsv.hsl = function (hsv) { + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var vmin = Math.max(v, 0.01); + var lmin; + var sl; + var l; + + l = (2 - s) * v; + lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; + }; // 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; - } + convert.hwb.rgb = function (hwb) { + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; - 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 + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } - 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; - } + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; - return [r * 255, g * 255, b * 255]; - } + if ((i & 0x01) !== 0) { + f = 1 - f; + } - function hwb2hsl(args) { - return rgb2hsl(hwb2rgb(args)); - } + n = wh + f * (v - wh); // linear interpolation - function hwb2hsv(args) { - return rgb2hsv(hwb2rgb(args)); - } + var r; + var g; + var b; + 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; + } - function hwb2cmyk(args) { - return rgb2cmyk(hwb2rgb(args)); - } + return [r * 255, g * 255, b * 255]; + }; - function hwb2keyword(args) { - return rgb2keyword(hwb2rgb(args)); - } + convert.cmyk.rgb = function (cmyk) { + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; - 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]; - } + 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); - function cmyk2hsl(args) { - return rgb2hsl(cmyk2rgb(args)); - } + return [r * 255, g * 255, b * 255]; + }; - function cmyk2hsv(args) { - return rgb2hsv(cmyk2rgb(args)); - } + convert.xyz.rgb = function (xyz) { + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; - function cmyk2hwb(args) { - return rgb2hwb(cmyk2rgb(args)); - } + 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); - function cmyk2keyword(args) { - return rgb2keyword(cmyk2rgb(args)); - } + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r * 12.92; + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g * 12.92; - function xyz2rgb(xyz) { - var x = xyz[0] / 100, - y = xyz[1] / 100, - z = xyz[2] / 100, - r, g, b; + b = b > 0.0031308 + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b * 12.92; - 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); + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); - // assume sRGB - r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r = (r * 12.92); + return [r * 255, g * 255, b * 255]; + }; - g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g = (g * 12.92); + convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; - b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b = (b * 12.92); + x /= 95.047; + y /= 100; + z /= 108.883; - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); + 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); - return [r * 255, g * 255, b * 255]; - } + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); - function xyz2lab(xyz) { - var x = xyz[0], - y = xyz[1], - z = xyz[2], - l, a, b; + return [l, a, b]; + }; - x /= 95.047; - y /= 100; - z /= 108.883; + convert.lab.xyz = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + var y2 = Math.pow(y, 3); + var x2 = Math.pow(x, 3); + var z2 = Math.pow(z, 3); + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + + x *= 95.047; + y *= 100; + z *= 108.883; + + return [x, y, z]; + }; - 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); + convert.lab.lch = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; - return [l, a, b]; - } + if (h < 0) { + h += 360; + } - function xyz2lch(args) { - return lab2lch(xyz2lab(args)); - } + c = Math.sqrt(a * a + b * b); - function lab2xyz(lab) { - var l = lab[0], - a = lab[1], - b = lab[2], - x, y, z, y2; + return [l, c, h]; + }; - 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); - } + convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; - x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); - 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 [l, a, b]; + }; - return [x, y, z]; - } + convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; + }; + + convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); + }; + + convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + + // we use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; + }; + + convert.ansi16.rgb = function (args) { + var color = args % 10; + + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; + }; + + convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; - function lab2lch(lab) { - var l = lab[0], - a = lab[1], - b = lab[2], - hr, h, c; + return [r, g, b]; + }; + + convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; + }; + + convert.hex.rgb = function (args) { + var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } + + var colorString = match[0]; + + if (match[0].length === 3) { + colorString = colorString.split('').map(function (char) { + return char + char; + }).join(''); + } + + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + + return [r, g, b]; + }; + + convert.rgb.hcg = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var max = Math.max(Math.max(r, g), b); + var min = Math.min(Math.min(r, g), b); + var chroma = (max - min); + var grayscale; + var hue; + + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + + if (chroma <= 0) { + hue = 0; + } else if (max === r) { + hue = ((g - b) / chroma) % 6; + } else if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma + 4; + } + + hue /= 6; + hue %= 1; + + return [hue * 360, chroma * 100, grayscale * 100]; + }; + + convert.hsl.hcg = function (hsl) { + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; + + if (l < 0.5) { + c = 2.0 * s * l; + } else { + c = 2.0 * s * (1.0 - l); + } + + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } + + return [hsl[0], c * 100, f * 100]; + }; + + convert.hsv.hcg = function (hsv) { + var s = hsv[1] / 100; + var v = hsv[2] / 100; + + var c = s * v; + var f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); + } + + return [hsv[0], c * 100, f * 100]; + }; + + convert.hcg.rgb = function (hcg) { + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; + + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; + pure[1] = v; + pure[2] = 0; + break; + case 1: + pure[0] = w; + pure[1] = 1; + pure[2] = 0; + break; + case 2: + pure[0] = 0; + pure[1] = 1; + pure[2] = v; + break; + case 3: + pure[0] = 0; + pure[1] = w; + pure[2] = 1; + break; + case 4: + pure[0] = v; + pure[1] = 0; + pure[2] = 1; + break; + default: + pure[0] = 1; + pure[1] = 0; + pure[2] = w; + } + + mg = (1.0 - c) * g; + + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; + }; + + convert.hcg.hsv = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; - if (h < 0) { - h += 360; + var v = c + g * (1.0 - c); + var f = 0; + + if (v > 0.0) { + f = c / v; + } + + return [hcg[0], f * 100, v * 100]; + }; + + convert.hcg.hsl = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; + + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + + return [hcg[0], s * 100, l * 100]; + }; + + convert.hcg.hwb = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; + }; + + convert.hwb.hcg = function (hwb) { + var w = hwb[1] / 100; + var b = hwb[2] / 100; + var v = 1 - b; + var c = v - w; + var g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); + } + + return [hwb[0], c * 100, g * 100]; + }; + + convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; + }; + + convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; + }; + + convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; + }; + + convert.gray.hsl = convert.gray.hsv = function (args) { + return [0, 0, args[0]]; + }; + + convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; + }; + + convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; + }; + + convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; + }; + + convert.gray.hex = function (gray) { + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; + + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; + }; + + convert.rgb.gray = function (rgb) { + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; + }; + }); + var conversions_1 = conversions.rgb; + var conversions_2 = conversions.hsl; + var conversions_3 = conversions.hsv; + var conversions_4 = conversions.hwb; + var conversions_5 = conversions.cmyk; + var conversions_6 = conversions.xyz; + var conversions_7 = conversions.lab; + var conversions_8 = conversions.lch; + var conversions_9 = conversions.hex; + var conversions_10 = conversions.keyword; + var conversions_11 = conversions.ansi16; + var conversions_12 = conversions.ansi256; + var conversions_13 = conversions.hcg; + var conversions_14 = conversions.apple; + var conversions_15 = conversions.gray; + + /* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + + function buildGraph() { + var graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + var models = Object.keys(conversions); + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; } - c = Math.sqrt(a * a + b * b); - return [l, c, h]; - } - function lab2rgb(args) { - return xyz2rgb(lab2xyz(args)); + return graph; } - function lch2lab(lch) { - var l = lch[0], - c = lch[1], - h = lch[2], - a, b, hr; +// https://en.wikipedia.org/wiki/Breadth-first_search + function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); - return [l, a, b]; - } + graph[fromModel].distance = 0; - function lch2xyz(args) { - return lab2xyz(lch2lab(args)); - } + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); - function lch2rgb(args) { - return lab2rgb(lch2lab(args)); - } + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; - function keyword2rgb(keyword) { - return cssKeywords[keyword]; - } + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } - function keyword2hsl(args) { - return rgb2hsl(keyword2rgb(args)); + return graph; } - function keyword2hsv(args) { - return rgb2hsv(keyword2rgb(args)); + function link(from, to) { + return function (args) { + return to(from(args)); + }; } - function keyword2hwb(args) { - return rgb2hwb(keyword2rgb(args)); - } + function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } - function keyword2cmyk(args) { - return rgb2cmyk(keyword2rgb(args)); + fn.conversion = path; + return fn; } - function keyword2lab(args) { - return rgb2lab(keyword2rgb(args)); - } + var route = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } - function keyword2xyz(args) { - return rgb2xyz(keyword2rgb(args)); - } + conversion[toModel] = wrapConversion(toModel, graph); + } - 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] + return conversion; }; - var reverseKeywords = {}; - for (var key in cssKeywords) { - reverseKeywords[JSON.stringify(cssKeywords[key])] = key; - } + var convert = {}; - var convert = function () { - return new Converter(); - }; + var models = Object.keys(conversions); - 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); + function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; } - })(func); - var pair = /(\w+)2(\w+)/.exec(func), - from = pair[1], - to = pair[2]; + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } - // export rgb2hsl and ["rgb"]["hsl"] - convert[from] = convert[from] || {}; + return fn(args); + }; - convert[from][to] = convert[func] = (function (func) { - return function (arg) { - if (typeof arg == "number") - arg = Array.prototype.slice.call(arguments); + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } - var val = conversions[func](arg); - if (typeof val == "string" || val === undefined) - return val; // keyword + return wrappedFn; + } - for (var i = 0; i < val.length; i++) - val[i] = Math.round(val[i]); - return val; + function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; } - })(func); - } + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } - /* Converter does lazy conversion and caching */ - var Converter = function () { - this.convs = {}; - }; + var result = fn(args); - /* 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); + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; } - return this.setValues(space, values); - }; + return wrappedFn; + } - /* 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; - }; + models.forEach(function (fromModel) { + convert[fromModel] = {}; - /* 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; - }; + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function (space) { - Converter.prototype[space] = function (vals) { - return this.routeSpace(space, arguments); - }; + var routes = route(fromModel); + var routeModels = Object.keys(routes); + + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); }); var colorConvert = convert; - var colorName = { + var colorName$1 = { "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], "aqua": [0, 255, 255], @@ -1044,7 +1490,7 @@ if (match[1] == "transparent") { return [0, 0, 0, 0]; } - rgb = colorName[match[1]]; + rgb = colorName$1[match[1]]; if (!rgb) { return; } @@ -1203,8 +1649,8 @@ //create a list of reverse color names var reverseNames = {}; - for (var name in colorName) { - reverseNames[colorName[name]] = name; + for (var name in colorName$1) { + reverseNames[colorName$1[name]] = name; } /* MIT license */ @@ -1968,14 +2414,12 @@ * @param {object} argN - Additional objects containing properties to merge in target. * @returns {object} The `target` object. */ - extend: function (target) { - var setFn = function (value, key) { - target[key] = value; - }; - for (var i = 1, ilen = arguments.length; i < ilen; ++i) { - helpers.each(arguments[i], setFn); - } - return target; + extend: Object.assign || function (target) { + return helpers.merge(target, [].slice.call(arguments, 1), { + merger: function (key, dst, src) { + dst[key] = src[key]; + } + }); }, /** @@ -2001,6 +2445,13 @@ ChartElement.__super__ = me.prototype; return ChartElement; + }, + + _deprecated: function (scope, value, previous, current) { + if (value !== undefined) { + console.warn(scope + ': "' + previous + + '" is deprecated. Please use "' + current + '" instead'); + } } }; @@ -2362,7 +2813,11 @@ if (style && typeof style === 'object') { type = style.toString(); if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { - ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); + ctx.save(); + ctx.translate(x, y); + ctx.rotate(rad); + ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height); + ctx.restore(); return; } } @@ -2554,6 +3009,8 @@ } }; +// TODO(v3): remove 'global' from namespace. all default are global and +// there's inconsistency around which options are under 'global' defaults._set('global', { defaultColor: 'rgba(0,0,0,0.1)', defaultFontColor: '#666', @@ -2612,8 +3069,6 @@ case '%': value /= 100; break; - default: - break; } return size * value; @@ -2678,9 +3133,12 @@ * is called with `context` as first argument and the result becomes the new input. * @param {number} [index] - If defined and the current value is an array, the value * at `index` become the new input. + * @param {object} [info] - object to return information about resolution in + * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable. * @since 2.7.0 */ - resolve: function (inputs, context, index) { + resolve: function (inputs, context, index, info) { + var cacheable = true; var i, ilen, value; for (i = 0, ilen = inputs.length; i < ilen; ++i) { @@ -2690,24 +3148,161 @@ } if (context !== undefined && typeof value === 'function') { value = value(context); + cacheable = false; } if (index !== undefined && helpers_core.isArray(value)) { value = value[index]; + cacheable = false; } if (value !== undefined) { + if (info && !cacheable) { + info.cacheable = false; + } return value; } } } }; + /** + * @alias Chart.helpers.math + * @namespace + */ + var exports$2 = { + /** + * Returns an array of factors sorted from 1 to sqrt(value) + * @private + */ + _factorize: function (value) { + var result = []; + var sqrt = Math.sqrt(value); + var i; + + for (i = 1; i < sqrt; i++) { + if (value % i === 0) { + result.push(i); + result.push(value / i); + } + } + if (sqrt === (sqrt | 0)) { // if value is a square number + result.push(sqrt); + } + + result.sort(function (a, b) { + return a - b; + }).pop(); + return result; + }, + + log10: Math.log10 || function (x) { + var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. + // Check for whole powers of 10, + // which due to floating point rounding error should be corrected. + var powerOf10 = Math.round(exponent); + var isPowerOf10 = x === Math.pow(10, powerOf10); + + return isPowerOf10 ? powerOf10 : exponent; + } + }; + + var helpers_math = exports$2; + +// DEPRECATIONS + + /** + * Provided for backward compatibility, use Chart.helpers.math.log10 instead. + * @namespace Chart.helpers.log10 + * @deprecated since version 2.9.0 + * @todo remove at version 3 + * @private + */ + helpers_core.log10 = exports$2.log10; + + var getRtlAdapter = function (rectX, width) { + return { + x: function (x) { + return rectX + rectX + width - x; + }, + setWidth: function (w) { + width = w; + }, + textAlign: function (align) { + if (align === 'center') { + return align; + } + return align === 'right' ? 'left' : 'right'; + }, + xPlus: function (x, value) { + return x - value; + }, + leftForLtr: function (x, itemWidth) { + return x - itemWidth; + }, + }; + }; + + var getLtrAdapter = function () { + return { + x: function (x) { + return x; + }, + setWidth: function (w) { // eslint-disable-line no-unused-vars + }, + textAlign: function (align) { + return align; + }, + xPlus: function (x, value) { + return x + value; + }, + leftForLtr: function (x, _itemWidth) { // eslint-disable-line no-unused-vars + return x; + }, + }; + }; + + var getAdapter = function (rtl, rectX, width) { + return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter(); + }; + + var overrideTextDirection = function (ctx, direction) { + var style, original; + if (direction === 'ltr' || direction === 'rtl') { + style = ctx.canvas.style; + original = [ + style.getPropertyValue('direction'), + style.getPropertyPriority('direction'), + ]; + + style.setProperty('direction', direction, 'important'); + ctx.prevTextDirection = original; + } + }; + + var restoreTextDirection = function (ctx) { + var original = ctx.prevTextDirection; + if (original !== undefined) { + delete ctx.prevTextDirection; + ctx.canvas.style.setProperty('direction', original[0], original[1]); + } + }; + + var helpers_rtl = { + getRtlAdapter: getAdapter, + overrideTextDirection: overrideTextDirection, + restoreTextDirection: restoreTextDirection, + }; + var helpers$1 = helpers_core; var easing = helpers_easing; var canvas = helpers_canvas; var options = helpers_options; + var math = helpers_math; + var rtl = helpers_rtl; helpers$1.easing = easing; helpers$1.canvas = canvas; helpers$1.options = options; + helpers$1.math = math; + helpers$1.rtl = rtl; function interpolate(start, view, model, ease) { var keys = Object.keys(model); @@ -2764,6 +3359,7 @@ }; helpers$1.extend(Element.prototype, { + _type: undefined, initialize: function () { this.hidden = false; @@ -2772,7 +3368,7 @@ pivot: function () { var me = this; if (!me._view) { - me._view = helpers$1.clone(me._model); + me._view = helpers$1.extend({}, me._model); } me._start = {}; return me; @@ -2786,7 +3382,7 @@ // No animation -> No Transition if (!model || ease === 1) { - me._view = model; + me._view = helpers$1.extend({}, model); me._start = null; return me; } @@ -2820,7 +3416,7 @@ var core_element = Element; - var exports$2 = core_element.extend({ + var exports$3 = core_element.extend({ chart: null, // the animation associated chart instance currentStep: 0, // the current animation step numSteps: 60, // default number of steps @@ -2831,7 +3427,7 @@ onAnimationComplete: null, // user specified callback to fire when the animation finishes }); - var core_animation = exports$2; + var core_animation = exports$3; // DEPRECATIONS @@ -2841,7 +3437,7 @@ * @deprecated since version 2.6.0 * @todo remove at version 3 */ - Object.defineProperty(exports$2.prototype, 'animationObject', { + Object.defineProperty(exports$3.prototype, 'animationObject', { get: function () { return this; } @@ -2853,7 +3449,7 @@ * @deprecated since version 2.6.0 * @todo remove at version 3 */ - Object.defineProperty(exports$2.prototype, 'chartInstance', { + Object.defineProperty(exports$3.prototype, 'chartInstance', { get: function () { return this.chart; }, @@ -3071,12 +3667,42 @@ */ dataElementType: null, + /** + * Dataset element option keys to be resolved in _resolveDatasetElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _datasetElementOptions: [ + 'backgroundColor', + 'borderCapStyle', + 'borderColor', + 'borderDash', + 'borderDashOffset', + 'borderJoinStyle', + 'borderWidth' + ], + + /** + * Data element option keys to be resolved in _resolveDataElementOptions. + * A derived controller may override this to resolve controller-specific options. + * The keys defined here are for backward compatibility for legend styles. + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'pointStyle' + ], + initialize: function (chart, datasetIndex) { var me = this; me.chart = chart; me.index = datasetIndex; me.linkScales(); me.addElements(); + me._type = me.getMeta().type; }, updateIndex: function (datasetIndex) { @@ -3086,13 +3712,16 @@ linkScales: function () { var me = this; var meta = me.getMeta(); + var chart = me.chart; + var scales = chart.scales; var dataset = me.getDataset(); + var scalesOpts = chart.options.scales; - if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { - meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; + if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) { + meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id; } - if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { - meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; + if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) { + meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id; } }, @@ -3137,7 +3766,7 @@ }, reset: function () { - this.update(true); + this._update(true); }, /** @@ -3213,6 +3842,31 @@ me.resyncElements(); }, + /** + * Returns the merged user-supplied and default dataset-level options + * @private + */ + _configure: function () { + var me = this; + me._config = helpers$1.merge({}, [ + me.chart.options.datasets[me._type], + me.getDataset(), + ], { + merger: function (key, target, source) { + if (key !== '_meta' && key !== 'data') { + helpers$1._merger(key, target, source); + } + } + }); + }, + + _update: function (reset) { + var me = this; + me._configure(); + me._cachedDataOpts = null; + me.update(reset); + }, + update: helpers$1.noop, transition: function (easingValue) { @@ -3245,6 +3899,127 @@ } }, + /** + * Returns a set of predefined style properties that should be used to represent the dataset + * or the data if the index is specified + * @param {number} index - data index + * @return {IStyleInterface} style object + */ + getStyle: function (index) { + var me = this; + var meta = me.getMeta(); + var dataset = meta.dataset; + var style; + + me._configure(); + if (dataset && index === undefined) { + style = me._resolveDatasetElementOptions(dataset || {}); + } else { + index = index || 0; + style = me._resolveDataElementOptions(meta.data[index] || {}, index); + } + + if (style.fill === false || style.fill === null) { + style.backgroundColor = style.borderColor; + } + + return style; + }, + + /** + * @private + */ + _resolveDatasetElementOptions: function (element, hover) { + var me = this; + var chart = me.chart; + var datasetOpts = me._config; + var custom = element.custom || {}; + var options = chart.options.elements[me.datasetElementType.prototype._type] || {}; + var elementOptions = me._datasetElementOptions; + var values = {}; + var i, ilen, key, readKey; + + // Scriptable options + var context = { + chart: chart, + dataset: me.getDataset(), + datasetIndex: me.index, + hover: hover + }; + + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key; + values[key] = resolve([ + custom[readKey], + datasetOpts[readKey], + options[readKey] + ], context); + } + + return values; + }, + + /** + * @private + */ + _resolveDataElementOptions: function (element, index) { + var me = this; + var custom = element && element.custom; + var cached = me._cachedDataOpts; + if (cached && !custom) { + return cached; + } + var chart = me.chart; + var datasetOpts = me._config; + var options = chart.options.elements[me.dataElementType.prototype._type] || {}; + var elementOptions = me._dataElementOptions; + var values = {}; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: me.getDataset(), + datasetIndex: me.index + }; + + // `resolve` sets cacheable to `false` if any option is indexed or scripted + var info = {cacheable: !custom}; + + var keys, i, ilen, key; + + custom = custom || {}; + + if (helpers$1.isArray(elementOptions)) { + for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { + key = elementOptions[i]; + values[key] = resolve([ + custom[key], + datasetOpts[key], + options[key] + ], context, index, info); + } + } else { + keys = Object.keys(elementOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve([ + custom[key], + datasetOpts[elementOptions[key]], + datasetOpts[key], + options[key] + ], context, index, info); + } + } + + if (info.cacheable) { + me._cachedDataOpts = Object.freeze(values); + } + + return values; + }, + removeHoverStyle: function (element) { helpers$1.merge(element._model, element.$previousStyle || {}); delete element.$previousStyle; @@ -3268,6 +4043,42 @@ model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index); }, + /** + * @private + */ + _removeDatasetHoverStyle: function () { + var element = this.getMeta().dataset; + + if (element) { + this.removeHoverStyle(element); + } + }, + + /** + * @private + */ + _setDatasetHoverStyle: function () { + var element = this.getMeta().dataset; + var prev = {}; + var i, ilen, key, keys, hoverOptions, model; + + if (!element) { + return; + } + + model = element._model; + hoverOptions = this._resolveDatasetElementOptions(element, true); + + keys = Object.keys(hoverOptions); + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + prev[key] = model[key]; + model[key] = hoverOptions[key]; + } + + element.$previousStyle = prev; + }, + /** * @private */ @@ -3336,6 +4147,8 @@ var core_datasetController = DatasetController; + var TAU = Math.PI * 2; + core_defaults._set('global', { elements: { arc: { @@ -3347,7 +4160,84 @@ } }); + function clipArc(ctx, arc) { + var startAngle = arc.startAngle; + var endAngle = arc.endAngle; + var pixelMargin = arc.pixelMargin; + var angleMargin = pixelMargin / arc.outerRadius; + var x = arc.x; + var y = arc.y; + + // Draw an inner border by cliping the arc and drawing a double-width border + // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders + ctx.beginPath(); + ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin); + if (arc.innerRadius > pixelMargin) { + angleMargin = pixelMargin / arc.innerRadius; + ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true); + } else { + ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2); + } + ctx.closePath(); + ctx.clip(); + } + + function drawFullCircleBorders(ctx, vm, arc, inner) { + var endAngle = arc.endAngle; + var i; + + if (inner) { + arc.endAngle = arc.startAngle + TAU; + clipArc(ctx, arc); + arc.endAngle = endAngle; + if (arc.endAngle === arc.startAngle && arc.fullCircles) { + arc.endAngle += TAU; + arc.fullCircles--; + } + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.stroke(); + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.stroke(); + } + } + + function drawBorder(ctx, vm, arc) { + var inner = vm.borderAlign === 'inner'; + + if (inner) { + ctx.lineWidth = vm.borderWidth * 2; + ctx.lineJoin = 'round'; + } else { + ctx.lineWidth = vm.borderWidth; + ctx.lineJoin = 'bevel'; + } + + if (arc.fullCircles) { + drawFullCircleBorders(ctx, vm, arc, inner); + } + + if (inner) { + clipArc(ctx, arc); + } + + ctx.beginPath(); + ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + ctx.stroke(); + } + var element_arc = core_element.extend({ + _type: 'arc', + inLabelRange: function (mouseX) { var vm = this._view; @@ -3369,13 +4259,13 @@ var startAngle = vm.startAngle; var endAngle = vm.endAngle; while (endAngle < startAngle) { - endAngle += 2.0 * Math.PI; + endAngle += TAU; } while (angle > endAngle) { - angle -= 2.0 * Math.PI; + angle -= TAU; } while (angle < startAngle) { - angle += 2.0 * Math.PI; + angle += TAU; } // Check if within the range of the open/close angle @@ -3416,51 +4306,44 @@ draw: function () { var ctx = this._chart.ctx; var vm = this._view; - var sA = vm.startAngle; - var eA = vm.endAngle; var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0; - var angleMargin; + var arc = { + x: vm.x, + y: vm.y, + innerRadius: vm.innerRadius, + outerRadius: Math.max(vm.outerRadius - pixelMargin, 0), + pixelMargin: pixelMargin, + startAngle: vm.startAngle, + endAngle: vm.endAngle, + fullCircles: Math.floor(vm.circumference / TAU) + }; + var i; ctx.save(); + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + + if (arc.fullCircles) { + arc.endAngle = arc.startAngle + TAU; + ctx.beginPath(); + ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); + ctx.closePath(); + for (i = 0; i < arc.fullCircles; ++i) { + ctx.fill(); + } + arc.endAngle = arc.startAngle + vm.circumference % TAU; + } + ctx.beginPath(); - ctx.arc(vm.x, vm.y, Math.max(vm.outerRadius - pixelMargin, 0), sA, eA); - ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); + ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle); + ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true); ctx.closePath(); - - ctx.fillStyle = vm.backgroundColor; ctx.fill(); if (vm.borderWidth) { - if (vm.borderAlign === 'inner') { - // Draw an inner border by cliping the arc and drawing a double-width border - // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders - ctx.beginPath(); - angleMargin = pixelMargin / vm.outerRadius; - ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin); - if (vm.innerRadius > pixelMargin) { - angleMargin = pixelMargin / vm.innerRadius; - ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true); - } else { - ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2); - } - ctx.closePath(); - ctx.clip(); - - ctx.beginPath(); - ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); - ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); - ctx.closePath(); - - ctx.lineWidth = vm.borderWidth * 2; - ctx.lineJoin = 'round'; - } else { - ctx.lineWidth = vm.borderWidth; - ctx.lineJoin = 'bevel'; - } - - ctx.strokeStyle = vm.borderColor; - ctx.stroke(); + drawBorder(ctx, vm, arc); } ctx.restore(); @@ -3489,6 +4372,8 @@ }); var element_line = core_element.extend({ + _type: 'line', + draw: function () { var me = this; var vm = me._view; @@ -3498,11 +4383,27 @@ var globalDefaults = core_defaults.global; var globalOptionLineElements = globalDefaults.elements.line; var lastDrawnIndex = -1; - var index, current, previous, currentVM; + var closePath = me._loop; + var index, previous, currentVM; + + if (!points.length) { + return; + } - // If we are looping, adding the first point again - if (me._loop && points.length) { - points.push(points[0]); + if (me._loop) { + for (index = 0; index < points.length; ++index) { + previous = helpers$1.previousItem(points, index); + // If the line has an open path, shift the point array + if (!points[index]._view.skip && previous._view.skip) { + points = points.slice(index).concat(points.slice(0, index)); + closePath = spanGaps; + break; + } + } + // If the line has a close path, add the first point again + if (closePath) { + points.push(points[0]); + } } ctx.save(); @@ -3522,35 +4423,34 @@ // Stroke Line ctx.beginPath(); - lastDrawnIndex = -1; - for (index = 0; index < points.length; ++index) { - current = points[index]; - previous = helpers$1.previousItem(points, index); - currentVM = current._view; + // First point moves to it's starting position no matter what + currentVM = points[0]._view; + if (!currentVM.skip) { + ctx.moveTo(currentVM.x, currentVM.y); + lastDrawnIndex = 0; + } - // First point moves to it's starting position no matter what - if (index === 0) { - if (!currentVM.skip) { - ctx.moveTo(currentVM.x, currentVM.y); - lastDrawnIndex = index; - } - } else { - previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; + for (index = 1; index < points.length; ++index) { + currentVM = points[index]._view; + previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex]; - if (!currentVM.skip) { - if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { - // There was a gap and this is the first point after the gap - ctx.moveTo(currentVM.x, currentVM.y); - } else { - // Line to next point - helpers$1.canvas.lineTo(ctx, previous._view, current._view); - } - lastDrawnIndex = index; + if (!currentVM.skip) { + if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { + // There was a gap and this is the first point after the gap + ctx.moveTo(currentVM.x, currentVM.y); + } else { + // Line to next point + helpers$1.canvas.lineTo(ctx, previous._view, currentVM); } + lastDrawnIndex = index; } } + if (closePath) { + ctx.closePath(); + } + ctx.stroke(); ctx.restore(); } @@ -3587,6 +4487,8 @@ } var element_point = core_element.extend({ + _type: 'point', + inRange: function (mouseX, mouseY) { var vm = this._view; return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; @@ -3769,6 +4671,8 @@ } var element_rectangle = core_element.extend({ + _type: 'rectangle', + draw: function () { var ctx = this._chart.ctx; var vm = this._view; @@ -3858,7 +4762,8 @@ elements.Point = Point; elements.Rectangle = Rectangle; - var resolve$1 = helpers$1.options.resolve; + var deprecated = helpers$1._deprecated; + var valueOrDefault$3 = helpers$1.valueOrDefault; core_defaults._set('bar', { hover: { @@ -3868,8 +4773,6 @@ scales: { xAxes: [{ type: 'category', - categoryPercentage: 0.8, - barPercentage: 0.9, offset: true, gridLines: { offsetGridLines: true @@ -3882,22 +4785,30 @@ } }); + core_defaults._set('global', { + datasets: { + bar: { + categoryPercentage: 0.8, + barPercentage: 0.9 + } + } + }); + /** * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. * @private */ function computeMinSampleSize(scale, pixels) { - var min = scale.isHorizontal() ? scale.width : scale.height; - var ticks = scale.getTicks(); + var min = scale._length; var prev, curr, i, ilen; for (i = 1, ilen = pixels.length; i < ilen; ++i) { min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1])); } - for (i = 0, ilen = ticks.length; i < ilen; ++i) { + for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) { curr = scale.getPixelForTick(i); - min = i > 0 ? Math.min(min, curr - prev) : min; + min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min; prev = curr; } @@ -3914,10 +4825,13 @@ var thickness = options.barThickness; var count = ruler.stackCount; var curr = ruler.pixels[index]; + var min = helpers$1.isNullOrUndef(thickness) + ? computeMinSampleSize(ruler.scale, ruler.pixels) + : -1; var size, ratio; if (helpers$1.isNullOrUndef(thickness)) { - size = ruler.min * options.categoryPercentage; + size = min * options.categoryPercentage; ratio = options.barPercentage; } else { // When bar thickness is enforced, category and bar percentages are ignored. @@ -3973,15 +4887,37 @@ dataElementType: elements.Rectangle, + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderSkipped', + 'borderWidth', + 'barPercentage', + 'barThickness', + 'categoryPercentage', + 'maxBarThickness', + 'minBarLength' + ], + initialize: function () { var me = this; - var meta; + var meta, scaleOpts; core_datasetController.prototype.initialize.apply(me, arguments); meta = me.getMeta(); meta.stack = me.getDataset().stack; meta.bar = true; + + scaleOpts = me._getIndexScale().options; + deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage'); + deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness'); + deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage'); + deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength'); + deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness'); }, update: function (reset) { @@ -4000,7 +4936,7 @@ var me = this; var meta = me.getMeta(); var dataset = me.getDataset(); - var options = me._resolveElementOptions(rectangle, index); + var options = me._resolveDataElementOptions(rectangle, index); rectangle._xScale = me.getScaleForId(meta.xAxisID); rectangle._yScale = me.getScaleForId(meta.yAxisID); @@ -4015,7 +4951,11 @@ label: me.chart.data.labels[index] }; - me._updateElementGeometry(rectangle, index, reset); + if (helpers$1.isArray(dataset.data[index])) { + rectangle._model.borderSkipped = null; + } + + me._updateElementGeometry(rectangle, index, reset, options); rectangle.pivot(); }, @@ -4023,15 +4963,15 @@ /** * @private */ - _updateElementGeometry: function (rectangle, index, reset) { + _updateElementGeometry: function (rectangle, index, reset, options) { var me = this; var model = rectangle._model; var vscale = me._getValueScale(); var base = vscale.getBasePixel(); var horizontal = vscale.isHorizontal(); var ruler = me._ruler || me.getRuler(); - var vpixels = me.calculateBarValuePixels(me.index, index); - var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); + var vpixels = me.calculateBarValuePixels(me.index, index, options); + var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options); model.horizontal = horizontal; model.base = reset ? base : vpixels.base; @@ -4049,21 +4989,27 @@ */ _getStacks: function (last) { var me = this; - var chart = me.chart; var scale = me._getIndexScale(); + var metasets = scale._getMatchingVisibleMetas(me._type); var stacked = scale.options.stacked; - var ilen = last === undefined ? chart.data.datasets.length : last + 1; + var ilen = metasets.length; var stacks = []; var i, meta; for (i = 0; i < ilen; ++i) { - meta = chart.getDatasetMeta(i); - if (meta.bar && chart.isDatasetVisible(i) && - (stacked === false || - (stacked === true && stacks.indexOf(meta.stack) === -1) || - (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { + meta = metasets[i]; + // stacked | meta.stack + // | found | not found | undefined + // false | x | x | x + // true | | x | + // undefined | | x | x + if (stacked === false || stacks.indexOf(meta.stack) === -1 || + (stacked === undefined && meta.stack === undefined)) { stacks.push(meta.stack); } + if (meta.index === last) { + break; + } } return stacks; @@ -4101,28 +5047,18 @@ getRuler: function () { var me = this; var scale = me._getIndexScale(); - var stackCount = me.getStackCount(); - var datasetIndex = me.index; - var isHorizontal = scale.isHorizontal(); - var start = isHorizontal ? scale.left : scale.top; - var end = start + (isHorizontal ? scale.width : scale.height); var pixels = []; - var i, ilen, min; + var i, ilen; for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { - pixels.push(scale.getPixelForValue(null, i, datasetIndex)); + pixels.push(scale.getPixelForValue(null, i, me.index)); } - min = helpers$1.isNullOrUndef(scale.options.barThickness) - ? computeMinSampleSize(scale, pixels) - : -1; - return { - min: min, pixels: pixels, - start: start, - end: end, - stackCount: stackCount, + start: scale._startPixel, + end: scale._endPixel, + stackCount: me.getStackCount(), scale: scale }; }, @@ -4131,31 +5067,35 @@ * Note: pixel values are not clamped to the scale area. * @private */ - calculateBarValuePixels: function (datasetIndex, index) { + calculateBarValuePixels: function (datasetIndex, index, options) { var me = this; var chart = me.chart; - var meta = me.getMeta(); var scale = me._getValueScale(); var isHorizontal = scale.isHorizontal(); var datasets = chart.data.datasets; - var value = +scale.getRightValue(datasets[datasetIndex].data[index]); - var minBarLength = scale.options.minBarLength; + var metasets = scale._getMatchingVisibleMetas(me._type); + var value = scale._parseValue(datasets[datasetIndex].data[index]); + var minBarLength = options.minBarLength; var stacked = scale.options.stacked; - var stack = meta.stack; - var start = 0; - var i, imeta, ivalue, base, head, size; + var stack = me.getMeta().stack; + var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max; + var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max; + var ilen = metasets.length; + var i, imeta, ivalue, base, head, size, stackLength; if (stacked || (stacked === undefined && stack !== undefined)) { - for (i = 0; i < datasetIndex; ++i) { - imeta = chart.getDatasetMeta(i); + for (i = 0; i < ilen; ++i) { + imeta = metasets[i]; + + if (imeta.index === datasetIndex) { + break; + } - if (imeta.bar && - imeta.stack === stack && - imeta.controller._getValueScaleId() === scale.id && - chart.isDatasetVisible(i)) { + if (imeta.stack === stack) { + stackLength = scale._parseValue(datasets[imeta.index].data[index]); + ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min; - ivalue = +scale.getRightValue(datasets[i].data[index]); - if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { + if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) { start += ivalue; } } @@ -4163,12 +5103,12 @@ } base = scale.getPixelForValue(start); - head = scale.getPixelForValue(start + value); + head = scale.getPixelForValue(start + length); size = head - base; if (minBarLength !== undefined && Math.abs(size) < minBarLength) { size = minBarLength; - if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) { + if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) { head = base - minBarLength; } else { head = base + minBarLength; @@ -4186,9 +5126,8 @@ /** * @private */ - calculateBarIndexPixels: function (datasetIndex, index, ruler) { + calculateBarIndexPixels: function (datasetIndex, index, ruler, options) { var me = this; - var options = ruler.scale.options; var range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options) : computeFitCategoryTraits(index, ruler, options); @@ -4196,7 +5135,7 @@ var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); var size = Math.min( - helpers$1.valueOrDefault(options.maxBarThickness, Infinity), + valueOrDefault$3(options.maxBarThickness, Infinity), range.chunk * range.ratio); return { @@ -4219,7 +5158,8 @@ helpers$1.canvas.clipArea(chart.ctx, chart.chartArea); for (; i < ilen; ++i) { - if (!isNaN(scale.getRightValue(dataset.data[i]))) { + var val = scale._parseValue(dataset.data[i]); + if (!isNaN(val.min) && !isNaN(val.max)) { rects[i].draw(); } } @@ -4230,46 +5170,25 @@ /** * @private */ - _resolveElementOptions: function (rectangle, index) { + _resolveDataElementOptions: function () { var me = this; - var chart = me.chart; - var datasets = chart.data.datasets; - var dataset = datasets[me.index]; - var custom = rectangle.custom || {}; - var options = chart.options.elements.rectangle; - var values = {}; - var i, ilen, key; - - // Scriptable options - var context = { - chart: chart, - dataIndex: index, - dataset: dataset, - datasetIndex: me.index - }; + var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments)); + var indexOpts = me._getIndexScale().options; + var valueOpts = me._getValueScale().options; - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderSkipped', - 'borderWidth' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$1([ - custom[key], - dataset[key], - options[key] - ], context, index); - } + values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage); + values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness); + values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage); + values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness); + values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength); return values; } + }); - var valueOrDefault$3 = helpers$1.valueOrDefault; - var resolve$2 = helpers$1.options.resolve; + var valueOrDefault$4 = helpers$1.valueOrDefault; + var resolve$1 = helpers$1.options.resolve; core_defaults._set('bubble', { hover: { @@ -4310,6 +5229,22 @@ */ dataElementType: elements.Point, + /** + * @private + */ + _dataElementOptions: [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle', + 'rotation' + ], + /** * @protected */ @@ -4333,7 +5268,7 @@ var custom = point.custom || {}; var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); - var options = me._resolveElementOptions(point, index); + var options = me._resolveDataElementOptions(point, index); var data = me.getDataset().data[index]; var dsIndex = me.index; @@ -4376,25 +5311,22 @@ radius: model.radius }; - model.backgroundColor = valueOrDefault$3(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); - model.borderColor = valueOrDefault$3(options.hoverBorderColor, getHoverColor(options.borderColor)); - model.borderWidth = valueOrDefault$3(options.hoverBorderWidth, options.borderWidth); + model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor)); + model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor)); + model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth); model.radius = options.radius + options.hoverRadius; }, /** * @private */ - _resolveElementOptions: function (point, index) { + _resolveDataElementOptions: function (point, index) { var me = this; var chart = me.chart; - var datasets = chart.data.datasets; - var dataset = datasets[me.index]; + var dataset = me.getDataset(); var custom = point.custom || {}; - var options = chart.options.elements.point; - var data = dataset.data[index]; - var values = {}; - var i, ilen, key; + var data = dataset.data[index] || {}; + var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments); // Scriptable options var context = { @@ -4404,42 +5336,28 @@ datasetIndex: me.index }; - var keys = [ - 'backgroundColor', - 'borderColor', - 'borderWidth', - 'hoverBackgroundColor', - 'hoverBorderColor', - 'hoverBorderWidth', - 'hoverRadius', - 'hitRadius', - 'pointStyle', - 'rotation' - ]; - - for (i = 0, ilen = keys.length; i < ilen; ++i) { - key = keys[i]; - values[key] = resolve$2([ - custom[key], - dataset[key], - options[key] - ], context, index); + // In case values were cached (and thus frozen), we need to clone the values + if (me._cachedDataOpts === values) { + values = helpers$1.extend({}, values); } // Custom radius resolution - values.radius = resolve$2([ + values.radius = resolve$1([ custom.radius, - data ? data.r : undefined, - dataset.radius, - options.radius + data.r, + me._config.radius, + chart.options.elements.point.radius ], context, index); return values; } }); - var resolve$3 = helpers$1.options.resolve; - var valueOrDefault$4 = helpers$1.valueOrDefault; + var valueOrDefault$5 = helpers$1.valueOrDefault; + + var PI$1 = Math.PI; + var DOUBLE_PI$1 = PI$1 * 2; + var HALF_PI$1 = PI$1 / 2; core_defaults._set('doughnut', { animation: { @@ -4452,25 +5370,25 @@ mode: 'single' }, legendCallback: function (chart) { - var text = []; - text.push('