Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1 | /*! |
| 2 | * Chart.js |
| 3 | * http://chartjs.org/ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4 | * Version: 2.6.0 |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 5 | * |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6 | * Copyright 2017 Nick Downie |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 7 | * Released under the MIT license |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8 | * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 9 | */ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 11 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 12 | },{}],2:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 13 | /* MIT license */ |
| 14 | var colorNames = require(6); |
| 15 | |
| 16 | module.exports = { |
| 17 | getRgba: getRgba, |
| 18 | getHsla: getHsla, |
| 19 | getRgb: getRgb, |
| 20 | getHsl: getHsl, |
| 21 | getHwb: getHwb, |
| 22 | getAlpha: getAlpha, |
| 23 | |
| 24 | hexString: hexString, |
| 25 | rgbString: rgbString, |
| 26 | rgbaString: rgbaString, |
| 27 | percentString: percentString, |
| 28 | percentaString: percentaString, |
| 29 | hslString: hslString, |
| 30 | hslaString: hslaString, |
| 31 | hwbString: hwbString, |
| 32 | keyword: keyword |
| 33 | } |
| 34 | |
| 35 | function getRgba(string) { |
| 36 | if (!string) { |
| 37 | return; |
| 38 | } |
| 39 | var abbr = /^#([a-fA-F0-9]{3})$/, |
| 40 | hex = /^#([a-fA-F0-9]{6})$/, |
| 41 | rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, |
| 42 | per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, |
| 43 | keyword = /(\w+)/; |
| 44 | |
| 45 | var rgb = [0, 0, 0], |
| 46 | a = 1, |
| 47 | match = string.match(abbr); |
| 48 | if (match) { |
| 49 | match = match[1]; |
| 50 | for (var i = 0; i < rgb.length; i++) { |
| 51 | rgb[i] = parseInt(match[i] + match[i], 16); |
| 52 | } |
| 53 | } |
| 54 | else if (match = string.match(hex)) { |
| 55 | match = match[1]; |
| 56 | for (var i = 0; i < rgb.length; i++) { |
| 57 | rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); |
| 58 | } |
| 59 | } |
| 60 | else if (match = string.match(rgba)) { |
| 61 | for (var i = 0; i < rgb.length; i++) { |
| 62 | rgb[i] = parseInt(match[i + 1]); |
| 63 | } |
| 64 | a = parseFloat(match[4]); |
| 65 | } |
| 66 | else if (match = string.match(per)) { |
| 67 | for (var i = 0; i < rgb.length; i++) { |
| 68 | rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); |
| 69 | } |
| 70 | a = parseFloat(match[4]); |
| 71 | } |
| 72 | else if (match = string.match(keyword)) { |
| 73 | if (match[1] == "transparent") { |
| 74 | return [0, 0, 0, 0]; |
| 75 | } |
| 76 | rgb = colorNames[match[1]]; |
| 77 | if (!rgb) { |
| 78 | return; |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | for (var i = 0; i < rgb.length; i++) { |
| 83 | rgb[i] = scale(rgb[i], 0, 255); |
| 84 | } |
| 85 | if (!a && a != 0) { |
| 86 | a = 1; |
| 87 | } |
| 88 | else { |
| 89 | a = scale(a, 0, 1); |
| 90 | } |
| 91 | rgb[3] = a; |
| 92 | return rgb; |
| 93 | } |
| 94 | |
| 95 | function getHsla(string) { |
| 96 | if (!string) { |
| 97 | return; |
| 98 | } |
| 99 | var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; |
| 100 | var match = string.match(hsl); |
| 101 | if (match) { |
| 102 | var alpha = parseFloat(match[4]); |
| 103 | var h = scale(parseInt(match[1]), 0, 360), |
| 104 | s = scale(parseFloat(match[2]), 0, 100), |
| 105 | l = scale(parseFloat(match[3]), 0, 100), |
| 106 | a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); |
| 107 | return [h, s, l, a]; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | function getHwb(string) { |
| 112 | if (!string) { |
| 113 | return; |
| 114 | } |
| 115 | var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; |
| 116 | var match = string.match(hwb); |
| 117 | if (match) { |
| 118 | var alpha = parseFloat(match[4]); |
| 119 | var h = scale(parseInt(match[1]), 0, 360), |
| 120 | w = scale(parseFloat(match[2]), 0, 100), |
| 121 | b = scale(parseFloat(match[3]), 0, 100), |
| 122 | a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); |
| 123 | return [h, w, b, a]; |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | function getRgb(string) { |
| 128 | var rgba = getRgba(string); |
| 129 | return rgba && rgba.slice(0, 3); |
| 130 | } |
| 131 | |
| 132 | function getHsl(string) { |
| 133 | var hsla = getHsla(string); |
| 134 | return hsla && hsla.slice(0, 3); |
| 135 | } |
| 136 | |
| 137 | function getAlpha(string) { |
| 138 | var vals = getRgba(string); |
| 139 | if (vals) { |
| 140 | return vals[3]; |
| 141 | } |
| 142 | else if (vals = getHsla(string)) { |
| 143 | return vals[3]; |
| 144 | } |
| 145 | else if (vals = getHwb(string)) { |
| 146 | return vals[3]; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | // generators |
| 151 | function hexString(rgb) { |
| 152 | return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) |
| 153 | + hexDouble(rgb[2]); |
| 154 | } |
| 155 | |
| 156 | function rgbString(rgba, alpha) { |
| 157 | if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { |
| 158 | return rgbaString(rgba, alpha); |
| 159 | } |
| 160 | return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; |
| 161 | } |
| 162 | |
| 163 | function rgbaString(rgba, alpha) { |
| 164 | if (alpha === undefined) { |
| 165 | alpha = (rgba[3] !== undefined ? rgba[3] : 1); |
| 166 | } |
| 167 | return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] |
| 168 | + ", " + alpha + ")"; |
| 169 | } |
| 170 | |
| 171 | function percentString(rgba, alpha) { |
| 172 | if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { |
| 173 | return percentaString(rgba, alpha); |
| 174 | } |
| 175 | var r = Math.round(rgba[0]/255 * 100), |
| 176 | g = Math.round(rgba[1]/255 * 100), |
| 177 | b = Math.round(rgba[2]/255 * 100); |
| 178 | |
| 179 | return "rgb(" + r + "%, " + g + "%, " + b + "%)"; |
| 180 | } |
| 181 | |
| 182 | function percentaString(rgba, alpha) { |
| 183 | var r = Math.round(rgba[0]/255 * 100), |
| 184 | g = Math.round(rgba[1]/255 * 100), |
| 185 | b = Math.round(rgba[2]/255 * 100); |
| 186 | return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; |
| 187 | } |
| 188 | |
| 189 | function hslString(hsla, alpha) { |
| 190 | if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { |
| 191 | return hslaString(hsla, alpha); |
| 192 | } |
| 193 | return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; |
| 194 | } |
| 195 | |
| 196 | function hslaString(hsla, alpha) { |
| 197 | if (alpha === undefined) { |
| 198 | alpha = (hsla[3] !== undefined ? hsla[3] : 1); |
| 199 | } |
| 200 | return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " |
| 201 | + alpha + ")"; |
| 202 | } |
| 203 | |
| 204 | // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax |
| 205 | // (hwb have alpha optional & 1 is default value) |
| 206 | function hwbString(hwb, alpha) { |
| 207 | if (alpha === undefined) { |
| 208 | alpha = (hwb[3] !== undefined ? hwb[3] : 1); |
| 209 | } |
| 210 | return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" |
| 211 | + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; |
| 212 | } |
| 213 | |
| 214 | function keyword(rgb) { |
| 215 | return reverseNames[rgb.slice(0, 3)]; |
| 216 | } |
| 217 | |
| 218 | // helpers |
| 219 | function scale(num, min, max) { |
| 220 | return Math.min(Math.max(min, num), max); |
| 221 | } |
| 222 | |
| 223 | function hexDouble(num) { |
| 224 | var str = num.toString(16).toUpperCase(); |
| 225 | return (str.length < 2) ? "0" + str : str; |
| 226 | } |
| 227 | |
| 228 | |
| 229 | //create a list of reverse color names |
| 230 | var reverseNames = {}; |
| 231 | for (var name in colorNames) { |
| 232 | reverseNames[colorNames[name]] = name; |
| 233 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 234 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 235 | },{"6":6}],3:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 236 | /* MIT license */ |
| 237 | var convert = require(5); |
| 238 | var string = require(2); |
| 239 | |
| 240 | var Color = function (obj) { |
| 241 | if (obj instanceof Color) { |
| 242 | return obj; |
| 243 | } |
| 244 | if (!(this instanceof Color)) { |
| 245 | return new Color(obj); |
| 246 | } |
| 247 | |
| 248 | this.valid = false; |
| 249 | this.values = { |
| 250 | rgb: [0, 0, 0], |
| 251 | hsl: [0, 0, 0], |
| 252 | hsv: [0, 0, 0], |
| 253 | hwb: [0, 0, 0], |
| 254 | cmyk: [0, 0, 0, 0], |
| 255 | alpha: 1 |
| 256 | }; |
| 257 | |
| 258 | // parse Color() argument |
| 259 | var vals; |
| 260 | if (typeof obj === 'string') { |
| 261 | vals = string.getRgba(obj); |
| 262 | if (vals) { |
| 263 | this.setValues('rgb', vals); |
| 264 | } else if (vals = string.getHsla(obj)) { |
| 265 | this.setValues('hsl', vals); |
| 266 | } else if (vals = string.getHwb(obj)) { |
| 267 | this.setValues('hwb', vals); |
| 268 | } |
| 269 | } else if (typeof obj === 'object') { |
| 270 | vals = obj; |
| 271 | if (vals.r !== undefined || vals.red !== undefined) { |
| 272 | this.setValues('rgb', vals); |
| 273 | } else if (vals.l !== undefined || vals.lightness !== undefined) { |
| 274 | this.setValues('hsl', vals); |
| 275 | } else if (vals.v !== undefined || vals.value !== undefined) { |
| 276 | this.setValues('hsv', vals); |
| 277 | } else if (vals.w !== undefined || vals.whiteness !== undefined) { |
| 278 | this.setValues('hwb', vals); |
| 279 | } else if (vals.c !== undefined || vals.cyan !== undefined) { |
| 280 | this.setValues('cmyk', vals); |
| 281 | } |
| 282 | } |
| 283 | }; |
| 284 | |
| 285 | Color.prototype = { |
| 286 | isValid: function () { |
| 287 | return this.valid; |
| 288 | }, |
| 289 | rgb: function () { |
| 290 | return this.setSpace('rgb', arguments); |
| 291 | }, |
| 292 | hsl: function () { |
| 293 | return this.setSpace('hsl', arguments); |
| 294 | }, |
| 295 | hsv: function () { |
| 296 | return this.setSpace('hsv', arguments); |
| 297 | }, |
| 298 | hwb: function () { |
| 299 | return this.setSpace('hwb', arguments); |
| 300 | }, |
| 301 | cmyk: function () { |
| 302 | return this.setSpace('cmyk', arguments); |
| 303 | }, |
| 304 | |
| 305 | rgbArray: function () { |
| 306 | return this.values.rgb; |
| 307 | }, |
| 308 | hslArray: function () { |
| 309 | return this.values.hsl; |
| 310 | }, |
| 311 | hsvArray: function () { |
| 312 | return this.values.hsv; |
| 313 | }, |
| 314 | hwbArray: function () { |
| 315 | var values = this.values; |
| 316 | if (values.alpha !== 1) { |
| 317 | return values.hwb.concat([values.alpha]); |
| 318 | } |
| 319 | return values.hwb; |
| 320 | }, |
| 321 | cmykArray: function () { |
| 322 | return this.values.cmyk; |
| 323 | }, |
| 324 | rgbaArray: function () { |
| 325 | var values = this.values; |
| 326 | return values.rgb.concat([values.alpha]); |
| 327 | }, |
| 328 | hslaArray: function () { |
| 329 | var values = this.values; |
| 330 | return values.hsl.concat([values.alpha]); |
| 331 | }, |
| 332 | alpha: function (val) { |
| 333 | if (val === undefined) { |
| 334 | return this.values.alpha; |
| 335 | } |
| 336 | this.setValues('alpha', val); |
| 337 | return this; |
| 338 | }, |
| 339 | |
| 340 | red: function (val) { |
| 341 | return this.setChannel('rgb', 0, val); |
| 342 | }, |
| 343 | green: function (val) { |
| 344 | return this.setChannel('rgb', 1, val); |
| 345 | }, |
| 346 | blue: function (val) { |
| 347 | return this.setChannel('rgb', 2, val); |
| 348 | }, |
| 349 | hue: function (val) { |
| 350 | if (val) { |
| 351 | val %= 360; |
| 352 | val = val < 0 ? 360 + val : val; |
| 353 | } |
| 354 | return this.setChannel('hsl', 0, val); |
| 355 | }, |
| 356 | saturation: function (val) { |
| 357 | return this.setChannel('hsl', 1, val); |
| 358 | }, |
| 359 | lightness: function (val) { |
| 360 | return this.setChannel('hsl', 2, val); |
| 361 | }, |
| 362 | saturationv: function (val) { |
| 363 | return this.setChannel('hsv', 1, val); |
| 364 | }, |
| 365 | whiteness: function (val) { |
| 366 | return this.setChannel('hwb', 1, val); |
| 367 | }, |
| 368 | blackness: function (val) { |
| 369 | return this.setChannel('hwb', 2, val); |
| 370 | }, |
| 371 | value: function (val) { |
| 372 | return this.setChannel('hsv', 2, val); |
| 373 | }, |
| 374 | cyan: function (val) { |
| 375 | return this.setChannel('cmyk', 0, val); |
| 376 | }, |
| 377 | magenta: function (val) { |
| 378 | return this.setChannel('cmyk', 1, val); |
| 379 | }, |
| 380 | yellow: function (val) { |
| 381 | return this.setChannel('cmyk', 2, val); |
| 382 | }, |
| 383 | black: function (val) { |
| 384 | return this.setChannel('cmyk', 3, val); |
| 385 | }, |
| 386 | |
| 387 | hexString: function () { |
| 388 | return string.hexString(this.values.rgb); |
| 389 | }, |
| 390 | rgbString: function () { |
| 391 | return string.rgbString(this.values.rgb, this.values.alpha); |
| 392 | }, |
| 393 | rgbaString: function () { |
| 394 | return string.rgbaString(this.values.rgb, this.values.alpha); |
| 395 | }, |
| 396 | percentString: function () { |
| 397 | return string.percentString(this.values.rgb, this.values.alpha); |
| 398 | }, |
| 399 | hslString: function () { |
| 400 | return string.hslString(this.values.hsl, this.values.alpha); |
| 401 | }, |
| 402 | hslaString: function () { |
| 403 | return string.hslaString(this.values.hsl, this.values.alpha); |
| 404 | }, |
| 405 | hwbString: function () { |
| 406 | return string.hwbString(this.values.hwb, this.values.alpha); |
| 407 | }, |
| 408 | keyword: function () { |
| 409 | return string.keyword(this.values.rgb, this.values.alpha); |
| 410 | }, |
| 411 | |
| 412 | rgbNumber: function () { |
| 413 | var rgb = this.values.rgb; |
| 414 | return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; |
| 415 | }, |
| 416 | |
| 417 | luminosity: function () { |
| 418 | // http://www.w3.org/TR/WCAG20/#relativeluminancedef |
| 419 | var rgb = this.values.rgb; |
| 420 | var lum = []; |
| 421 | for (var i = 0; i < rgb.length; i++) { |
| 422 | var chan = rgb[i] / 255; |
| 423 | lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); |
| 424 | } |
| 425 | return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; |
| 426 | }, |
| 427 | |
| 428 | contrast: function (color2) { |
| 429 | // http://www.w3.org/TR/WCAG20/#contrast-ratiodef |
| 430 | var lum1 = this.luminosity(); |
| 431 | var lum2 = color2.luminosity(); |
| 432 | if (lum1 > lum2) { |
| 433 | return (lum1 + 0.05) / (lum2 + 0.05); |
| 434 | } |
| 435 | return (lum2 + 0.05) / (lum1 + 0.05); |
| 436 | }, |
| 437 | |
| 438 | level: function (color2) { |
| 439 | var contrastRatio = this.contrast(color2); |
| 440 | if (contrastRatio >= 7.1) { |
| 441 | return 'AAA'; |
| 442 | } |
| 443 | |
| 444 | return (contrastRatio >= 4.5) ? 'AA' : ''; |
| 445 | }, |
| 446 | |
| 447 | dark: function () { |
| 448 | // YIQ equation from http://24ways.org/2010/calculating-color-contrast |
| 449 | var rgb = this.values.rgb; |
| 450 | var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; |
| 451 | return yiq < 128; |
| 452 | }, |
| 453 | |
| 454 | light: function () { |
| 455 | return !this.dark(); |
| 456 | }, |
| 457 | |
| 458 | negate: function () { |
| 459 | var rgb = []; |
| 460 | for (var i = 0; i < 3; i++) { |
| 461 | rgb[i] = 255 - this.values.rgb[i]; |
| 462 | } |
| 463 | this.setValues('rgb', rgb); |
| 464 | return this; |
| 465 | }, |
| 466 | |
| 467 | lighten: function (ratio) { |
| 468 | var hsl = this.values.hsl; |
| 469 | hsl[2] += hsl[2] * ratio; |
| 470 | this.setValues('hsl', hsl); |
| 471 | return this; |
| 472 | }, |
| 473 | |
| 474 | darken: function (ratio) { |
| 475 | var hsl = this.values.hsl; |
| 476 | hsl[2] -= hsl[2] * ratio; |
| 477 | this.setValues('hsl', hsl); |
| 478 | return this; |
| 479 | }, |
| 480 | |
| 481 | saturate: function (ratio) { |
| 482 | var hsl = this.values.hsl; |
| 483 | hsl[1] += hsl[1] * ratio; |
| 484 | this.setValues('hsl', hsl); |
| 485 | return this; |
| 486 | }, |
| 487 | |
| 488 | desaturate: function (ratio) { |
| 489 | var hsl = this.values.hsl; |
| 490 | hsl[1] -= hsl[1] * ratio; |
| 491 | this.setValues('hsl', hsl); |
| 492 | return this; |
| 493 | }, |
| 494 | |
| 495 | whiten: function (ratio) { |
| 496 | var hwb = this.values.hwb; |
| 497 | hwb[1] += hwb[1] * ratio; |
| 498 | this.setValues('hwb', hwb); |
| 499 | return this; |
| 500 | }, |
| 501 | |
| 502 | blacken: function (ratio) { |
| 503 | var hwb = this.values.hwb; |
| 504 | hwb[2] += hwb[2] * ratio; |
| 505 | this.setValues('hwb', hwb); |
| 506 | return this; |
| 507 | }, |
| 508 | |
| 509 | greyscale: function () { |
| 510 | var rgb = this.values.rgb; |
| 511 | // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale |
| 512 | var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; |
| 513 | this.setValues('rgb', [val, val, val]); |
| 514 | return this; |
| 515 | }, |
| 516 | |
| 517 | clearer: function (ratio) { |
| 518 | var alpha = this.values.alpha; |
| 519 | this.setValues('alpha', alpha - (alpha * ratio)); |
| 520 | return this; |
| 521 | }, |
| 522 | |
| 523 | opaquer: function (ratio) { |
| 524 | var alpha = this.values.alpha; |
| 525 | this.setValues('alpha', alpha + (alpha * ratio)); |
| 526 | return this; |
| 527 | }, |
| 528 | |
| 529 | rotate: function (degrees) { |
| 530 | var hsl = this.values.hsl; |
| 531 | var hue = (hsl[0] + degrees) % 360; |
| 532 | hsl[0] = hue < 0 ? 360 + hue : hue; |
| 533 | this.setValues('hsl', hsl); |
| 534 | return this; |
| 535 | }, |
| 536 | |
| 537 | /** |
| 538 | * Ported from sass implementation in C |
| 539 | * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 |
| 540 | */ |
| 541 | mix: function (mixinColor, weight) { |
| 542 | var color1 = this; |
| 543 | var color2 = mixinColor; |
| 544 | var p = weight === undefined ? 0.5 : weight; |
| 545 | |
| 546 | var w = 2 * p - 1; |
| 547 | var a = color1.alpha() - color2.alpha(); |
| 548 | |
| 549 | var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; |
| 550 | var w2 = 1 - w1; |
| 551 | |
| 552 | return this |
| 553 | .rgb( |
| 554 | w1 * color1.red() + w2 * color2.red(), |
| 555 | w1 * color1.green() + w2 * color2.green(), |
| 556 | w1 * color1.blue() + w2 * color2.blue() |
| 557 | ) |
| 558 | .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); |
| 559 | }, |
| 560 | |
| 561 | toJSON: function () { |
| 562 | return this.rgb(); |
| 563 | }, |
| 564 | |
| 565 | clone: function () { |
| 566 | // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, |
| 567 | // making the final build way to big to embed in Chart.js. So let's do it manually, |
| 568 | // assuming that values to clone are 1 dimension arrays containing only numbers, |
| 569 | // except 'alpha' which is a number. |
| 570 | var result = new Color(); |
| 571 | var source = this.values; |
| 572 | var target = result.values; |
| 573 | var value, type; |
| 574 | |
| 575 | for (var prop in source) { |
| 576 | if (source.hasOwnProperty(prop)) { |
| 577 | value = source[prop]; |
| 578 | type = ({}).toString.call(value); |
| 579 | if (type === '[object Array]') { |
| 580 | target[prop] = value.slice(0); |
| 581 | } else if (type === '[object Number]') { |
| 582 | target[prop] = value; |
| 583 | } else { |
| 584 | console.error('unexpected color value:', value); |
| 585 | } |
| 586 | } |
| 587 | } |
| 588 | |
| 589 | return result; |
| 590 | } |
| 591 | }; |
| 592 | |
| 593 | Color.prototype.spaces = { |
| 594 | rgb: ['red', 'green', 'blue'], |
| 595 | hsl: ['hue', 'saturation', 'lightness'], |
| 596 | hsv: ['hue', 'saturation', 'value'], |
| 597 | hwb: ['hue', 'whiteness', 'blackness'], |
| 598 | cmyk: ['cyan', 'magenta', 'yellow', 'black'] |
| 599 | }; |
| 600 | |
| 601 | Color.prototype.maxes = { |
| 602 | rgb: [255, 255, 255], |
| 603 | hsl: [360, 100, 100], |
| 604 | hsv: [360, 100, 100], |
| 605 | hwb: [360, 100, 100], |
| 606 | cmyk: [100, 100, 100, 100] |
| 607 | }; |
| 608 | |
| 609 | Color.prototype.getValues = function (space) { |
| 610 | var values = this.values; |
| 611 | var vals = {}; |
| 612 | |
| 613 | for (var i = 0; i < space.length; i++) { |
| 614 | vals[space.charAt(i)] = values[space][i]; |
| 615 | } |
| 616 | |
| 617 | if (values.alpha !== 1) { |
| 618 | vals.a = values.alpha; |
| 619 | } |
| 620 | |
| 621 | // {r: 255, g: 255, b: 255, a: 0.4} |
| 622 | return vals; |
| 623 | }; |
| 624 | |
| 625 | Color.prototype.setValues = function (space, vals) { |
| 626 | var values = this.values; |
| 627 | var spaces = this.spaces; |
| 628 | var maxes = this.maxes; |
| 629 | var alpha = 1; |
| 630 | var i; |
| 631 | |
| 632 | this.valid = true; |
| 633 | |
| 634 | if (space === 'alpha') { |
| 635 | alpha = vals; |
| 636 | } else if (vals.length) { |
| 637 | // [10, 10, 10] |
| 638 | values[space] = vals.slice(0, space.length); |
| 639 | alpha = vals[space.length]; |
| 640 | } else if (vals[space.charAt(0)] !== undefined) { |
| 641 | // {r: 10, g: 10, b: 10} |
| 642 | for (i = 0; i < space.length; i++) { |
| 643 | values[space][i] = vals[space.charAt(i)]; |
| 644 | } |
| 645 | |
| 646 | alpha = vals.a; |
| 647 | } else if (vals[spaces[space][0]] !== undefined) { |
| 648 | // {red: 10, green: 10, blue: 10} |
| 649 | var chans = spaces[space]; |
| 650 | |
| 651 | for (i = 0; i < space.length; i++) { |
| 652 | values[space][i] = vals[chans[i]]; |
| 653 | } |
| 654 | |
| 655 | alpha = vals.alpha; |
| 656 | } |
| 657 | |
| 658 | values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); |
| 659 | |
| 660 | if (space === 'alpha') { |
| 661 | return false; |
| 662 | } |
| 663 | |
| 664 | var capped; |
| 665 | |
| 666 | // cap values of the space prior converting all values |
| 667 | for (i = 0; i < space.length; i++) { |
| 668 | capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); |
| 669 | values[space][i] = Math.round(capped); |
| 670 | } |
| 671 | |
| 672 | // convert to all the other color spaces |
| 673 | for (var sname in spaces) { |
| 674 | if (sname !== space) { |
| 675 | values[sname] = convert[space][sname](values[space]); |
| 676 | } |
| 677 | } |
| 678 | |
| 679 | return true; |
| 680 | }; |
| 681 | |
| 682 | Color.prototype.setSpace = function (space, args) { |
| 683 | var vals = args[0]; |
| 684 | |
| 685 | if (vals === undefined) { |
| 686 | // color.rgb() |
| 687 | return this.getValues(space); |
| 688 | } |
| 689 | |
| 690 | // color.rgb(10, 10, 10) |
| 691 | if (typeof vals === 'number') { |
| 692 | vals = Array.prototype.slice.call(args); |
| 693 | } |
| 694 | |
| 695 | this.setValues(space, vals); |
| 696 | return this; |
| 697 | }; |
| 698 | |
| 699 | Color.prototype.setChannel = function (space, index, val) { |
| 700 | var svalues = this.values[space]; |
| 701 | if (val === undefined) { |
| 702 | // color.red() |
| 703 | return svalues[index]; |
| 704 | } else if (val === svalues[index]) { |
| 705 | // color.red(color.red()) |
| 706 | return this; |
| 707 | } |
| 708 | |
| 709 | // color.red(100) |
| 710 | svalues[index] = val; |
| 711 | this.setValues(space, svalues); |
| 712 | |
| 713 | return this; |
| 714 | }; |
| 715 | |
| 716 | if (typeof window !== 'undefined') { |
| 717 | window.Color = Color; |
| 718 | } |
| 719 | |
| 720 | module.exports = Color; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 721 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 722 | },{"2":2,"5":5}],4:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 723 | /* MIT license */ |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 724 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 725 | module.exports = { |
| 726 | rgb2hsl: rgb2hsl, |
| 727 | rgb2hsv: rgb2hsv, |
| 728 | rgb2hwb: rgb2hwb, |
| 729 | rgb2cmyk: rgb2cmyk, |
| 730 | rgb2keyword: rgb2keyword, |
| 731 | rgb2xyz: rgb2xyz, |
| 732 | rgb2lab: rgb2lab, |
| 733 | rgb2lch: rgb2lch, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 734 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 735 | hsl2rgb: hsl2rgb, |
| 736 | hsl2hsv: hsl2hsv, |
| 737 | hsl2hwb: hsl2hwb, |
| 738 | hsl2cmyk: hsl2cmyk, |
| 739 | hsl2keyword: hsl2keyword, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 740 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 741 | hsv2rgb: hsv2rgb, |
| 742 | hsv2hsl: hsv2hsl, |
| 743 | hsv2hwb: hsv2hwb, |
| 744 | hsv2cmyk: hsv2cmyk, |
| 745 | hsv2keyword: hsv2keyword, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 746 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 747 | hwb2rgb: hwb2rgb, |
| 748 | hwb2hsl: hwb2hsl, |
| 749 | hwb2hsv: hwb2hsv, |
| 750 | hwb2cmyk: hwb2cmyk, |
| 751 | hwb2keyword: hwb2keyword, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 752 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 753 | cmyk2rgb: cmyk2rgb, |
| 754 | cmyk2hsl: cmyk2hsl, |
| 755 | cmyk2hsv: cmyk2hsv, |
| 756 | cmyk2hwb: cmyk2hwb, |
| 757 | cmyk2keyword: cmyk2keyword, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 758 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 759 | keyword2rgb: keyword2rgb, |
| 760 | keyword2hsl: keyword2hsl, |
| 761 | keyword2hsv: keyword2hsv, |
| 762 | keyword2hwb: keyword2hwb, |
| 763 | keyword2cmyk: keyword2cmyk, |
| 764 | keyword2lab: keyword2lab, |
| 765 | keyword2xyz: keyword2xyz, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 766 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 767 | xyz2rgb: xyz2rgb, |
| 768 | xyz2lab: xyz2lab, |
| 769 | xyz2lch: xyz2lch, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 770 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 771 | lab2xyz: lab2xyz, |
| 772 | lab2rgb: lab2rgb, |
| 773 | lab2lch: lab2lch, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 774 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 775 | lch2lab: lch2lab, |
| 776 | lch2xyz: lch2xyz, |
| 777 | lch2rgb: lch2rgb |
| 778 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 779 | |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 780 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 781 | function rgb2hsl(rgb) { |
| 782 | var r = rgb[0]/255, |
| 783 | g = rgb[1]/255, |
| 784 | b = rgb[2]/255, |
| 785 | min = Math.min(r, g, b), |
| 786 | max = Math.max(r, g, b), |
| 787 | delta = max - min, |
| 788 | h, s, l; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 789 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 790 | if (max == min) |
| 791 | h = 0; |
| 792 | else if (r == max) |
| 793 | h = (g - b) / delta; |
| 794 | else if (g == max) |
| 795 | h = 2 + (b - r) / delta; |
| 796 | else if (b == max) |
| 797 | h = 4 + (r - g)/ delta; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 798 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 799 | h = Math.min(h * 60, 360); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 800 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 801 | if (h < 0) |
| 802 | h += 360; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 803 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 804 | l = (min + max) / 2; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 805 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 806 | if (max == min) |
| 807 | s = 0; |
| 808 | else if (l <= 0.5) |
| 809 | s = delta / (max + min); |
| 810 | else |
| 811 | s = delta / (2 - max - min); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 812 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 813 | return [h, s * 100, l * 100]; |
| 814 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 815 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 816 | function rgb2hsv(rgb) { |
| 817 | var r = rgb[0], |
| 818 | g = rgb[1], |
| 819 | b = rgb[2], |
| 820 | min = Math.min(r, g, b), |
| 821 | max = Math.max(r, g, b), |
| 822 | delta = max - min, |
| 823 | h, s, v; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 824 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 825 | if (max == 0) |
| 826 | s = 0; |
| 827 | else |
| 828 | s = (delta/max * 1000)/10; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 829 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 830 | if (max == min) |
| 831 | h = 0; |
| 832 | else if (r == max) |
| 833 | h = (g - b) / delta; |
| 834 | else if (g == max) |
| 835 | h = 2 + (b - r) / delta; |
| 836 | else if (b == max) |
| 837 | h = 4 + (r - g) / delta; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 838 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 839 | h = Math.min(h * 60, 360); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 840 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 841 | if (h < 0) |
| 842 | h += 360; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 843 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 844 | v = ((max / 255) * 1000) / 10; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 845 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 846 | return [h, s, v]; |
| 847 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 848 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 849 | function rgb2hwb(rgb) { |
| 850 | var r = rgb[0], |
| 851 | g = rgb[1], |
| 852 | b = rgb[2], |
| 853 | h = rgb2hsl(rgb)[0], |
| 854 | w = 1/255 * Math.min(r, Math.min(g, b)), |
| 855 | b = 1 - 1/255 * Math.max(r, Math.max(g, b)); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 856 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 857 | return [h, w * 100, b * 100]; |
| 858 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 859 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 860 | function rgb2cmyk(rgb) { |
| 861 | var r = rgb[0] / 255, |
| 862 | g = rgb[1] / 255, |
| 863 | b = rgb[2] / 255, |
| 864 | c, m, y, k; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 865 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 866 | k = Math.min(1 - r, 1 - g, 1 - b); |
| 867 | c = (1 - r - k) / (1 - k) || 0; |
| 868 | m = (1 - g - k) / (1 - k) || 0; |
| 869 | y = (1 - b - k) / (1 - k) || 0; |
| 870 | return [c * 100, m * 100, y * 100, k * 100]; |
| 871 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 872 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 873 | function rgb2keyword(rgb) { |
| 874 | return reverseKeywords[JSON.stringify(rgb)]; |
| 875 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 876 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 877 | function rgb2xyz(rgb) { |
| 878 | var r = rgb[0] / 255, |
| 879 | g = rgb[1] / 255, |
| 880 | b = rgb[2] / 255; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 881 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 882 | // assume sRGB |
| 883 | r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); |
| 884 | g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); |
| 885 | b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 886 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 887 | var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); |
| 888 | var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); |
| 889 | var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 890 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 891 | return [x * 100, y *100, z * 100]; |
| 892 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 893 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 894 | function rgb2lab(rgb) { |
| 895 | var xyz = rgb2xyz(rgb), |
| 896 | x = xyz[0], |
| 897 | y = xyz[1], |
| 898 | z = xyz[2], |
| 899 | l, a, b; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 900 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 901 | x /= 95.047; |
| 902 | y /= 100; |
| 903 | z /= 108.883; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 904 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 905 | x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); |
| 906 | y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); |
| 907 | z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 908 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 909 | l = (116 * y) - 16; |
| 910 | a = 500 * (x - y); |
| 911 | b = 200 * (y - z); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 912 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 913 | return [l, a, b]; |
| 914 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 915 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 916 | function rgb2lch(args) { |
| 917 | return lab2lch(rgb2lab(args)); |
| 918 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 919 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 920 | function hsl2rgb(hsl) { |
| 921 | var h = hsl[0] / 360, |
| 922 | s = hsl[1] / 100, |
| 923 | l = hsl[2] / 100, |
| 924 | t1, t2, t3, rgb, val; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 925 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 926 | if (s == 0) { |
| 927 | val = l * 255; |
| 928 | return [val, val, val]; |
| 929 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 930 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 931 | if (l < 0.5) |
| 932 | t2 = l * (1 + s); |
| 933 | else |
| 934 | t2 = l + s - l * s; |
| 935 | t1 = 2 * l - t2; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 936 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 937 | rgb = [0, 0, 0]; |
| 938 | for (var i = 0; i < 3; i++) { |
| 939 | t3 = h + 1 / 3 * - (i - 1); |
| 940 | t3 < 0 && t3++; |
| 941 | t3 > 1 && t3--; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 942 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 943 | if (6 * t3 < 1) |
| 944 | val = t1 + (t2 - t1) * 6 * t3; |
| 945 | else if (2 * t3 < 1) |
| 946 | val = t2; |
| 947 | else if (3 * t3 < 2) |
| 948 | val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; |
| 949 | else |
| 950 | val = t1; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 951 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 952 | rgb[i] = val * 255; |
| 953 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 954 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 955 | return rgb; |
| 956 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 957 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 958 | function hsl2hsv(hsl) { |
| 959 | var h = hsl[0], |
| 960 | s = hsl[1] / 100, |
| 961 | l = hsl[2] / 100, |
| 962 | sv, v; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 963 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 964 | if(l === 0) { |
| 965 | // no need to do calc on black |
| 966 | // also avoids divide by 0 error |
| 967 | return [0, 0, 0]; |
| 968 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 969 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 970 | l *= 2; |
| 971 | s *= (l <= 1) ? l : 2 - l; |
| 972 | v = (l + s) / 2; |
| 973 | sv = (2 * s) / (l + s); |
| 974 | return [h, sv * 100, v * 100]; |
| 975 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 976 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 977 | function hsl2hwb(args) { |
| 978 | return rgb2hwb(hsl2rgb(args)); |
| 979 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 980 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 981 | function hsl2cmyk(args) { |
| 982 | return rgb2cmyk(hsl2rgb(args)); |
| 983 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 984 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 985 | function hsl2keyword(args) { |
| 986 | return rgb2keyword(hsl2rgb(args)); |
| 987 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 988 | |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 989 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 990 | function hsv2rgb(hsv) { |
| 991 | var h = hsv[0] / 60, |
| 992 | s = hsv[1] / 100, |
| 993 | v = hsv[2] / 100, |
| 994 | hi = Math.floor(h) % 6; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 995 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 996 | var f = h - Math.floor(h), |
| 997 | p = 255 * v * (1 - s), |
| 998 | q = 255 * v * (1 - (s * f)), |
| 999 | t = 255 * v * (1 - (s * (1 - f))), |
| 1000 | v = 255 * v; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1001 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1002 | switch(hi) { |
| 1003 | case 0: |
| 1004 | return [v, t, p]; |
| 1005 | case 1: |
| 1006 | return [q, v, p]; |
| 1007 | case 2: |
| 1008 | return [p, v, t]; |
| 1009 | case 3: |
| 1010 | return [p, q, v]; |
| 1011 | case 4: |
| 1012 | return [t, p, v]; |
| 1013 | case 5: |
| 1014 | return [v, p, q]; |
| 1015 | } |
| 1016 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1017 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1018 | function hsv2hsl(hsv) { |
| 1019 | var h = hsv[0], |
| 1020 | s = hsv[1] / 100, |
| 1021 | v = hsv[2] / 100, |
| 1022 | sl, l; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1023 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1024 | l = (2 - s) * v; |
| 1025 | sl = s * v; |
| 1026 | sl /= (l <= 1) ? l : 2 - l; |
| 1027 | sl = sl || 0; |
| 1028 | l /= 2; |
| 1029 | return [h, sl * 100, l * 100]; |
| 1030 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1031 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1032 | function hsv2hwb(args) { |
| 1033 | return rgb2hwb(hsv2rgb(args)) |
| 1034 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1035 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1036 | function hsv2cmyk(args) { |
| 1037 | return rgb2cmyk(hsv2rgb(args)); |
| 1038 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1039 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1040 | function hsv2keyword(args) { |
| 1041 | return rgb2keyword(hsv2rgb(args)); |
| 1042 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1043 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1044 | // http://dev.w3.org/csswg/css-color/#hwb-to-rgb |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1045 | function hwb2rgb(hwb) { |
| 1046 | var h = hwb[0] / 360, |
| 1047 | wh = hwb[1] / 100, |
| 1048 | bl = hwb[2] / 100, |
| 1049 | ratio = wh + bl, |
| 1050 | i, v, f, n; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1051 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1052 | // wh + bl cant be > 1 |
| 1053 | if (ratio > 1) { |
| 1054 | wh /= ratio; |
| 1055 | bl /= ratio; |
| 1056 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1057 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1058 | i = Math.floor(6 * h); |
| 1059 | v = 1 - bl; |
| 1060 | f = 6 * h - i; |
| 1061 | if ((i & 0x01) != 0) { |
| 1062 | f = 1 - f; |
| 1063 | } |
| 1064 | n = wh + f * (v - wh); // linear interpolation |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1065 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1066 | switch (i) { |
| 1067 | default: |
| 1068 | case 6: |
| 1069 | case 0: r = v; g = n; b = wh; break; |
| 1070 | case 1: r = n; g = v; b = wh; break; |
| 1071 | case 2: r = wh; g = v; b = n; break; |
| 1072 | case 3: r = wh; g = n; b = v; break; |
| 1073 | case 4: r = n; g = wh; b = v; break; |
| 1074 | case 5: r = v; g = wh; b = n; break; |
| 1075 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1076 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1077 | return [r * 255, g * 255, b * 255]; |
| 1078 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1079 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1080 | function hwb2hsl(args) { |
| 1081 | return rgb2hsl(hwb2rgb(args)); |
| 1082 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1083 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1084 | function hwb2hsv(args) { |
| 1085 | return rgb2hsv(hwb2rgb(args)); |
| 1086 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1087 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1088 | function hwb2cmyk(args) { |
| 1089 | return rgb2cmyk(hwb2rgb(args)); |
| 1090 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1091 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1092 | function hwb2keyword(args) { |
| 1093 | return rgb2keyword(hwb2rgb(args)); |
| 1094 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1095 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1096 | function cmyk2rgb(cmyk) { |
| 1097 | var c = cmyk[0] / 100, |
| 1098 | m = cmyk[1] / 100, |
| 1099 | y = cmyk[2] / 100, |
| 1100 | k = cmyk[3] / 100, |
| 1101 | r, g, b; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1102 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1103 | r = 1 - Math.min(1, c * (1 - k) + k); |
| 1104 | g = 1 - Math.min(1, m * (1 - k) + k); |
| 1105 | b = 1 - Math.min(1, y * (1 - k) + k); |
| 1106 | return [r * 255, g * 255, b * 255]; |
| 1107 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1108 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1109 | function cmyk2hsl(args) { |
| 1110 | return rgb2hsl(cmyk2rgb(args)); |
| 1111 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1112 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1113 | function cmyk2hsv(args) { |
| 1114 | return rgb2hsv(cmyk2rgb(args)); |
| 1115 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1116 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1117 | function cmyk2hwb(args) { |
| 1118 | return rgb2hwb(cmyk2rgb(args)); |
| 1119 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1120 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1121 | function cmyk2keyword(args) { |
| 1122 | return rgb2keyword(cmyk2rgb(args)); |
| 1123 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1124 | |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1125 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1126 | function xyz2rgb(xyz) { |
| 1127 | var x = xyz[0] / 100, |
| 1128 | y = xyz[1] / 100, |
| 1129 | z = xyz[2] / 100, |
| 1130 | r, g, b; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1131 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1132 | r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); |
| 1133 | g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); |
| 1134 | b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1135 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1136 | // assume sRGB |
| 1137 | r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) |
| 1138 | : r = (r * 12.92); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1139 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1140 | g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) |
| 1141 | : g = (g * 12.92); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1142 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1143 | b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) |
| 1144 | : b = (b * 12.92); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1145 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1146 | r = Math.min(Math.max(0, r), 1); |
| 1147 | g = Math.min(Math.max(0, g), 1); |
| 1148 | b = Math.min(Math.max(0, b), 1); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1149 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1150 | return [r * 255, g * 255, b * 255]; |
| 1151 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1152 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1153 | function xyz2lab(xyz) { |
| 1154 | var x = xyz[0], |
| 1155 | y = xyz[1], |
| 1156 | z = xyz[2], |
| 1157 | l, a, b; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1158 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1159 | x /= 95.047; |
| 1160 | y /= 100; |
| 1161 | z /= 108.883; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1162 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1163 | x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); |
| 1164 | y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); |
| 1165 | z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1166 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1167 | l = (116 * y) - 16; |
| 1168 | a = 500 * (x - y); |
| 1169 | b = 200 * (y - z); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1170 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1171 | return [l, a, b]; |
| 1172 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1173 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1174 | function xyz2lch(args) { |
| 1175 | return lab2lch(xyz2lab(args)); |
| 1176 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1177 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1178 | function lab2xyz(lab) { |
| 1179 | var l = lab[0], |
| 1180 | a = lab[1], |
| 1181 | b = lab[2], |
| 1182 | x, y, z, y2; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1183 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1184 | if (l <= 8) { |
| 1185 | y = (l * 100) / 903.3; |
| 1186 | y2 = (7.787 * (y / 100)) + (16 / 116); |
| 1187 | } else { |
| 1188 | y = 100 * Math.pow((l + 16) / 116, 3); |
| 1189 | y2 = Math.pow(y / 100, 1/3); |
| 1190 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1191 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1192 | x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1193 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1194 | z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1195 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1196 | return [x, y, z]; |
| 1197 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1198 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1199 | function lab2lch(lab) { |
| 1200 | var l = lab[0], |
| 1201 | a = lab[1], |
| 1202 | b = lab[2], |
| 1203 | hr, h, c; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1204 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1205 | hr = Math.atan2(b, a); |
| 1206 | h = hr * 360 / 2 / Math.PI; |
| 1207 | if (h < 0) { |
| 1208 | h += 360; |
| 1209 | } |
| 1210 | c = Math.sqrt(a * a + b * b); |
| 1211 | return [l, c, h]; |
| 1212 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1213 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1214 | function lab2rgb(args) { |
| 1215 | return xyz2rgb(lab2xyz(args)); |
| 1216 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1217 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1218 | function lch2lab(lch) { |
| 1219 | var l = lch[0], |
| 1220 | c = lch[1], |
| 1221 | h = lch[2], |
| 1222 | a, b, hr; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1223 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1224 | hr = h / 360 * 2 * Math.PI; |
| 1225 | a = c * Math.cos(hr); |
| 1226 | b = c * Math.sin(hr); |
| 1227 | return [l, a, b]; |
| 1228 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1229 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1230 | function lch2xyz(args) { |
| 1231 | return lab2xyz(lch2lab(args)); |
| 1232 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1233 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1234 | function lch2rgb(args) { |
| 1235 | return lab2rgb(lch2lab(args)); |
| 1236 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1237 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1238 | function keyword2rgb(keyword) { |
| 1239 | return cssKeywords[keyword]; |
| 1240 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1241 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1242 | function keyword2hsl(args) { |
| 1243 | return rgb2hsl(keyword2rgb(args)); |
| 1244 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1245 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1246 | function keyword2hsv(args) { |
| 1247 | return rgb2hsv(keyword2rgb(args)); |
| 1248 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1249 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1250 | function keyword2hwb(args) { |
| 1251 | return rgb2hwb(keyword2rgb(args)); |
| 1252 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1253 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1254 | function keyword2cmyk(args) { |
| 1255 | return rgb2cmyk(keyword2rgb(args)); |
| 1256 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1257 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1258 | function keyword2lab(args) { |
| 1259 | return rgb2lab(keyword2rgb(args)); |
| 1260 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1261 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1262 | function keyword2xyz(args) { |
| 1263 | return rgb2xyz(keyword2rgb(args)); |
| 1264 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1265 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1266 | var cssKeywords = { |
| 1267 | aliceblue: [240,248,255], |
| 1268 | antiquewhite: [250,235,215], |
| 1269 | aqua: [0,255,255], |
| 1270 | aquamarine: [127,255,212], |
| 1271 | azure: [240,255,255], |
| 1272 | beige: [245,245,220], |
| 1273 | bisque: [255,228,196], |
| 1274 | black: [0,0,0], |
| 1275 | blanchedalmond: [255,235,205], |
| 1276 | blue: [0,0,255], |
| 1277 | blueviolet: [138,43,226], |
| 1278 | brown: [165,42,42], |
| 1279 | burlywood: [222,184,135], |
| 1280 | cadetblue: [95,158,160], |
| 1281 | chartreuse: [127,255,0], |
| 1282 | chocolate: [210,105,30], |
| 1283 | coral: [255,127,80], |
| 1284 | cornflowerblue: [100,149,237], |
| 1285 | cornsilk: [255,248,220], |
| 1286 | crimson: [220,20,60], |
| 1287 | cyan: [0,255,255], |
| 1288 | darkblue: [0,0,139], |
| 1289 | darkcyan: [0,139,139], |
| 1290 | darkgoldenrod: [184,134,11], |
| 1291 | darkgray: [169,169,169], |
| 1292 | darkgreen: [0,100,0], |
| 1293 | darkgrey: [169,169,169], |
| 1294 | darkkhaki: [189,183,107], |
| 1295 | darkmagenta: [139,0,139], |
| 1296 | darkolivegreen: [85,107,47], |
| 1297 | darkorange: [255,140,0], |
| 1298 | darkorchid: [153,50,204], |
| 1299 | darkred: [139,0,0], |
| 1300 | darksalmon: [233,150,122], |
| 1301 | darkseagreen: [143,188,143], |
| 1302 | darkslateblue: [72,61,139], |
| 1303 | darkslategray: [47,79,79], |
| 1304 | darkslategrey: [47,79,79], |
| 1305 | darkturquoise: [0,206,209], |
| 1306 | darkviolet: [148,0,211], |
| 1307 | deeppink: [255,20,147], |
| 1308 | deepskyblue: [0,191,255], |
| 1309 | dimgray: [105,105,105], |
| 1310 | dimgrey: [105,105,105], |
| 1311 | dodgerblue: [30,144,255], |
| 1312 | firebrick: [178,34,34], |
| 1313 | floralwhite: [255,250,240], |
| 1314 | forestgreen: [34,139,34], |
| 1315 | fuchsia: [255,0,255], |
| 1316 | gainsboro: [220,220,220], |
| 1317 | ghostwhite: [248,248,255], |
| 1318 | gold: [255,215,0], |
| 1319 | goldenrod: [218,165,32], |
| 1320 | gray: [128,128,128], |
| 1321 | green: [0,128,0], |
| 1322 | greenyellow: [173,255,47], |
| 1323 | grey: [128,128,128], |
| 1324 | honeydew: [240,255,240], |
| 1325 | hotpink: [255,105,180], |
| 1326 | indianred: [205,92,92], |
| 1327 | indigo: [75,0,130], |
| 1328 | ivory: [255,255,240], |
| 1329 | khaki: [240,230,140], |
| 1330 | lavender: [230,230,250], |
| 1331 | lavenderblush: [255,240,245], |
| 1332 | lawngreen: [124,252,0], |
| 1333 | lemonchiffon: [255,250,205], |
| 1334 | lightblue: [173,216,230], |
| 1335 | lightcoral: [240,128,128], |
| 1336 | lightcyan: [224,255,255], |
| 1337 | lightgoldenrodyellow: [250,250,210], |
| 1338 | lightgray: [211,211,211], |
| 1339 | lightgreen: [144,238,144], |
| 1340 | lightgrey: [211,211,211], |
| 1341 | lightpink: [255,182,193], |
| 1342 | lightsalmon: [255,160,122], |
| 1343 | lightseagreen: [32,178,170], |
| 1344 | lightskyblue: [135,206,250], |
| 1345 | lightslategray: [119,136,153], |
| 1346 | lightslategrey: [119,136,153], |
| 1347 | lightsteelblue: [176,196,222], |
| 1348 | lightyellow: [255,255,224], |
| 1349 | lime: [0,255,0], |
| 1350 | limegreen: [50,205,50], |
| 1351 | linen: [250,240,230], |
| 1352 | magenta: [255,0,255], |
| 1353 | maroon: [128,0,0], |
| 1354 | mediumaquamarine: [102,205,170], |
| 1355 | mediumblue: [0,0,205], |
| 1356 | mediumorchid: [186,85,211], |
| 1357 | mediumpurple: [147,112,219], |
| 1358 | mediumseagreen: [60,179,113], |
| 1359 | mediumslateblue: [123,104,238], |
| 1360 | mediumspringgreen: [0,250,154], |
| 1361 | mediumturquoise: [72,209,204], |
| 1362 | mediumvioletred: [199,21,133], |
| 1363 | midnightblue: [25,25,112], |
| 1364 | mintcream: [245,255,250], |
| 1365 | mistyrose: [255,228,225], |
| 1366 | moccasin: [255,228,181], |
| 1367 | navajowhite: [255,222,173], |
| 1368 | navy: [0,0,128], |
| 1369 | oldlace: [253,245,230], |
| 1370 | olive: [128,128,0], |
| 1371 | olivedrab: [107,142,35], |
| 1372 | orange: [255,165,0], |
| 1373 | orangered: [255,69,0], |
| 1374 | orchid: [218,112,214], |
| 1375 | palegoldenrod: [238,232,170], |
| 1376 | palegreen: [152,251,152], |
| 1377 | paleturquoise: [175,238,238], |
| 1378 | palevioletred: [219,112,147], |
| 1379 | papayawhip: [255,239,213], |
| 1380 | peachpuff: [255,218,185], |
| 1381 | peru: [205,133,63], |
| 1382 | pink: [255,192,203], |
| 1383 | plum: [221,160,221], |
| 1384 | powderblue: [176,224,230], |
| 1385 | purple: [128,0,128], |
| 1386 | rebeccapurple: [102, 51, 153], |
| 1387 | red: [255,0,0], |
| 1388 | rosybrown: [188,143,143], |
| 1389 | royalblue: [65,105,225], |
| 1390 | saddlebrown: [139,69,19], |
| 1391 | salmon: [250,128,114], |
| 1392 | sandybrown: [244,164,96], |
| 1393 | seagreen: [46,139,87], |
| 1394 | seashell: [255,245,238], |
| 1395 | sienna: [160,82,45], |
| 1396 | silver: [192,192,192], |
| 1397 | skyblue: [135,206,235], |
| 1398 | slateblue: [106,90,205], |
| 1399 | slategray: [112,128,144], |
| 1400 | slategrey: [112,128,144], |
| 1401 | snow: [255,250,250], |
| 1402 | springgreen: [0,255,127], |
| 1403 | steelblue: [70,130,180], |
| 1404 | tan: [210,180,140], |
| 1405 | teal: [0,128,128], |
| 1406 | thistle: [216,191,216], |
| 1407 | tomato: [255,99,71], |
| 1408 | turquoise: [64,224,208], |
| 1409 | violet: [238,130,238], |
| 1410 | wheat: [245,222,179], |
| 1411 | white: [255,255,255], |
| 1412 | whitesmoke: [245,245,245], |
| 1413 | yellow: [255,255,0], |
| 1414 | yellowgreen: [154,205,50] |
| 1415 | }; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1416 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1417 | var reverseKeywords = {}; |
| 1418 | for (var key in cssKeywords) { |
| 1419 | reverseKeywords[JSON.stringify(cssKeywords[key])] = key; |
| 1420 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1421 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1422 | },{}],5:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1423 | var conversions = require(4); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1424 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1425 | var convert = function() { |
| 1426 | return new Converter(); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1427 | } |
| 1428 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1429 | for (var func in conversions) { |
| 1430 | // export Raw versions |
| 1431 | convert[func + "Raw"] = (function(func) { |
| 1432 | // accept array or plain args |
| 1433 | return function(arg) { |
| 1434 | if (typeof arg == "number") |
| 1435 | arg = Array.prototype.slice.call(arguments); |
| 1436 | return conversions[func](arg); |
| 1437 | } |
| 1438 | })(func); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1439 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1440 | var pair = /(\w+)2(\w+)/.exec(func), |
| 1441 | from = pair[1], |
| 1442 | to = pair[2]; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1443 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1444 | // export rgb2hsl and ["rgb"]["hsl"] |
| 1445 | convert[from] = convert[from] || {}; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1446 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1447 | convert[from][to] = convert[func] = (function(func) { |
| 1448 | return function(arg) { |
| 1449 | if (typeof arg == "number") |
| 1450 | arg = Array.prototype.slice.call(arguments); |
| 1451 | |
| 1452 | var val = conversions[func](arg); |
| 1453 | if (typeof val == "string" || val === undefined) |
| 1454 | return val; // keyword |
| 1455 | |
| 1456 | for (var i = 0; i < val.length; i++) |
| 1457 | val[i] = Math.round(val[i]); |
| 1458 | return val; |
| 1459 | } |
| 1460 | })(func); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1461 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1462 | |
| 1463 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1464 | /* Converter does lazy conversion and caching */ |
| 1465 | var Converter = function() { |
| 1466 | this.convs = {}; |
| 1467 | }; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1468 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1469 | /* Either get the values for a space or |
| 1470 | set the values for a space, depending on args */ |
| 1471 | Converter.prototype.routeSpace = function(space, args) { |
| 1472 | var values = args[0]; |
| 1473 | if (values === undefined) { |
| 1474 | // color.rgb() |
| 1475 | return this.getValues(space); |
| 1476 | } |
| 1477 | // color.rgb(10, 10, 10) |
| 1478 | if (typeof values == "number") { |
| 1479 | values = Array.prototype.slice.call(args); |
| 1480 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1481 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1482 | return this.setValues(space, values); |
| 1483 | }; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1484 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1485 | /* Set the values for a space, invalidating cache */ |
| 1486 | Converter.prototype.setValues = function(space, values) { |
| 1487 | this.space = space; |
| 1488 | this.convs = {}; |
| 1489 | this.convs[space] = values; |
| 1490 | return this; |
| 1491 | }; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1492 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1493 | /* Get the values for a space. If there's already |
| 1494 | a conversion for the space, fetch it, otherwise |
| 1495 | compute it */ |
| 1496 | Converter.prototype.getValues = function(space) { |
| 1497 | var vals = this.convs[space]; |
| 1498 | if (!vals) { |
| 1499 | var fspace = this.space, |
| 1500 | from = this.convs[fspace]; |
| 1501 | vals = convert[fspace][space](from); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1502 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1503 | this.convs[space] = vals; |
| 1504 | } |
| 1505 | return vals; |
| 1506 | }; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1507 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1508 | ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { |
| 1509 | Converter.prototype[space] = function(vals) { |
| 1510 | return this.routeSpace(space, arguments); |
| 1511 | } |
| 1512 | }); |
| 1513 | |
| 1514 | module.exports = convert; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1515 | },{"4":4}],6:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1516 | module.exports = { |
| 1517 | "aliceblue": [240, 248, 255], |
| 1518 | "antiquewhite": [250, 235, 215], |
| 1519 | "aqua": [0, 255, 255], |
| 1520 | "aquamarine": [127, 255, 212], |
| 1521 | "azure": [240, 255, 255], |
| 1522 | "beige": [245, 245, 220], |
| 1523 | "bisque": [255, 228, 196], |
| 1524 | "black": [0, 0, 0], |
| 1525 | "blanchedalmond": [255, 235, 205], |
| 1526 | "blue": [0, 0, 255], |
| 1527 | "blueviolet": [138, 43, 226], |
| 1528 | "brown": [165, 42, 42], |
| 1529 | "burlywood": [222, 184, 135], |
| 1530 | "cadetblue": [95, 158, 160], |
| 1531 | "chartreuse": [127, 255, 0], |
| 1532 | "chocolate": [210, 105, 30], |
| 1533 | "coral": [255, 127, 80], |
| 1534 | "cornflowerblue": [100, 149, 237], |
| 1535 | "cornsilk": [255, 248, 220], |
| 1536 | "crimson": [220, 20, 60], |
| 1537 | "cyan": [0, 255, 255], |
| 1538 | "darkblue": [0, 0, 139], |
| 1539 | "darkcyan": [0, 139, 139], |
| 1540 | "darkgoldenrod": [184, 134, 11], |
| 1541 | "darkgray": [169, 169, 169], |
| 1542 | "darkgreen": [0, 100, 0], |
| 1543 | "darkgrey": [169, 169, 169], |
| 1544 | "darkkhaki": [189, 183, 107], |
| 1545 | "darkmagenta": [139, 0, 139], |
| 1546 | "darkolivegreen": [85, 107, 47], |
| 1547 | "darkorange": [255, 140, 0], |
| 1548 | "darkorchid": [153, 50, 204], |
| 1549 | "darkred": [139, 0, 0], |
| 1550 | "darksalmon": [233, 150, 122], |
| 1551 | "darkseagreen": [143, 188, 143], |
| 1552 | "darkslateblue": [72, 61, 139], |
| 1553 | "darkslategray": [47, 79, 79], |
| 1554 | "darkslategrey": [47, 79, 79], |
| 1555 | "darkturquoise": [0, 206, 209], |
| 1556 | "darkviolet": [148, 0, 211], |
| 1557 | "deeppink": [255, 20, 147], |
| 1558 | "deepskyblue": [0, 191, 255], |
| 1559 | "dimgray": [105, 105, 105], |
| 1560 | "dimgrey": [105, 105, 105], |
| 1561 | "dodgerblue": [30, 144, 255], |
| 1562 | "firebrick": [178, 34, 34], |
| 1563 | "floralwhite": [255, 250, 240], |
| 1564 | "forestgreen": [34, 139, 34], |
| 1565 | "fuchsia": [255, 0, 255], |
| 1566 | "gainsboro": [220, 220, 220], |
| 1567 | "ghostwhite": [248, 248, 255], |
| 1568 | "gold": [255, 215, 0], |
| 1569 | "goldenrod": [218, 165, 32], |
| 1570 | "gray": [128, 128, 128], |
| 1571 | "green": [0, 128, 0], |
| 1572 | "greenyellow": [173, 255, 47], |
| 1573 | "grey": [128, 128, 128], |
| 1574 | "honeydew": [240, 255, 240], |
| 1575 | "hotpink": [255, 105, 180], |
| 1576 | "indianred": [205, 92, 92], |
| 1577 | "indigo": [75, 0, 130], |
| 1578 | "ivory": [255, 255, 240], |
| 1579 | "khaki": [240, 230, 140], |
| 1580 | "lavender": [230, 230, 250], |
| 1581 | "lavenderblush": [255, 240, 245], |
| 1582 | "lawngreen": [124, 252, 0], |
| 1583 | "lemonchiffon": [255, 250, 205], |
| 1584 | "lightblue": [173, 216, 230], |
| 1585 | "lightcoral": [240, 128, 128], |
| 1586 | "lightcyan": [224, 255, 255], |
| 1587 | "lightgoldenrodyellow": [250, 250, 210], |
| 1588 | "lightgray": [211, 211, 211], |
| 1589 | "lightgreen": [144, 238, 144], |
| 1590 | "lightgrey": [211, 211, 211], |
| 1591 | "lightpink": [255, 182, 193], |
| 1592 | "lightsalmon": [255, 160, 122], |
| 1593 | "lightseagreen": [32, 178, 170], |
| 1594 | "lightskyblue": [135, 206, 250], |
| 1595 | "lightslategray": [119, 136, 153], |
| 1596 | "lightslategrey": [119, 136, 153], |
| 1597 | "lightsteelblue": [176, 196, 222], |
| 1598 | "lightyellow": [255, 255, 224], |
| 1599 | "lime": [0, 255, 0], |
| 1600 | "limegreen": [50, 205, 50], |
| 1601 | "linen": [250, 240, 230], |
| 1602 | "magenta": [255, 0, 255], |
| 1603 | "maroon": [128, 0, 0], |
| 1604 | "mediumaquamarine": [102, 205, 170], |
| 1605 | "mediumblue": [0, 0, 205], |
| 1606 | "mediumorchid": [186, 85, 211], |
| 1607 | "mediumpurple": [147, 112, 219], |
| 1608 | "mediumseagreen": [60, 179, 113], |
| 1609 | "mediumslateblue": [123, 104, 238], |
| 1610 | "mediumspringgreen": [0, 250, 154], |
| 1611 | "mediumturquoise": [72, 209, 204], |
| 1612 | "mediumvioletred": [199, 21, 133], |
| 1613 | "midnightblue": [25, 25, 112], |
| 1614 | "mintcream": [245, 255, 250], |
| 1615 | "mistyrose": [255, 228, 225], |
| 1616 | "moccasin": [255, 228, 181], |
| 1617 | "navajowhite": [255, 222, 173], |
| 1618 | "navy": [0, 0, 128], |
| 1619 | "oldlace": [253, 245, 230], |
| 1620 | "olive": [128, 128, 0], |
| 1621 | "olivedrab": [107, 142, 35], |
| 1622 | "orange": [255, 165, 0], |
| 1623 | "orangered": [255, 69, 0], |
| 1624 | "orchid": [218, 112, 214], |
| 1625 | "palegoldenrod": [238, 232, 170], |
| 1626 | "palegreen": [152, 251, 152], |
| 1627 | "paleturquoise": [175, 238, 238], |
| 1628 | "palevioletred": [219, 112, 147], |
| 1629 | "papayawhip": [255, 239, 213], |
| 1630 | "peachpuff": [255, 218, 185], |
| 1631 | "peru": [205, 133, 63], |
| 1632 | "pink": [255, 192, 203], |
| 1633 | "plum": [221, 160, 221], |
| 1634 | "powderblue": [176, 224, 230], |
| 1635 | "purple": [128, 0, 128], |
| 1636 | "rebeccapurple": [102, 51, 153], |
| 1637 | "red": [255, 0, 0], |
| 1638 | "rosybrown": [188, 143, 143], |
| 1639 | "royalblue": [65, 105, 225], |
| 1640 | "saddlebrown": [139, 69, 19], |
| 1641 | "salmon": [250, 128, 114], |
| 1642 | "sandybrown": [244, 164, 96], |
| 1643 | "seagreen": [46, 139, 87], |
| 1644 | "seashell": [255, 245, 238], |
| 1645 | "sienna": [160, 82, 45], |
| 1646 | "silver": [192, 192, 192], |
| 1647 | "skyblue": [135, 206, 235], |
| 1648 | "slateblue": [106, 90, 205], |
| 1649 | "slategray": [112, 128, 144], |
| 1650 | "slategrey": [112, 128, 144], |
| 1651 | "snow": [255, 250, 250], |
| 1652 | "springgreen": [0, 255, 127], |
| 1653 | "steelblue": [70, 130, 180], |
| 1654 | "tan": [210, 180, 140], |
| 1655 | "teal": [0, 128, 128], |
| 1656 | "thistle": [216, 191, 216], |
| 1657 | "tomato": [255, 99, 71], |
| 1658 | "turquoise": [64, 224, 208], |
| 1659 | "violet": [238, 130, 238], |
| 1660 | "wheat": [245, 222, 179], |
| 1661 | "white": [255, 255, 255], |
| 1662 | "whitesmoke": [245, 245, 245], |
| 1663 | "yellow": [255, 255, 0], |
| 1664 | "yellowgreen": [154, 205, 50] |
| 1665 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1666 | },{}],7:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1667 | /** |
| 1668 | * @namespace Chart |
| 1669 | */ |
| 1670 | var Chart = require(28)(); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1671 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1672 | require(26)(Chart); |
| 1673 | require(40)(Chart); |
| 1674 | require(22)(Chart); |
| 1675 | require(25)(Chart); |
| 1676 | require(30)(Chart); |
| 1677 | require(21)(Chart); |
| 1678 | require(23)(Chart); |
| 1679 | require(24)(Chart); |
| 1680 | require(29)(Chart); |
| 1681 | require(32)(Chart); |
| 1682 | require(33)(Chart); |
| 1683 | require(31)(Chart); |
| 1684 | require(27)(Chart); |
| 1685 | require(34)(Chart); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1686 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1687 | require(35)(Chart); |
| 1688 | require(36)(Chart); |
| 1689 | require(37)(Chart); |
| 1690 | require(38)(Chart); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1691 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1692 | require(46)(Chart); |
| 1693 | require(44)(Chart); |
| 1694 | require(45)(Chart); |
| 1695 | require(47)(Chart); |
| 1696 | require(48)(Chart); |
| 1697 | require(49)(Chart); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 1698 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1699 | // Controllers must be loaded after elements |
| 1700 | // See Chart.core.datasetController.dataElementType |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1701 | require(15)(Chart); |
| 1702 | require(16)(Chart); |
| 1703 | require(17)(Chart); |
| 1704 | require(18)(Chart); |
| 1705 | require(19)(Chart); |
| 1706 | require(20)(Chart); |
Jian Li | 82101d9 | 2016-05-04 12:00:46 -0700 | [diff] [blame] | 1707 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1708 | require(8)(Chart); |
| 1709 | require(9)(Chart); |
| 1710 | require(10)(Chart); |
| 1711 | require(11)(Chart); |
| 1712 | require(12)(Chart); |
| 1713 | require(13)(Chart); |
| 1714 | require(14)(Chart); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1715 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1716 | // Loading built-it plugins |
| 1717 | var plugins = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1718 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1719 | plugins.push( |
| 1720 | require(41)(Chart), |
| 1721 | require(42)(Chart), |
| 1722 | require(43)(Chart) |
| 1723 | ); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1724 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1725 | Chart.plugins.register(plugins); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1726 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1727 | module.exports = Chart; |
| 1728 | if (typeof window !== 'undefined') { |
| 1729 | window.Chart = Chart; |
| 1730 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1731 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1732 | },{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"49":49,"8":8,"9":9}],8:[function(require,module,exports){ |
| 1733 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1734 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1735 | module.exports = function(Chart) { |
| 1736 | |
| 1737 | Chart.Bar = function(context, config) { |
| 1738 | config.type = 'bar'; |
| 1739 | |
| 1740 | return new Chart(context, config); |
| 1741 | }; |
| 1742 | |
| 1743 | }; |
| 1744 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1745 | },{}],9:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1746 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1747 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1748 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1749 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1750 | Chart.Bubble = function(context, config) { |
| 1751 | config.type = 'bubble'; |
| 1752 | return new Chart(context, config); |
| 1753 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1754 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1755 | }; |
| 1756 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1757 | },{}],10:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1758 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1759 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1760 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1761 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1762 | Chart.Doughnut = function(context, config) { |
| 1763 | config.type = 'doughnut'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1764 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1765 | return new Chart(context, config); |
| 1766 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1767 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1768 | }; |
| 1769 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1770 | },{}],11:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1771 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1772 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1773 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1774 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1775 | Chart.Line = function(context, config) { |
| 1776 | config.type = 'line'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1777 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1778 | return new Chart(context, config); |
| 1779 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1780 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1781 | }; |
| 1782 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1783 | },{}],12:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1784 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1785 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1786 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1787 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1788 | Chart.PolarArea = function(context, config) { |
| 1789 | config.type = 'polarArea'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1790 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1791 | return new Chart(context, config); |
| 1792 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1793 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1794 | }; |
| 1795 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1796 | },{}],13:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1797 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1798 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1799 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1800 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1801 | Chart.Radar = function(context, config) { |
| 1802 | config.type = 'radar'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1803 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1804 | return new Chart(context, config); |
| 1805 | }; |
| 1806 | |
| 1807 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1808 | |
| 1809 | },{}],14:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1810 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1811 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1812 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1813 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1814 | var defaultConfig = { |
| 1815 | hover: { |
| 1816 | mode: 'single' |
| 1817 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1818 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1819 | scales: { |
| 1820 | xAxes: [{ |
| 1821 | type: 'linear', // scatter should not use a category axis |
| 1822 | position: 'bottom', |
| 1823 | id: 'x-axis-1' // need an ID so datasets can reference the scale |
| 1824 | }], |
| 1825 | yAxes: [{ |
| 1826 | type: 'linear', |
| 1827 | position: 'left', |
| 1828 | id: 'y-axis-1' |
| 1829 | }] |
| 1830 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1831 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1832 | tooltips: { |
| 1833 | callbacks: { |
| 1834 | title: function() { |
| 1835 | // Title doesn't make sense for scatter since we format the data as a point |
| 1836 | return ''; |
| 1837 | }, |
| 1838 | label: function(tooltipItem) { |
| 1839 | return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; |
| 1840 | } |
| 1841 | } |
| 1842 | } |
| 1843 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1844 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1845 | // Register the default config for this type |
| 1846 | Chart.defaults.scatter = defaultConfig; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1847 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1848 | // Scatter charts use line controllers |
| 1849 | Chart.controllers.scatter = Chart.controllers.line; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1850 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1851 | Chart.Scatter = function(context, config) { |
| 1852 | config.type = 'scatter'; |
| 1853 | return new Chart(context, config); |
| 1854 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1855 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1856 | }; |
| 1857 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1858 | },{}],15:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1859 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1860 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1861 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1862 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1863 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1864 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1865 | Chart.defaults.bar = { |
| 1866 | hover: { |
| 1867 | mode: 'label' |
| 1868 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1869 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1870 | scales: { |
| 1871 | xAxes: [{ |
| 1872 | type: 'category', |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1873 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1874 | // Specific to Bar Controller |
| 1875 | categoryPercentage: 0.8, |
| 1876 | barPercentage: 0.9, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1877 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1878 | // grid line settings |
| 1879 | gridLines: { |
| 1880 | offsetGridLines: true |
| 1881 | } |
| 1882 | }], |
| 1883 | yAxes: [{ |
| 1884 | type: 'linear' |
| 1885 | }] |
| 1886 | } |
| 1887 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1888 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1889 | Chart.controllers.bar = Chart.DatasetController.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1890 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1891 | dataElementType: Chart.elements.Rectangle, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1892 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1893 | initialize: function() { |
| 1894 | var me = this; |
| 1895 | var meta; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1896 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1897 | Chart.DatasetController.prototype.initialize.apply(me, arguments); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1898 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1899 | meta = me.getMeta(); |
| 1900 | meta.stack = me.getDataset().stack; |
| 1901 | meta.bar = true; |
| 1902 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1903 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1904 | update: function(reset) { |
| 1905 | var me = this; |
| 1906 | var elements = me.getMeta().data; |
| 1907 | var i, ilen; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1908 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1909 | me._ruler = me.getRuler(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1910 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1911 | for (i = 0, ilen = elements.length; i < ilen; ++i) { |
| 1912 | me.updateElement(elements[i], i, reset); |
| 1913 | } |
| 1914 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1915 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1916 | updateElement: function(rectangle, index, reset) { |
| 1917 | var me = this; |
| 1918 | var chart = me.chart; |
| 1919 | var meta = me.getMeta(); |
| 1920 | var dataset = me.getDataset(); |
| 1921 | var custom = rectangle.custom || {}; |
| 1922 | var rectangleOptions = chart.options.elements.rectangle; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1923 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1924 | rectangle._xScale = me.getScaleForId(meta.xAxisID); |
| 1925 | rectangle._yScale = me.getScaleForId(meta.yAxisID); |
| 1926 | rectangle._datasetIndex = me.index; |
| 1927 | rectangle._index = index; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1928 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1929 | rectangle._model = { |
| 1930 | datasetLabel: dataset.label, |
| 1931 | label: chart.data.labels[index], |
| 1932 | borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, |
| 1933 | backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), |
| 1934 | borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), |
| 1935 | borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) |
| 1936 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1937 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1938 | me.updateElementGeometry(rectangle, index, reset); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1939 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1940 | rectangle.pivot(); |
| 1941 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1942 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1943 | /** |
| 1944 | * @private |
| 1945 | */ |
| 1946 | updateElementGeometry: function(rectangle, index, reset) { |
| 1947 | var me = this; |
| 1948 | var model = rectangle._model; |
| 1949 | var vscale = me.getValueScale(); |
| 1950 | var base = vscale.getBasePixel(); |
| 1951 | var horizontal = vscale.isHorizontal(); |
| 1952 | var ruler = me._ruler || me.getRuler(); |
| 1953 | var vpixels = me.calculateBarValuePixels(me.index, index); |
| 1954 | var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1955 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1956 | model.horizontal = horizontal; |
| 1957 | model.base = reset? base : vpixels.base; |
| 1958 | model.x = horizontal? reset? base : vpixels.head : ipixels.center; |
| 1959 | model.y = horizontal? ipixels.center : reset? base : vpixels.head; |
| 1960 | model.height = horizontal? ipixels.size : undefined; |
| 1961 | model.width = horizontal? undefined : ipixels.size; |
| 1962 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1963 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1964 | /** |
| 1965 | * @private |
| 1966 | */ |
| 1967 | getValueScaleId: function() { |
| 1968 | return this.getMeta().yAxisID; |
| 1969 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1970 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1971 | /** |
| 1972 | * @private |
| 1973 | */ |
| 1974 | getIndexScaleId: function() { |
| 1975 | return this.getMeta().xAxisID; |
| 1976 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1977 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1978 | /** |
| 1979 | * @private |
| 1980 | */ |
| 1981 | getValueScale: function() { |
| 1982 | return this.getScaleForId(this.getValueScaleId()); |
| 1983 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1984 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1985 | /** |
| 1986 | * @private |
| 1987 | */ |
| 1988 | getIndexScale: function() { |
| 1989 | return this.getScaleForId(this.getIndexScaleId()); |
| 1990 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 1991 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 1992 | /** |
| 1993 | * Returns the effective number of stacks based on groups and bar visibility. |
| 1994 | * @private |
| 1995 | */ |
| 1996 | getStackCount: function(last) { |
| 1997 | var me = this; |
| 1998 | var chart = me.chart; |
| 1999 | var scale = me.getIndexScale(); |
| 2000 | var stacked = scale.options.stacked; |
| 2001 | var ilen = last === undefined? chart.data.datasets.length : last + 1; |
| 2002 | var stacks = []; |
| 2003 | var i, meta; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2004 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2005 | for (i = 0; i < ilen; ++i) { |
| 2006 | meta = chart.getDatasetMeta(i); |
| 2007 | if (meta.bar && chart.isDatasetVisible(i) && |
| 2008 | (stacked === false || |
| 2009 | (stacked === true && stacks.indexOf(meta.stack) === -1) || |
| 2010 | (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { |
| 2011 | stacks.push(meta.stack); |
| 2012 | } |
| 2013 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2014 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2015 | return stacks.length; |
| 2016 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2017 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2018 | /** |
| 2019 | * Returns the stack index for the given dataset based on groups and bar visibility. |
| 2020 | * @private |
| 2021 | */ |
| 2022 | getStackIndex: function(datasetIndex) { |
| 2023 | return this.getStackCount(datasetIndex) - 1; |
| 2024 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2025 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2026 | /** |
| 2027 | * @private |
| 2028 | */ |
| 2029 | getRuler: function() { |
| 2030 | var me = this; |
| 2031 | var scale = me.getIndexScale(); |
| 2032 | var options = scale.options; |
| 2033 | var stackCount = me.getStackCount(); |
| 2034 | var fullSize = scale.isHorizontal()? scale.width : scale.height; |
| 2035 | var tickSize = fullSize / scale.ticks.length; |
| 2036 | var categorySize = tickSize * options.categoryPercentage; |
| 2037 | var fullBarSize = categorySize / stackCount; |
| 2038 | var barSize = fullBarSize * options.barPercentage; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2039 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2040 | barSize = Math.min( |
| 2041 | helpers.getValueOrDefault(options.barThickness, barSize), |
| 2042 | helpers.getValueOrDefault(options.maxBarThickness, Infinity)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2043 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2044 | return { |
| 2045 | stackCount: stackCount, |
| 2046 | tickSize: tickSize, |
| 2047 | categorySize: categorySize, |
| 2048 | categorySpacing: tickSize - categorySize, |
| 2049 | fullBarSize: fullBarSize, |
| 2050 | barSize: barSize, |
| 2051 | barSpacing: fullBarSize - barSize, |
| 2052 | scale: scale |
| 2053 | }; |
| 2054 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2055 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2056 | /** |
| 2057 | * Note: pixel values are not clamped to the scale area. |
| 2058 | * @private |
| 2059 | */ |
| 2060 | calculateBarValuePixels: function(datasetIndex, index) { |
| 2061 | var me = this; |
| 2062 | var chart = me.chart; |
| 2063 | var meta = me.getMeta(); |
| 2064 | var scale = me.getValueScale(); |
| 2065 | var datasets = chart.data.datasets; |
| 2066 | var value = Number(datasets[datasetIndex].data[index]); |
| 2067 | var stacked = scale.options.stacked; |
| 2068 | var stack = meta.stack; |
| 2069 | var start = 0; |
| 2070 | var i, imeta, ivalue, base, head, size; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2071 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2072 | if (stacked || (stacked === undefined && stack !== undefined)) { |
| 2073 | for (i = 0; i < datasetIndex; ++i) { |
| 2074 | imeta = chart.getDatasetMeta(i); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2075 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2076 | if (imeta.bar && |
| 2077 | imeta.stack === stack && |
| 2078 | imeta.controller.getValueScaleId() === scale.id && |
| 2079 | chart.isDatasetVisible(i)) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2080 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2081 | ivalue = Number(datasets[i].data[index]); |
| 2082 | if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { |
| 2083 | start += ivalue; |
| 2084 | } |
| 2085 | } |
| 2086 | } |
| 2087 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2088 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2089 | base = scale.getPixelForValue(start); |
| 2090 | head = scale.getPixelForValue(start + value); |
| 2091 | size = (head - base) / 2; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2092 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2093 | return { |
| 2094 | size: size, |
| 2095 | base: base, |
| 2096 | head: head, |
| 2097 | center: head + size / 2 |
| 2098 | }; |
| 2099 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2100 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2101 | /** |
| 2102 | * @private |
| 2103 | */ |
| 2104 | calculateBarIndexPixels: function(datasetIndex, index, ruler) { |
| 2105 | var me = this; |
| 2106 | var scale = ruler.scale; |
| 2107 | var isCombo = me.chart.isCombo; |
| 2108 | var stackIndex = me.getStackIndex(datasetIndex); |
| 2109 | var base = scale.getPixelForValue(null, index, datasetIndex, isCombo); |
| 2110 | var size = ruler.barSize; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2111 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2112 | base -= isCombo? ruler.tickSize / 2 : 0; |
| 2113 | base += ruler.fullBarSize * stackIndex; |
| 2114 | base += ruler.categorySpacing / 2; |
| 2115 | base += ruler.barSpacing / 2; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2116 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2117 | return { |
| 2118 | size: size, |
| 2119 | base: base, |
| 2120 | head: base + size, |
| 2121 | center: base + size / 2 |
| 2122 | }; |
| 2123 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2124 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2125 | draw: function() { |
| 2126 | var me = this; |
| 2127 | var chart = me.chart; |
| 2128 | var elements = me.getMeta().data; |
| 2129 | var dataset = me.getDataset(); |
| 2130 | var ilen = elements.length; |
| 2131 | var i = 0; |
| 2132 | var d; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2133 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2134 | helpers.canvas.clipArea(chart.ctx, chart.chartArea); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2135 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2136 | for (; i<ilen; ++i) { |
| 2137 | d = dataset.data[i]; |
| 2138 | if (d !== null && d !== undefined && !isNaN(d)) { |
| 2139 | elements[i].draw(); |
| 2140 | } |
| 2141 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2142 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2143 | helpers.canvas.unclipArea(chart.ctx); |
| 2144 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2145 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2146 | setHoverStyle: function(rectangle) { |
| 2147 | var dataset = this.chart.data.datasets[rectangle._datasetIndex]; |
| 2148 | var index = rectangle._index; |
| 2149 | var custom = rectangle.custom || {}; |
| 2150 | var model = rectangle._model; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2151 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2152 | model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
| 2153 | model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
| 2154 | model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); |
| 2155 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2156 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2157 | removeHoverStyle: function(rectangle) { |
| 2158 | var dataset = this.chart.data.datasets[rectangle._datasetIndex]; |
| 2159 | var index = rectangle._index; |
| 2160 | var custom = rectangle.custom || {}; |
| 2161 | var model = rectangle._model; |
| 2162 | var rectangleElementOptions = this.chart.options.elements.rectangle; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2163 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2164 | model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); |
| 2165 | model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); |
| 2166 | model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); |
| 2167 | } |
| 2168 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2169 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2170 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2171 | // including horizontalBar in the bar file, instead of a file of its own |
| 2172 | // it extends bar (like pie extends doughnut) |
| 2173 | Chart.defaults.horizontalBar = { |
| 2174 | hover: { |
| 2175 | mode: 'label' |
| 2176 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2177 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2178 | scales: { |
| 2179 | xAxes: [{ |
| 2180 | type: 'linear', |
| 2181 | position: 'bottom' |
| 2182 | }], |
| 2183 | yAxes: [{ |
| 2184 | position: 'left', |
| 2185 | type: 'category', |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2186 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2187 | // Specific to Horizontal Bar Controller |
| 2188 | categoryPercentage: 0.8, |
| 2189 | barPercentage: 0.9, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2190 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2191 | // grid line settings |
| 2192 | gridLines: { |
| 2193 | offsetGridLines: true |
| 2194 | } |
| 2195 | }] |
| 2196 | }, |
| 2197 | elements: { |
| 2198 | rectangle: { |
| 2199 | borderSkipped: 'left' |
| 2200 | } |
| 2201 | }, |
| 2202 | tooltips: { |
| 2203 | callbacks: { |
| 2204 | title: function(tooltipItems, data) { |
| 2205 | // Pick first xLabel for now |
| 2206 | var title = ''; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2207 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2208 | if (tooltipItems.length > 0) { |
| 2209 | if (tooltipItems[0].yLabel) { |
| 2210 | title = tooltipItems[0].yLabel; |
| 2211 | } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { |
| 2212 | title = data.labels[tooltipItems[0].index]; |
| 2213 | } |
| 2214 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2215 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2216 | return title; |
| 2217 | }, |
| 2218 | label: function(tooltipItem, data) { |
| 2219 | var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; |
| 2220 | return datasetLabel + ': ' + tooltipItem.xLabel; |
| 2221 | } |
| 2222 | } |
| 2223 | } |
| 2224 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2225 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2226 | Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ |
| 2227 | /** |
| 2228 | * @private |
| 2229 | */ |
| 2230 | getValueScaleId: function() { |
| 2231 | return this.getMeta().xAxisID; |
| 2232 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2233 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2234 | /** |
| 2235 | * @private |
| 2236 | */ |
| 2237 | getIndexScaleId: function() { |
| 2238 | return this.getMeta().yAxisID; |
| 2239 | } |
| 2240 | }); |
| 2241 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2242 | |
| 2243 | },{}],16:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2244 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2245 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2246 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2247 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2248 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2249 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2250 | Chart.defaults.bubble = { |
| 2251 | hover: { |
| 2252 | mode: 'single' |
| 2253 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2254 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2255 | scales: { |
| 2256 | xAxes: [{ |
| 2257 | type: 'linear', // bubble should probably use a linear scale by default |
| 2258 | position: 'bottom', |
| 2259 | id: 'x-axis-0' // need an ID so datasets can reference the scale |
| 2260 | }], |
| 2261 | yAxes: [{ |
| 2262 | type: 'linear', |
| 2263 | position: 'left', |
| 2264 | id: 'y-axis-0' |
| 2265 | }] |
| 2266 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2267 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2268 | tooltips: { |
| 2269 | callbacks: { |
| 2270 | title: function() { |
| 2271 | // Title doesn't make sense for scatter since we format the data as a point |
| 2272 | return ''; |
| 2273 | }, |
| 2274 | label: function(tooltipItem, data) { |
| 2275 | var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; |
| 2276 | var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; |
| 2277 | return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')'; |
| 2278 | } |
| 2279 | } |
| 2280 | } |
| 2281 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2282 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2283 | Chart.controllers.bubble = Chart.DatasetController.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2284 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2285 | dataElementType: Chart.elements.Point, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2286 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2287 | update: function(reset) { |
| 2288 | var me = this; |
| 2289 | var meta = me.getMeta(); |
| 2290 | var points = meta.data; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2291 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2292 | // Update Points |
| 2293 | helpers.each(points, function(point, index) { |
| 2294 | me.updateElement(point, index, reset); |
| 2295 | }); |
| 2296 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2297 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2298 | updateElement: function(point, index, reset) { |
| 2299 | var me = this; |
| 2300 | var meta = me.getMeta(); |
| 2301 | var xScale = me.getScaleForId(meta.xAxisID); |
| 2302 | var yScale = me.getScaleForId(meta.yAxisID); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2303 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2304 | var custom = point.custom || {}; |
| 2305 | var dataset = me.getDataset(); |
| 2306 | var data = dataset.data[index]; |
| 2307 | var pointElementOptions = me.chart.options.elements.point; |
| 2308 | var dsIndex = me.index; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2309 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2310 | helpers.extend(point, { |
| 2311 | // Utility |
| 2312 | _xScale: xScale, |
| 2313 | _yScale: yScale, |
| 2314 | _datasetIndex: dsIndex, |
| 2315 | _index: index, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2316 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2317 | // Desired view properties |
| 2318 | _model: { |
| 2319 | x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), |
| 2320 | y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), |
| 2321 | // Appearance |
| 2322 | radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2323 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2324 | // Tooltip |
| 2325 | hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) |
| 2326 | } |
| 2327 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2328 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2329 | // Trick to reset the styles of the point |
| 2330 | Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2331 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2332 | var model = point._model; |
| 2333 | model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2334 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2335 | point.pivot(); |
| 2336 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2337 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2338 | getRadius: function(value) { |
| 2339 | return value.r || this.chart.options.elements.point.radius; |
| 2340 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2341 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2342 | setHoverStyle: function(point) { |
| 2343 | var me = this; |
| 2344 | Chart.DatasetController.prototype.setHoverStyle.call(me, point); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2345 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2346 | // Radius |
| 2347 | var dataset = me.chart.data.datasets[point._datasetIndex]; |
| 2348 | var index = point._index; |
| 2349 | var custom = point.custom || {}; |
| 2350 | var model = point._model; |
| 2351 | model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); |
| 2352 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2353 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2354 | removeHoverStyle: function(point) { |
| 2355 | var me = this; |
| 2356 | Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2357 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2358 | var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; |
| 2359 | var custom = point.custom || {}; |
| 2360 | var model = point._model; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2361 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2362 | model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); |
| 2363 | } |
| 2364 | }); |
| 2365 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2366 | |
| 2367 | },{}],17:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2368 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2369 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2370 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2371 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2372 | var helpers = Chart.helpers, |
| 2373 | defaults = Chart.defaults; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2374 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2375 | defaults.doughnut = { |
| 2376 | animation: { |
| 2377 | // Boolean - Whether we animate the rotation of the Doughnut |
| 2378 | animateRotate: true, |
| 2379 | // Boolean - Whether we animate scaling the Doughnut from the centre |
| 2380 | animateScale: false |
| 2381 | }, |
| 2382 | aspectRatio: 1, |
| 2383 | hover: { |
| 2384 | mode: 'single' |
| 2385 | }, |
| 2386 | legendCallback: function(chart) { |
| 2387 | var text = []; |
| 2388 | text.push('<ul class="' + chart.id + '-legend">'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2389 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2390 | var data = chart.data; |
| 2391 | var datasets = data.datasets; |
| 2392 | var labels = data.labels; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2393 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2394 | if (datasets.length) { |
| 2395 | for (var i = 0; i < datasets[0].data.length; ++i) { |
| 2396 | text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); |
| 2397 | if (labels[i]) { |
| 2398 | text.push(labels[i]); |
| 2399 | } |
| 2400 | text.push('</li>'); |
| 2401 | } |
| 2402 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2403 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2404 | text.push('</ul>'); |
| 2405 | return text.join(''); |
| 2406 | }, |
| 2407 | legend: { |
| 2408 | labels: { |
| 2409 | generateLabels: function(chart) { |
| 2410 | var data = chart.data; |
| 2411 | if (data.labels.length && data.datasets.length) { |
| 2412 | return data.labels.map(function(label, i) { |
| 2413 | var meta = chart.getDatasetMeta(0); |
| 2414 | var ds = data.datasets[0]; |
| 2415 | var arc = meta.data[i]; |
| 2416 | var custom = arc && arc.custom || {}; |
| 2417 | var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
| 2418 | var arcOpts = chart.options.elements.arc; |
| 2419 | var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); |
| 2420 | var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); |
| 2421 | var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2422 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2423 | return { |
| 2424 | text: label, |
| 2425 | fillStyle: fill, |
| 2426 | strokeStyle: stroke, |
| 2427 | lineWidth: bw, |
| 2428 | hidden: isNaN(ds.data[i]) || meta.data[i].hidden, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2429 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2430 | // Extra data used for toggling the correct item |
| 2431 | index: i |
| 2432 | }; |
| 2433 | }); |
| 2434 | } |
| 2435 | return []; |
| 2436 | } |
| 2437 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2438 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2439 | onClick: function(e, legendItem) { |
| 2440 | var index = legendItem.index; |
| 2441 | var chart = this.chart; |
| 2442 | var i, ilen, meta; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2443 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2444 | for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
| 2445 | meta = chart.getDatasetMeta(i); |
| 2446 | // toggle visibility of index if exists |
| 2447 | if (meta.data[index]) { |
| 2448 | meta.data[index].hidden = !meta.data[index].hidden; |
| 2449 | } |
| 2450 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2451 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2452 | chart.update(); |
| 2453 | } |
| 2454 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2455 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2456 | // The percentage of the chart that we cut out of the middle. |
| 2457 | cutoutPercentage: 50, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2458 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2459 | // The rotation of the chart, where the first data arc begins. |
| 2460 | rotation: Math.PI * -0.5, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2461 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2462 | // The total circumference of the chart. |
| 2463 | circumference: Math.PI * 2.0, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2464 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2465 | // Need to override these to give a nice default |
| 2466 | tooltips: { |
| 2467 | callbacks: { |
| 2468 | title: function() { |
| 2469 | return ''; |
| 2470 | }, |
| 2471 | label: function(tooltipItem, data) { |
| 2472 | var dataLabel = data.labels[tooltipItem.index]; |
| 2473 | var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2474 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2475 | if (helpers.isArray(dataLabel)) { |
| 2476 | // show value on first line of multiline label |
| 2477 | // need to clone because we are changing the value |
| 2478 | dataLabel = dataLabel.slice(); |
| 2479 | dataLabel[0] += value; |
| 2480 | } else { |
| 2481 | dataLabel += value; |
| 2482 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2483 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2484 | return dataLabel; |
| 2485 | } |
| 2486 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 2487 | } |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2488 | }; |
| 2489 | |
| 2490 | defaults.pie = helpers.clone(defaults.doughnut); |
| 2491 | helpers.extend(defaults.pie, { |
| 2492 | cutoutPercentage: 0 |
| 2493 | }); |
| 2494 | |
| 2495 | |
| 2496 | Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ |
| 2497 | |
| 2498 | dataElementType: Chart.elements.Arc, |
| 2499 | |
| 2500 | linkScales: helpers.noop, |
| 2501 | |
| 2502 | // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly |
| 2503 | getRingIndex: function(datasetIndex) { |
| 2504 | var ringIndex = 0; |
| 2505 | |
| 2506 | for (var j = 0; j < datasetIndex; ++j) { |
| 2507 | if (this.chart.isDatasetVisible(j)) { |
| 2508 | ++ringIndex; |
| 2509 | } |
| 2510 | } |
| 2511 | |
| 2512 | return ringIndex; |
| 2513 | }, |
| 2514 | |
| 2515 | update: function(reset) { |
| 2516 | var me = this; |
| 2517 | var chart = me.chart, |
| 2518 | chartArea = chart.chartArea, |
| 2519 | opts = chart.options, |
| 2520 | arcOpts = opts.elements.arc, |
| 2521 | availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, |
| 2522 | availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, |
| 2523 | minSize = Math.min(availableWidth, availableHeight), |
| 2524 | offset = { |
| 2525 | x: 0, |
| 2526 | y: 0 |
| 2527 | }, |
| 2528 | meta = me.getMeta(), |
| 2529 | cutoutPercentage = opts.cutoutPercentage, |
| 2530 | circumference = opts.circumference; |
| 2531 | |
| 2532 | // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc |
| 2533 | if (circumference < Math.PI * 2.0) { |
| 2534 | var startAngle = opts.rotation % (Math.PI * 2.0); |
| 2535 | startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); |
| 2536 | var endAngle = startAngle + circumference; |
| 2537 | var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; |
| 2538 | var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; |
| 2539 | var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); |
| 2540 | var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); |
| 2541 | var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); |
| 2542 | var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); |
| 2543 | var cutout = cutoutPercentage / 100.0; |
| 2544 | 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))}; |
| 2545 | 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))}; |
| 2546 | var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; |
| 2547 | minSize = Math.min(availableWidth / size.width, availableHeight / size.height); |
| 2548 | offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; |
| 2549 | } |
| 2550 | |
| 2551 | chart.borderWidth = me.getMaxBorderWidth(meta.data); |
| 2552 | chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); |
| 2553 | chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); |
| 2554 | chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); |
| 2555 | chart.offsetX = offset.x * chart.outerRadius; |
| 2556 | chart.offsetY = offset.y * chart.outerRadius; |
| 2557 | |
| 2558 | meta.total = me.calculateTotal(); |
| 2559 | |
| 2560 | me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); |
| 2561 | me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); |
| 2562 | |
| 2563 | helpers.each(meta.data, function(arc, index) { |
| 2564 | me.updateElement(arc, index, reset); |
| 2565 | }); |
| 2566 | }, |
| 2567 | |
| 2568 | updateElement: function(arc, index, reset) { |
| 2569 | var me = this; |
| 2570 | var chart = me.chart, |
| 2571 | chartArea = chart.chartArea, |
| 2572 | opts = chart.options, |
| 2573 | animationOpts = opts.animation, |
| 2574 | centerX = (chartArea.left + chartArea.right) / 2, |
| 2575 | centerY = (chartArea.top + chartArea.bottom) / 2, |
| 2576 | startAngle = opts.rotation, // non reset case handled later |
| 2577 | endAngle = opts.rotation, // non reset case handled later |
| 2578 | dataset = me.getDataset(), |
| 2579 | circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), |
| 2580 | innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, |
| 2581 | outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, |
| 2582 | valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
| 2583 | |
| 2584 | helpers.extend(arc, { |
| 2585 | // Utility |
| 2586 | _datasetIndex: me.index, |
| 2587 | _index: index, |
| 2588 | |
| 2589 | // Desired view properties |
| 2590 | _model: { |
| 2591 | x: centerX + chart.offsetX, |
| 2592 | y: centerY + chart.offsetY, |
| 2593 | startAngle: startAngle, |
| 2594 | endAngle: endAngle, |
| 2595 | circumference: circumference, |
| 2596 | outerRadius: outerRadius, |
| 2597 | innerRadius: innerRadius, |
| 2598 | label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) |
| 2599 | } |
| 2600 | }); |
| 2601 | |
| 2602 | var model = arc._model; |
| 2603 | // Resets the visual styles |
| 2604 | this.removeHoverStyle(arc); |
| 2605 | |
| 2606 | // Set correct angles if not resetting |
| 2607 | if (!reset || !animationOpts.animateRotate) { |
| 2608 | if (index === 0) { |
| 2609 | model.startAngle = opts.rotation; |
| 2610 | } else { |
| 2611 | model.startAngle = me.getMeta().data[index - 1]._model.endAngle; |
| 2612 | } |
| 2613 | |
| 2614 | model.endAngle = model.startAngle + model.circumference; |
| 2615 | } |
| 2616 | |
| 2617 | arc.pivot(); |
| 2618 | }, |
| 2619 | |
| 2620 | removeHoverStyle: function(arc) { |
| 2621 | Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); |
| 2622 | }, |
| 2623 | |
| 2624 | calculateTotal: function() { |
| 2625 | var dataset = this.getDataset(); |
| 2626 | var meta = this.getMeta(); |
| 2627 | var total = 0; |
| 2628 | var value; |
| 2629 | |
| 2630 | helpers.each(meta.data, function(element, index) { |
| 2631 | value = dataset.data[index]; |
| 2632 | if (!isNaN(value) && !element.hidden) { |
| 2633 | total += Math.abs(value); |
| 2634 | } |
| 2635 | }); |
| 2636 | |
| 2637 | /* if (total === 0) { |
| 2638 | total = NaN; |
| 2639 | }*/ |
| 2640 | |
| 2641 | return total; |
| 2642 | }, |
| 2643 | |
| 2644 | calculateCircumference: function(value) { |
| 2645 | var total = this.getMeta().total; |
| 2646 | if (total > 0 && !isNaN(value)) { |
| 2647 | return (Math.PI * 2.0) * (value / total); |
| 2648 | } |
| 2649 | return 0; |
| 2650 | }, |
| 2651 | |
| 2652 | // gets the max border or hover width to properly scale pie charts |
| 2653 | getMaxBorderWidth: function(elements) { |
| 2654 | var max = 0, |
| 2655 | index = this.index, |
| 2656 | length = elements.length, |
| 2657 | borderWidth, |
| 2658 | hoverWidth; |
| 2659 | |
| 2660 | for (var i = 0; i < length; i++) { |
| 2661 | borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; |
| 2662 | hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; |
| 2663 | |
| 2664 | max = borderWidth > max ? borderWidth : max; |
| 2665 | max = hoverWidth > max ? hoverWidth : max; |
| 2666 | } |
| 2667 | return max; |
| 2668 | } |
| 2669 | }); |
| 2670 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2671 | |
| 2672 | },{}],18:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2673 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2674 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2675 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2676 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2677 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2678 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2679 | Chart.defaults.line = { |
| 2680 | showLines: true, |
| 2681 | spanGaps: false, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2682 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2683 | hover: { |
| 2684 | mode: 'label' |
| 2685 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2686 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2687 | scales: { |
| 2688 | xAxes: [{ |
| 2689 | type: 'category', |
| 2690 | id: 'x-axis-0' |
| 2691 | }], |
| 2692 | yAxes: [{ |
| 2693 | type: 'linear', |
| 2694 | id: 'y-axis-0' |
| 2695 | }] |
| 2696 | } |
| 2697 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2698 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2699 | function lineEnabled(dataset, options) { |
| 2700 | return helpers.getValueOrDefault(dataset.showLine, options.showLines); |
| 2701 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2702 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2703 | Chart.controllers.line = Chart.DatasetController.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2704 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2705 | datasetElementType: Chart.elements.Line, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2706 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2707 | dataElementType: Chart.elements.Point, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2708 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2709 | update: function(reset) { |
| 2710 | var me = this; |
| 2711 | var meta = me.getMeta(); |
| 2712 | var line = meta.dataset; |
| 2713 | var points = meta.data || []; |
| 2714 | var options = me.chart.options; |
| 2715 | var lineElementOptions = options.elements.line; |
| 2716 | var scale = me.getScaleForId(meta.yAxisID); |
| 2717 | var i, ilen, custom; |
| 2718 | var dataset = me.getDataset(); |
| 2719 | var showLine = lineEnabled(dataset, options); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2720 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2721 | // Update Line |
| 2722 | if (showLine) { |
| 2723 | custom = line.custom || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2724 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2725 | // Compatibility: If the properties are defined with only the old name, use those values |
| 2726 | if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { |
| 2727 | dataset.lineTension = dataset.tension; |
| 2728 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2729 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2730 | // Utility |
| 2731 | line._scale = scale; |
| 2732 | line._datasetIndex = me.index; |
| 2733 | // Data |
| 2734 | line._children = points; |
| 2735 | // Model |
| 2736 | line._model = { |
| 2737 | // Appearance |
| 2738 | // The default behavior of lines is to break at null values, according |
| 2739 | // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 |
| 2740 | // This option gives lines the ability to span gaps |
| 2741 | spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, |
| 2742 | tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), |
| 2743 | backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), |
| 2744 | borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), |
| 2745 | borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), |
| 2746 | borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), |
| 2747 | borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), |
| 2748 | borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), |
| 2749 | borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), |
| 2750 | fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), |
| 2751 | steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), |
| 2752 | cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), |
| 2753 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2754 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2755 | line.pivot(); |
| 2756 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2757 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2758 | // Update Points |
| 2759 | for (i=0, ilen=points.length; i<ilen; ++i) { |
| 2760 | me.updateElement(points[i], i, reset); |
| 2761 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2762 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2763 | if (showLine && line._model.tension !== 0) { |
| 2764 | me.updateBezierControlPoints(); |
| 2765 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2766 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2767 | // Now pivot the point for animation |
| 2768 | for (i=0, ilen=points.length; i<ilen; ++i) { |
| 2769 | points[i].pivot(); |
| 2770 | } |
| 2771 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2772 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2773 | getPointBackgroundColor: function(point, index) { |
| 2774 | var backgroundColor = this.chart.options.elements.point.backgroundColor; |
| 2775 | var dataset = this.getDataset(); |
| 2776 | var custom = point.custom || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2777 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2778 | if (custom.backgroundColor) { |
| 2779 | backgroundColor = custom.backgroundColor; |
| 2780 | } else if (dataset.pointBackgroundColor) { |
| 2781 | backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); |
| 2782 | } else if (dataset.backgroundColor) { |
| 2783 | backgroundColor = dataset.backgroundColor; |
| 2784 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2785 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2786 | return backgroundColor; |
| 2787 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2788 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2789 | getPointBorderColor: function(point, index) { |
| 2790 | var borderColor = this.chart.options.elements.point.borderColor; |
| 2791 | var dataset = this.getDataset(); |
| 2792 | var custom = point.custom || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2793 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2794 | if (custom.borderColor) { |
| 2795 | borderColor = custom.borderColor; |
| 2796 | } else if (dataset.pointBorderColor) { |
| 2797 | borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); |
| 2798 | } else if (dataset.borderColor) { |
| 2799 | borderColor = dataset.borderColor; |
| 2800 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2801 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2802 | return borderColor; |
| 2803 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2804 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2805 | getPointBorderWidth: function(point, index) { |
| 2806 | var borderWidth = this.chart.options.elements.point.borderWidth; |
| 2807 | var dataset = this.getDataset(); |
| 2808 | var custom = point.custom || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2809 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2810 | if (!isNaN(custom.borderWidth)) { |
| 2811 | borderWidth = custom.borderWidth; |
| 2812 | } else if (!isNaN(dataset.pointBorderWidth)) { |
| 2813 | borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); |
| 2814 | } else if (!isNaN(dataset.borderWidth)) { |
| 2815 | borderWidth = dataset.borderWidth; |
| 2816 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2817 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2818 | return borderWidth; |
| 2819 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2820 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2821 | updateElement: function(point, index, reset) { |
| 2822 | var me = this; |
| 2823 | var meta = me.getMeta(); |
| 2824 | var custom = point.custom || {}; |
| 2825 | var dataset = me.getDataset(); |
| 2826 | var datasetIndex = me.index; |
| 2827 | var value = dataset.data[index]; |
| 2828 | var yScale = me.getScaleForId(meta.yAxisID); |
| 2829 | var xScale = me.getScaleForId(meta.xAxisID); |
| 2830 | var pointOptions = me.chart.options.elements.point; |
| 2831 | var x, y; |
| 2832 | var labels = me.chart.data.labels || []; |
| 2833 | var includeOffset = (labels.length === 1 || dataset.data.length === 1) || me.chart.isCombo; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2834 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2835 | // Compatibility: If the properties are defined with only the old name, use those values |
| 2836 | if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
| 2837 | dataset.pointRadius = dataset.radius; |
| 2838 | } |
| 2839 | if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { |
| 2840 | dataset.pointHitRadius = dataset.hitRadius; |
| 2841 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2842 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2843 | x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, includeOffset); |
| 2844 | y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2845 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2846 | // Utility |
| 2847 | point._xScale = xScale; |
| 2848 | point._yScale = yScale; |
| 2849 | point._datasetIndex = datasetIndex; |
| 2850 | point._index = index; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2851 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2852 | // Desired view properties |
| 2853 | point._model = { |
| 2854 | x: x, |
| 2855 | y: y, |
| 2856 | skip: custom.skip || isNaN(x) || isNaN(y), |
| 2857 | // Appearance |
| 2858 | radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), |
| 2859 | pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), |
| 2860 | backgroundColor: me.getPointBackgroundColor(point, index), |
| 2861 | borderColor: me.getPointBorderColor(point, index), |
| 2862 | borderWidth: me.getPointBorderWidth(point, index), |
| 2863 | tension: meta.dataset._model ? meta.dataset._model.tension : 0, |
| 2864 | steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, |
| 2865 | // Tooltip |
| 2866 | hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) |
| 2867 | }; |
| 2868 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2869 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2870 | calculatePointY: function(value, index, datasetIndex) { |
| 2871 | var me = this; |
| 2872 | var chart = me.chart; |
| 2873 | var meta = me.getMeta(); |
| 2874 | var yScale = me.getScaleForId(meta.yAxisID); |
| 2875 | var sumPos = 0; |
| 2876 | var sumNeg = 0; |
| 2877 | var i, ds, dsMeta; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2878 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2879 | if (yScale.options.stacked) { |
| 2880 | for (i = 0; i < datasetIndex; i++) { |
| 2881 | ds = chart.data.datasets[i]; |
| 2882 | dsMeta = chart.getDatasetMeta(i); |
| 2883 | if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { |
| 2884 | var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); |
| 2885 | if (stackedRightValue < 0) { |
| 2886 | sumNeg += stackedRightValue || 0; |
| 2887 | } else { |
| 2888 | sumPos += stackedRightValue || 0; |
| 2889 | } |
| 2890 | } |
| 2891 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2892 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2893 | var rightValue = Number(yScale.getRightValue(value)); |
| 2894 | if (rightValue < 0) { |
| 2895 | return yScale.getPixelForValue(sumNeg + rightValue); |
| 2896 | } |
| 2897 | return yScale.getPixelForValue(sumPos + rightValue); |
| 2898 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2899 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2900 | return yScale.getPixelForValue(value); |
| 2901 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2902 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2903 | updateBezierControlPoints: function() { |
| 2904 | var me = this; |
| 2905 | var meta = me.getMeta(); |
| 2906 | var area = me.chart.chartArea; |
| 2907 | var points = (meta.data || []); |
| 2908 | var i, ilen, point, model, controlPoints; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2909 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2910 | // Only consider points that are drawn in case the spanGaps option is used |
| 2911 | if (meta.dataset._model.spanGaps) { |
| 2912 | points = points.filter(function(pt) { |
| 2913 | return !pt._model.skip; |
| 2914 | }); |
| 2915 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2916 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2917 | function capControlPoint(pt, min, max) { |
| 2918 | return Math.max(Math.min(pt, max), min); |
| 2919 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2920 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2921 | if (meta.dataset._model.cubicInterpolationMode === 'monotone') { |
| 2922 | helpers.splineCurveMonotone(points); |
| 2923 | } else { |
| 2924 | for (i = 0, ilen = points.length; i < ilen; ++i) { |
| 2925 | point = points[i]; |
| 2926 | model = point._model; |
| 2927 | controlPoints = helpers.splineCurve( |
| 2928 | helpers.previousItem(points, i)._model, |
| 2929 | model, |
| 2930 | helpers.nextItem(points, i)._model, |
| 2931 | meta.dataset._model.tension |
| 2932 | ); |
| 2933 | model.controlPointPreviousX = controlPoints.previous.x; |
| 2934 | model.controlPointPreviousY = controlPoints.previous.y; |
| 2935 | model.controlPointNextX = controlPoints.next.x; |
| 2936 | model.controlPointNextY = controlPoints.next.y; |
| 2937 | } |
| 2938 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2939 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2940 | if (me.chart.options.elements.line.capBezierPoints) { |
| 2941 | for (i = 0, ilen = points.length; i < ilen; ++i) { |
| 2942 | model = points[i]._model; |
| 2943 | model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); |
| 2944 | model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); |
| 2945 | model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); |
| 2946 | model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); |
| 2947 | } |
| 2948 | } |
| 2949 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2950 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2951 | draw: function() { |
| 2952 | var me = this; |
| 2953 | var chart = me.chart; |
| 2954 | var meta = me.getMeta(); |
| 2955 | var points = meta.data || []; |
| 2956 | var area = chart.chartArea; |
| 2957 | var ilen = points.length; |
| 2958 | var i = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2959 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2960 | Chart.canvasHelpers.clipArea(chart.ctx, area); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2961 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2962 | if (lineEnabled(me.getDataset(), chart.options)) { |
| 2963 | meta.dataset.draw(); |
| 2964 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2965 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2966 | Chart.canvasHelpers.unclipArea(chart.ctx); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2967 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2968 | // Draw the points |
| 2969 | for (; i<ilen; ++i) { |
| 2970 | points[i].draw(area); |
| 2971 | } |
| 2972 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2973 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2974 | setHoverStyle: function(point) { |
| 2975 | // Point |
| 2976 | var dataset = this.chart.data.datasets[point._datasetIndex]; |
| 2977 | var index = point._index; |
| 2978 | var custom = point.custom || {}; |
| 2979 | var model = point._model; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2980 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2981 | model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); |
| 2982 | model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
| 2983 | model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
| 2984 | model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); |
| 2985 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2986 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2987 | removeHoverStyle: function(point) { |
| 2988 | var me = this; |
| 2989 | var dataset = me.chart.data.datasets[point._datasetIndex]; |
| 2990 | var index = point._index; |
| 2991 | var custom = point.custom || {}; |
| 2992 | var model = point._model; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2993 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2994 | // Compatibility: If the properties are defined with only the old name, use those values |
| 2995 | if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
| 2996 | dataset.pointRadius = dataset.radius; |
| 2997 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 2998 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 2999 | model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); |
| 3000 | model.backgroundColor = me.getPointBackgroundColor(point, index); |
| 3001 | model.borderColor = me.getPointBorderColor(point, index); |
| 3002 | model.borderWidth = me.getPointBorderWidth(point, index); |
| 3003 | } |
| 3004 | }); |
| 3005 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3006 | |
| 3007 | },{}],19:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3008 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3009 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3010 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3011 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3012 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3013 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3014 | Chart.defaults.polarArea = { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3015 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3016 | scale: { |
| 3017 | type: 'radialLinear', |
| 3018 | angleLines: { |
| 3019 | display: false |
| 3020 | }, |
| 3021 | gridLines: { |
| 3022 | circular: true |
| 3023 | }, |
| 3024 | pointLabels: { |
| 3025 | display: false |
| 3026 | }, |
| 3027 | ticks: { |
| 3028 | beginAtZero: true |
| 3029 | } |
| 3030 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3031 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3032 | // Boolean - Whether to animate the rotation of the chart |
| 3033 | animation: { |
| 3034 | animateRotate: true, |
| 3035 | animateScale: true |
| 3036 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3037 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3038 | startAngle: -0.5 * Math.PI, |
| 3039 | aspectRatio: 1, |
| 3040 | legendCallback: function(chart) { |
| 3041 | var text = []; |
| 3042 | text.push('<ul class="' + chart.id + '-legend">'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3043 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3044 | var data = chart.data; |
| 3045 | var datasets = data.datasets; |
| 3046 | var labels = data.labels; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3047 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3048 | if (datasets.length) { |
| 3049 | for (var i = 0; i < datasets[0].data.length; ++i) { |
| 3050 | text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); |
| 3051 | if (labels[i]) { |
| 3052 | text.push(labels[i]); |
| 3053 | } |
| 3054 | text.push('</li>'); |
| 3055 | } |
| 3056 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3057 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3058 | text.push('</ul>'); |
| 3059 | return text.join(''); |
| 3060 | }, |
| 3061 | legend: { |
| 3062 | labels: { |
| 3063 | generateLabels: function(chart) { |
| 3064 | var data = chart.data; |
| 3065 | if (data.labels.length && data.datasets.length) { |
| 3066 | return data.labels.map(function(label, i) { |
| 3067 | var meta = chart.getDatasetMeta(0); |
| 3068 | var ds = data.datasets[0]; |
| 3069 | var arc = meta.data[i]; |
| 3070 | var custom = arc.custom || {}; |
| 3071 | var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
| 3072 | var arcOpts = chart.options.elements.arc; |
| 3073 | var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); |
| 3074 | var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); |
| 3075 | var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3076 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3077 | return { |
| 3078 | text: label, |
| 3079 | fillStyle: fill, |
| 3080 | strokeStyle: stroke, |
| 3081 | lineWidth: bw, |
| 3082 | hidden: isNaN(ds.data[i]) || meta.data[i].hidden, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3083 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3084 | // Extra data used for toggling the correct item |
| 3085 | index: i |
| 3086 | }; |
| 3087 | }); |
| 3088 | } |
| 3089 | return []; |
| 3090 | } |
| 3091 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3092 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3093 | onClick: function(e, legendItem) { |
| 3094 | var index = legendItem.index; |
| 3095 | var chart = this.chart; |
| 3096 | var i, ilen, meta; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3097 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3098 | for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { |
| 3099 | meta = chart.getDatasetMeta(i); |
| 3100 | meta.data[index].hidden = !meta.data[index].hidden; |
| 3101 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3102 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3103 | chart.update(); |
| 3104 | } |
| 3105 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3106 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3107 | // Need to override these to give a nice default |
| 3108 | tooltips: { |
| 3109 | callbacks: { |
| 3110 | title: function() { |
| 3111 | return ''; |
| 3112 | }, |
| 3113 | label: function(tooltipItem, data) { |
| 3114 | return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; |
| 3115 | } |
| 3116 | } |
| 3117 | } |
| 3118 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3119 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3120 | Chart.controllers.polarArea = Chart.DatasetController.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3121 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3122 | dataElementType: Chart.elements.Arc, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3123 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3124 | linkScales: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3125 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3126 | update: function(reset) { |
| 3127 | var me = this; |
| 3128 | var chart = me.chart; |
| 3129 | var chartArea = chart.chartArea; |
| 3130 | var meta = me.getMeta(); |
| 3131 | var opts = chart.options; |
| 3132 | var arcOpts = opts.elements.arc; |
| 3133 | var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); |
| 3134 | chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); |
| 3135 | chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); |
| 3136 | chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3137 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3138 | me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); |
| 3139 | me.innerRadius = me.outerRadius - chart.radiusLength; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3140 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3141 | meta.count = me.countVisibleElements(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3142 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3143 | helpers.each(meta.data, function(arc, index) { |
| 3144 | me.updateElement(arc, index, reset); |
| 3145 | }); |
| 3146 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3147 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3148 | updateElement: function(arc, index, reset) { |
| 3149 | var me = this; |
| 3150 | var chart = me.chart; |
| 3151 | var dataset = me.getDataset(); |
| 3152 | var opts = chart.options; |
| 3153 | var animationOpts = opts.animation; |
| 3154 | var scale = chart.scale; |
| 3155 | var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; |
| 3156 | var labels = chart.data.labels; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3157 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3158 | var circumference = me.calculateCircumference(dataset.data[index]); |
| 3159 | var centerX = scale.xCenter; |
| 3160 | var centerY = scale.yCenter; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3161 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3162 | // If there is NaN data before us, we need to calculate the starting angle correctly. |
| 3163 | // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data |
| 3164 | var visibleCount = 0; |
| 3165 | var meta = me.getMeta(); |
| 3166 | for (var i = 0; i < index; ++i) { |
| 3167 | if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { |
| 3168 | ++visibleCount; |
| 3169 | } |
| 3170 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3171 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3172 | // var negHalfPI = -0.5 * Math.PI; |
| 3173 | var datasetStartAngle = opts.startAngle; |
| 3174 | var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); |
| 3175 | var startAngle = datasetStartAngle + (circumference * visibleCount); |
| 3176 | var endAngle = startAngle + (arc.hidden ? 0 : circumference); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3177 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3178 | var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3179 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3180 | helpers.extend(arc, { |
| 3181 | // Utility |
| 3182 | _datasetIndex: me.index, |
| 3183 | _index: index, |
| 3184 | _scale: scale, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3185 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3186 | // Desired view properties |
| 3187 | _model: { |
| 3188 | x: centerX, |
| 3189 | y: centerY, |
| 3190 | innerRadius: 0, |
| 3191 | outerRadius: reset ? resetRadius : distance, |
| 3192 | startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, |
| 3193 | endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, |
| 3194 | label: getValueAtIndexOrDefault(labels, index, labels[index]) |
| 3195 | } |
| 3196 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3197 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3198 | // Apply border and fill style |
| 3199 | me.removeHoverStyle(arc); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3200 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3201 | arc.pivot(); |
| 3202 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3203 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3204 | removeHoverStyle: function(arc) { |
| 3205 | Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); |
| 3206 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3207 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3208 | countVisibleElements: function() { |
| 3209 | var dataset = this.getDataset(); |
| 3210 | var meta = this.getMeta(); |
| 3211 | var count = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3212 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3213 | helpers.each(meta.data, function(element, index) { |
| 3214 | if (!isNaN(dataset.data[index]) && !element.hidden) { |
| 3215 | count++; |
| 3216 | } |
| 3217 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3218 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3219 | return count; |
| 3220 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3221 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3222 | calculateCircumference: function(value) { |
| 3223 | var count = this.getMeta().count; |
| 3224 | if (count > 0 && !isNaN(value)) { |
| 3225 | return (2 * Math.PI) / count; |
| 3226 | } |
| 3227 | return 0; |
| 3228 | } |
| 3229 | }); |
| 3230 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3231 | |
| 3232 | },{}],20:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3233 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3234 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3235 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3236 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3237 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3238 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3239 | Chart.defaults.radar = { |
| 3240 | aspectRatio: 1, |
| 3241 | scale: { |
| 3242 | type: 'radialLinear' |
| 3243 | }, |
| 3244 | elements: { |
| 3245 | line: { |
| 3246 | tension: 0 // no bezier in radar |
| 3247 | } |
| 3248 | } |
| 3249 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3250 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3251 | Chart.controllers.radar = Chart.DatasetController.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3252 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3253 | datasetElementType: Chart.elements.Line, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3254 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3255 | dataElementType: Chart.elements.Point, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3256 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3257 | linkScales: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3258 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3259 | update: function(reset) { |
| 3260 | var me = this; |
| 3261 | var meta = me.getMeta(); |
| 3262 | var line = meta.dataset; |
| 3263 | var points = meta.data; |
| 3264 | var custom = line.custom || {}; |
| 3265 | var dataset = me.getDataset(); |
| 3266 | var lineElementOptions = me.chart.options.elements.line; |
| 3267 | var scale = me.chart.scale; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3268 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3269 | // Compatibility: If the properties are defined with only the old name, use those values |
| 3270 | if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { |
| 3271 | dataset.lineTension = dataset.tension; |
| 3272 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3273 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3274 | helpers.extend(meta.dataset, { |
| 3275 | // Utility |
| 3276 | _datasetIndex: me.index, |
| 3277 | _scale: scale, |
| 3278 | // Data |
| 3279 | _children: points, |
| 3280 | _loop: true, |
| 3281 | // Model |
| 3282 | _model: { |
| 3283 | // Appearance |
| 3284 | tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), |
| 3285 | backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), |
| 3286 | borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), |
| 3287 | borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), |
| 3288 | fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), |
| 3289 | borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), |
| 3290 | borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), |
| 3291 | borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), |
| 3292 | borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), |
| 3293 | } |
| 3294 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3295 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3296 | meta.dataset.pivot(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3297 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3298 | // Update Points |
| 3299 | helpers.each(points, function(point, index) { |
| 3300 | me.updateElement(point, index, reset); |
| 3301 | }, me); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3302 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3303 | // Update bezier control points |
| 3304 | me.updateBezierControlPoints(); |
| 3305 | }, |
| 3306 | updateElement: function(point, index, reset) { |
| 3307 | var me = this; |
| 3308 | var custom = point.custom || {}; |
| 3309 | var dataset = me.getDataset(); |
| 3310 | var scale = me.chart.scale; |
| 3311 | var pointElementOptions = me.chart.options.elements.point; |
| 3312 | var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3313 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3314 | // Compatibility: If the properties are defined with only the old name, use those values |
| 3315 | if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { |
| 3316 | dataset.pointRadius = dataset.radius; |
| 3317 | } |
| 3318 | if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { |
| 3319 | dataset.pointHitRadius = dataset.hitRadius; |
| 3320 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3321 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3322 | helpers.extend(point, { |
| 3323 | // Utility |
| 3324 | _datasetIndex: me.index, |
| 3325 | _index: index, |
| 3326 | _scale: scale, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3327 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3328 | // Desired view properties |
| 3329 | _model: { |
| 3330 | x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales |
| 3331 | y: reset ? scale.yCenter : pointPosition.y, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3332 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3333 | // Appearance |
| 3334 | tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), |
| 3335 | radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), |
| 3336 | backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), |
| 3337 | borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), |
| 3338 | borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), |
| 3339 | pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3340 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3341 | // Tooltip |
| 3342 | hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) |
| 3343 | } |
| 3344 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3345 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3346 | point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); |
| 3347 | }, |
| 3348 | updateBezierControlPoints: function() { |
| 3349 | var chartArea = this.chart.chartArea; |
| 3350 | var meta = this.getMeta(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3351 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3352 | helpers.each(meta.data, function(point, index) { |
| 3353 | var model = point._model; |
| 3354 | var controlPoints = helpers.splineCurve( |
| 3355 | helpers.previousItem(meta.data, index, true)._model, |
| 3356 | model, |
| 3357 | helpers.nextItem(meta.data, index, true)._model, |
| 3358 | model.tension |
| 3359 | ); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3360 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3361 | // Prevent the bezier going outside of the bounds of the graph |
| 3362 | model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); |
| 3363 | model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3364 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3365 | model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); |
| 3366 | model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3367 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3368 | // Now pivot the point for animation |
| 3369 | point.pivot(); |
| 3370 | }); |
| 3371 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3372 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3373 | setHoverStyle: function(point) { |
| 3374 | // Point |
| 3375 | var dataset = this.chart.data.datasets[point._datasetIndex]; |
| 3376 | var custom = point.custom || {}; |
| 3377 | var index = point._index; |
| 3378 | var model = point._model; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3379 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3380 | model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); |
| 3381 | model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); |
| 3382 | model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); |
| 3383 | model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); |
| 3384 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3385 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3386 | removeHoverStyle: function(point) { |
| 3387 | var dataset = this.chart.data.datasets[point._datasetIndex]; |
| 3388 | var custom = point.custom || {}; |
| 3389 | var index = point._index; |
| 3390 | var model = point._model; |
| 3391 | var pointElementOptions = this.chart.options.elements.point; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3392 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3393 | model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); |
| 3394 | model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); |
| 3395 | model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); |
| 3396 | model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); |
| 3397 | } |
| 3398 | }); |
| 3399 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3400 | |
| 3401 | },{}],21:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3402 | /* global window: false */ |
| 3403 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3404 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3405 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3406 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3407 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3408 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3409 | Chart.defaults.global.animation = { |
| 3410 | duration: 1000, |
| 3411 | easing: 'easeOutQuart', |
| 3412 | onProgress: helpers.noop, |
| 3413 | onComplete: helpers.noop |
| 3414 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3415 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3416 | Chart.Animation = Chart.Element.extend({ |
| 3417 | chart: null, // the animation associated chart instance |
| 3418 | currentStep: 0, // the current animation step |
| 3419 | numSteps: 60, // default number of steps |
| 3420 | easing: '', // the easing to use for this animation |
| 3421 | render: null, // render function used by the animation service |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3422 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3423 | onAnimationProgress: null, // user specified callback to fire on each step of the animation |
| 3424 | onAnimationComplete: null, // user specified callback to fire when the animation finishes |
| 3425 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3426 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3427 | Chart.animationService = { |
| 3428 | frameDuration: 17, |
| 3429 | animations: [], |
| 3430 | dropFrames: 0, |
| 3431 | request: null, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3432 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3433 | /** |
| 3434 | * @param {Chart} chart - The chart to animate. |
| 3435 | * @param {Chart.Animation} animation - The animation that we will animate. |
| 3436 | * @param {Number} duration - The animation duration in ms. |
| 3437 | * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions |
| 3438 | */ |
| 3439 | addAnimation: function(chart, animation, duration, lazy) { |
| 3440 | var animations = this.animations; |
| 3441 | var i, ilen; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3442 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3443 | animation.chart = chart; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3444 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3445 | if (!lazy) { |
| 3446 | chart.animating = true; |
| 3447 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3448 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3449 | for (i=0, ilen=animations.length; i < ilen; ++i) { |
| 3450 | if (animations[i].chart === chart) { |
| 3451 | animations[i] = animation; |
| 3452 | return; |
| 3453 | } |
| 3454 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3455 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3456 | animations.push(animation); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3457 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3458 | // If there are no animations queued, manually kickstart a digest, for lack of a better word |
| 3459 | if (animations.length === 1) { |
| 3460 | this.requestAnimationFrame(); |
| 3461 | } |
| 3462 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3463 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3464 | cancelAnimation: function(chart) { |
| 3465 | var index = helpers.findIndex(this.animations, function(animation) { |
| 3466 | return animation.chart === chart; |
| 3467 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3468 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3469 | if (index !== -1) { |
| 3470 | this.animations.splice(index, 1); |
| 3471 | chart.animating = false; |
| 3472 | } |
| 3473 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3474 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3475 | requestAnimationFrame: function() { |
| 3476 | var me = this; |
| 3477 | if (me.request === null) { |
| 3478 | // Skip animation frame requests until the active one is executed. |
| 3479 | // This can happen when processing mouse events, e.g. 'mousemove' |
| 3480 | // and 'mouseout' events will trigger multiple renders. |
| 3481 | me.request = helpers.requestAnimFrame.call(window, function() { |
| 3482 | me.request = null; |
| 3483 | me.startDigest(); |
| 3484 | }); |
| 3485 | } |
| 3486 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3487 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3488 | /** |
| 3489 | * @private |
| 3490 | */ |
| 3491 | startDigest: function() { |
| 3492 | var me = this; |
| 3493 | var startTime = Date.now(); |
| 3494 | var framesToDrop = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3495 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3496 | if (me.dropFrames > 1) { |
| 3497 | framesToDrop = Math.floor(me.dropFrames); |
| 3498 | me.dropFrames = me.dropFrames % 1; |
| 3499 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3500 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3501 | me.advance(1 + framesToDrop); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3502 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3503 | var endTime = Date.now(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3504 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3505 | me.dropFrames += (endTime - startTime) / me.frameDuration; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3506 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3507 | // Do we have more stuff to animate? |
| 3508 | if (me.animations.length > 0) { |
| 3509 | me.requestAnimationFrame(); |
| 3510 | } |
| 3511 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3512 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3513 | /** |
| 3514 | * @private |
| 3515 | */ |
| 3516 | advance: function(count) { |
| 3517 | var animations = this.animations; |
| 3518 | var animation, chart; |
| 3519 | var i = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3520 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3521 | while (i < animations.length) { |
| 3522 | animation = animations[i]; |
| 3523 | chart = animation.chart; |
| 3524 | |
| 3525 | animation.currentStep = (animation.currentStep || 0) + count; |
| 3526 | animation.currentStep = Math.min(animation.currentStep, animation.numSteps); |
| 3527 | |
| 3528 | helpers.callback(animation.render, [chart, animation], chart); |
| 3529 | helpers.callback(animation.onAnimationProgress, [animation], chart); |
| 3530 | |
| 3531 | if (animation.currentStep >= animation.numSteps) { |
| 3532 | helpers.callback(animation.onAnimationComplete, [animation], chart); |
| 3533 | chart.animating = false; |
| 3534 | animations.splice(i, 1); |
| 3535 | } else { |
| 3536 | ++i; |
| 3537 | } |
| 3538 | } |
| 3539 | } |
| 3540 | }; |
| 3541 | |
| 3542 | /** |
| 3543 | * Provided for backward compatibility, use Chart.Animation instead |
| 3544 | * @prop Chart.Animation#animationObject |
| 3545 | * @deprecated since version 2.6.0 |
| 3546 | * @todo remove at version 3 |
| 3547 | */ |
| 3548 | Object.defineProperty(Chart.Animation.prototype, 'animationObject', { |
| 3549 | get: function() { |
| 3550 | return this; |
| 3551 | } |
| 3552 | }); |
| 3553 | |
| 3554 | /** |
| 3555 | * Provided for backward compatibility, use Chart.Animation#chart instead |
| 3556 | * @prop Chart.Animation#chartInstance |
| 3557 | * @deprecated since version 2.6.0 |
| 3558 | * @todo remove at version 3 |
| 3559 | */ |
| 3560 | Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { |
| 3561 | get: function() { |
| 3562 | return this.chart; |
| 3563 | }, |
| 3564 | set: function(value) { |
| 3565 | this.chart = value; |
| 3566 | } |
| 3567 | }); |
| 3568 | |
| 3569 | }; |
| 3570 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3571 | },{}],22:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3572 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3573 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3574 | module.exports = function(Chart) { |
| 3575 | // Global Chart canvas helpers object for drawing items to canvas |
| 3576 | var helpers = Chart.canvasHelpers = {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3577 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3578 | helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { |
| 3579 | var type, edgeLength, xOffset, yOffset, height, size; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3580 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3581 | if (typeof pointStyle === 'object') { |
| 3582 | type = pointStyle.toString(); |
| 3583 | if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { |
| 3584 | ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2, pointStyle.width, pointStyle.height); |
| 3585 | return; |
| 3586 | } |
| 3587 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3588 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3589 | if (isNaN(radius) || radius <= 0) { |
| 3590 | return; |
| 3591 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3592 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3593 | switch (pointStyle) { |
| 3594 | // Default includes circle |
| 3595 | default: |
| 3596 | ctx.beginPath(); |
| 3597 | ctx.arc(x, y, radius, 0, Math.PI * 2); |
| 3598 | ctx.closePath(); |
| 3599 | ctx.fill(); |
| 3600 | break; |
| 3601 | case 'triangle': |
| 3602 | ctx.beginPath(); |
| 3603 | edgeLength = 3 * radius / Math.sqrt(3); |
| 3604 | height = edgeLength * Math.sqrt(3) / 2; |
| 3605 | ctx.moveTo(x - edgeLength / 2, y + height / 3); |
| 3606 | ctx.lineTo(x + edgeLength / 2, y + height / 3); |
| 3607 | ctx.lineTo(x, y - 2 * height / 3); |
| 3608 | ctx.closePath(); |
| 3609 | ctx.fill(); |
| 3610 | break; |
| 3611 | case 'rect': |
| 3612 | size = 1 / Math.SQRT2 * radius; |
| 3613 | ctx.beginPath(); |
| 3614 | ctx.fillRect(x - size, y - size, 2 * size, 2 * size); |
| 3615 | ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); |
| 3616 | break; |
| 3617 | case 'rectRounded': |
| 3618 | var offset = radius / Math.SQRT2; |
| 3619 | var leftX = x - offset; |
| 3620 | var topY = y - offset; |
| 3621 | var sideSize = Math.SQRT2 * radius; |
| 3622 | Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2); |
| 3623 | ctx.fill(); |
| 3624 | break; |
| 3625 | case 'rectRot': |
| 3626 | size = 1 / Math.SQRT2 * radius; |
| 3627 | ctx.beginPath(); |
| 3628 | ctx.moveTo(x - size, y); |
| 3629 | ctx.lineTo(x, y + size); |
| 3630 | ctx.lineTo(x + size, y); |
| 3631 | ctx.lineTo(x, y - size); |
| 3632 | ctx.closePath(); |
| 3633 | ctx.fill(); |
| 3634 | break; |
| 3635 | case 'cross': |
| 3636 | ctx.beginPath(); |
| 3637 | ctx.moveTo(x, y + radius); |
| 3638 | ctx.lineTo(x, y - radius); |
| 3639 | ctx.moveTo(x - radius, y); |
| 3640 | ctx.lineTo(x + radius, y); |
| 3641 | ctx.closePath(); |
| 3642 | break; |
| 3643 | case 'crossRot': |
| 3644 | ctx.beginPath(); |
| 3645 | xOffset = Math.cos(Math.PI / 4) * radius; |
| 3646 | yOffset = Math.sin(Math.PI / 4) * radius; |
| 3647 | ctx.moveTo(x - xOffset, y - yOffset); |
| 3648 | ctx.lineTo(x + xOffset, y + yOffset); |
| 3649 | ctx.moveTo(x - xOffset, y + yOffset); |
| 3650 | ctx.lineTo(x + xOffset, y - yOffset); |
| 3651 | ctx.closePath(); |
| 3652 | break; |
| 3653 | case 'star': |
| 3654 | ctx.beginPath(); |
| 3655 | ctx.moveTo(x, y + radius); |
| 3656 | ctx.lineTo(x, y - radius); |
| 3657 | ctx.moveTo(x - radius, y); |
| 3658 | ctx.lineTo(x + radius, y); |
| 3659 | xOffset = Math.cos(Math.PI / 4) * radius; |
| 3660 | yOffset = Math.sin(Math.PI / 4) * radius; |
| 3661 | ctx.moveTo(x - xOffset, y - yOffset); |
| 3662 | ctx.lineTo(x + xOffset, y + yOffset); |
| 3663 | ctx.moveTo(x - xOffset, y + yOffset); |
| 3664 | ctx.lineTo(x + xOffset, y - yOffset); |
| 3665 | ctx.closePath(); |
| 3666 | break; |
| 3667 | case 'line': |
| 3668 | ctx.beginPath(); |
| 3669 | ctx.moveTo(x - radius, y); |
| 3670 | ctx.lineTo(x + radius, y); |
| 3671 | ctx.closePath(); |
| 3672 | break; |
| 3673 | case 'dash': |
| 3674 | ctx.beginPath(); |
| 3675 | ctx.moveTo(x, y); |
| 3676 | ctx.lineTo(x + radius, y); |
| 3677 | ctx.closePath(); |
| 3678 | break; |
| 3679 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3680 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3681 | ctx.stroke(); |
| 3682 | }; |
| 3683 | |
| 3684 | helpers.clipArea = function(ctx, clipArea) { |
| 3685 | ctx.save(); |
| 3686 | ctx.beginPath(); |
| 3687 | ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top); |
| 3688 | ctx.clip(); |
| 3689 | }; |
| 3690 | |
| 3691 | helpers.unclipArea = function(ctx) { |
| 3692 | ctx.restore(); |
| 3693 | }; |
| 3694 | |
| 3695 | helpers.lineTo = function(ctx, previous, target, flip) { |
| 3696 | if (target.steppedLine) { |
| 3697 | if (target.steppedLine === 'after') { |
| 3698 | ctx.lineTo(previous.x, target.y); |
| 3699 | } else { |
| 3700 | ctx.lineTo(target.x, previous.y); |
| 3701 | } |
| 3702 | ctx.lineTo(target.x, target.y); |
| 3703 | return; |
| 3704 | } |
| 3705 | |
| 3706 | if (!target.tension) { |
| 3707 | ctx.lineTo(target.x, target.y); |
| 3708 | return; |
| 3709 | } |
| 3710 | |
| 3711 | ctx.bezierCurveTo( |
| 3712 | flip? previous.controlPointPreviousX : previous.controlPointNextX, |
| 3713 | flip? previous.controlPointPreviousY : previous.controlPointNextY, |
| 3714 | flip? target.controlPointNextX : target.controlPointPreviousX, |
| 3715 | flip? target.controlPointNextY : target.controlPointPreviousY, |
| 3716 | target.x, |
| 3717 | target.y); |
| 3718 | }; |
| 3719 | |
| 3720 | Chart.helpers.canvas = helpers; |
| 3721 | }; |
| 3722 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3723 | },{}],23:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3724 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3725 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3726 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3727 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3728 | var helpers = Chart.helpers; |
| 3729 | var plugins = Chart.plugins; |
| 3730 | var platform = Chart.platform; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3731 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3732 | // Create a dictionary of chart types, to allow for extension of existing types |
| 3733 | Chart.types = {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3734 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3735 | // Store a reference to each instance - allowing us to globally resize chart instances on window resize. |
| 3736 | // Destroy method on the chart will remove the instance of the chart from this reference. |
| 3737 | Chart.instances = {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3738 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3739 | // Controllers available for dataset visualization eg. bar, line, slice, etc. |
| 3740 | Chart.controllers = {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3741 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3742 | /** |
| 3743 | * Initializes the given config with global and chart default values. |
| 3744 | */ |
| 3745 | function initConfig(config) { |
| 3746 | config = config || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3747 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3748 | // Do NOT use configMerge() for the data object because this method merges arrays |
| 3749 | // and so would change references to labels and datasets, preventing data updates. |
| 3750 | var data = config.data = config.data || {}; |
| 3751 | data.datasets = data.datasets || []; |
| 3752 | data.labels = data.labels || []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3753 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3754 | config.options = helpers.configMerge( |
| 3755 | Chart.defaults.global, |
| 3756 | Chart.defaults[config.type], |
| 3757 | config.options || {}); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3758 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3759 | return config; |
| 3760 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3761 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3762 | /** |
| 3763 | * Updates the config of the chart |
| 3764 | * @param chart {Chart} chart to update the options for |
| 3765 | */ |
| 3766 | function updateConfig(chart) { |
| 3767 | var newOptions = chart.options; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3768 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3769 | // Update Scale(s) with options |
| 3770 | if (newOptions.scale) { |
| 3771 | chart.scale.options = newOptions.scale; |
| 3772 | } else if (newOptions.scales) { |
| 3773 | newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { |
| 3774 | chart.scales[scaleOptions.id].options = scaleOptions; |
| 3775 | }); |
| 3776 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3777 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3778 | // Tooltip |
| 3779 | chart.tooltip._options = newOptions.tooltips; |
| 3780 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3781 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3782 | function positionIsHorizontal(position) { |
| 3783 | return position === 'top' || position === 'bottom'; |
| 3784 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3785 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3786 | helpers.extend(Chart.prototype, /** @lends Chart */ { |
| 3787 | /** |
| 3788 | * @private |
| 3789 | */ |
| 3790 | construct: function(item, config) { |
| 3791 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3792 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3793 | config = initConfig(config); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3794 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3795 | var context = platform.acquireContext(item, config); |
| 3796 | var canvas = context && context.canvas; |
| 3797 | var height = canvas && canvas.height; |
| 3798 | var width = canvas && canvas.width; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3799 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3800 | me.id = helpers.uid(); |
| 3801 | me.ctx = context; |
| 3802 | me.canvas = canvas; |
| 3803 | me.config = config; |
| 3804 | me.width = width; |
| 3805 | me.height = height; |
| 3806 | me.aspectRatio = height? width / height : null; |
| 3807 | me.options = config.options; |
| 3808 | me._bufferedRender = false; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3809 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3810 | /** |
| 3811 | * Provided for backward compatibility, Chart and Chart.Controller have been merged, |
| 3812 | * the "instance" still need to be defined since it might be called from plugins. |
| 3813 | * @prop Chart#chart |
| 3814 | * @deprecated since version 2.6.0 |
| 3815 | * @todo remove at version 3 |
| 3816 | * @private |
| 3817 | */ |
| 3818 | me.chart = me; |
| 3819 | me.controller = me; // chart.chart.controller #inception |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3820 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3821 | // Add the chart instance to the global namespace |
| 3822 | Chart.instances[me.id] = me; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3823 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3824 | // Define alias to the config data: `chart.data === chart.config.data` |
| 3825 | Object.defineProperty(me, 'data', { |
| 3826 | get: function() { |
| 3827 | return me.config.data; |
| 3828 | }, |
| 3829 | set: function(value) { |
| 3830 | me.config.data = value; |
| 3831 | } |
| 3832 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3833 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3834 | if (!context || !canvas) { |
| 3835 | // The given item is not a compatible context2d element, let's return before finalizing |
| 3836 | // the chart initialization but after setting basic chart / controller properties that |
| 3837 | // can help to figure out that the chart is not valid (e.g chart.canvas !== null); |
| 3838 | // https://github.com/chartjs/Chart.js/issues/2807 |
| 3839 | console.error("Failed to create chart: can't acquire context from the given item"); |
| 3840 | return; |
| 3841 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3842 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3843 | me.initialize(); |
| 3844 | me.update(); |
| 3845 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3846 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3847 | /** |
| 3848 | * @private |
| 3849 | */ |
| 3850 | initialize: function() { |
| 3851 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3852 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3853 | // Before init plugin notification |
| 3854 | plugins.notify(me, 'beforeInit'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3855 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3856 | helpers.retinaScale(me); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3857 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3858 | me.bindEvents(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3859 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3860 | if (me.options.responsive) { |
| 3861 | // Initial resize before chart draws (must be silent to preserve initial animations). |
| 3862 | me.resize(true); |
| 3863 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3864 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3865 | // Make sure scales have IDs and are built before we build any controllers. |
| 3866 | me.ensureScalesHaveIDs(); |
| 3867 | me.buildScales(); |
| 3868 | me.initToolTip(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3869 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3870 | // After init plugin notification |
| 3871 | plugins.notify(me, 'afterInit'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3872 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3873 | return me; |
| 3874 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3875 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3876 | clear: function() { |
| 3877 | helpers.clear(this); |
| 3878 | return this; |
| 3879 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3880 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3881 | stop: function() { |
| 3882 | // Stops any current animation loop occurring |
| 3883 | Chart.animationService.cancelAnimation(this); |
| 3884 | return this; |
| 3885 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3886 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3887 | resize: function(silent) { |
| 3888 | var me = this; |
| 3889 | var options = me.options; |
| 3890 | var canvas = me.canvas; |
| 3891 | var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3892 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3893 | // the canvas render width and height will be casted to integers so make sure that |
| 3894 | // the canvas display style uses the same integer values to avoid blurring effect. |
| 3895 | var newWidth = Math.floor(helpers.getMaximumWidth(canvas)); |
| 3896 | var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3897 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3898 | if (me.width === newWidth && me.height === newHeight) { |
| 3899 | return; |
| 3900 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3901 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3902 | canvas.width = me.width = newWidth; |
| 3903 | canvas.height = me.height = newHeight; |
| 3904 | canvas.style.width = newWidth + 'px'; |
| 3905 | canvas.style.height = newHeight + 'px'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3906 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3907 | helpers.retinaScale(me); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3908 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3909 | if (!silent) { |
| 3910 | // Notify any plugins about the resize |
| 3911 | var newSize = {width: newWidth, height: newHeight}; |
| 3912 | plugins.notify(me, 'resize', [newSize]); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3913 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3914 | // Notify of resize |
| 3915 | if (me.options.onResize) { |
| 3916 | me.options.onResize(me, newSize); |
| 3917 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3918 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3919 | me.stop(); |
| 3920 | me.update(me.options.responsiveAnimationDuration); |
| 3921 | } |
| 3922 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3923 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3924 | ensureScalesHaveIDs: function() { |
| 3925 | var options = this.options; |
| 3926 | var scalesOptions = options.scales || {}; |
| 3927 | var scaleOptions = options.scale; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3928 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3929 | helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { |
| 3930 | xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); |
| 3931 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3932 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3933 | helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { |
| 3934 | yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); |
| 3935 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3936 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3937 | if (scaleOptions) { |
| 3938 | scaleOptions.id = scaleOptions.id || 'scale'; |
| 3939 | } |
| 3940 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3941 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3942 | /** |
| 3943 | * Builds a map of scale ID to scale object for future lookup. |
| 3944 | */ |
| 3945 | buildScales: function() { |
| 3946 | var me = this; |
| 3947 | var options = me.options; |
| 3948 | var scales = me.scales = {}; |
| 3949 | var items = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3950 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3951 | if (options.scales) { |
| 3952 | items = items.concat( |
| 3953 | (options.scales.xAxes || []).map(function(xAxisOptions) { |
| 3954 | return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; |
| 3955 | }), |
| 3956 | (options.scales.yAxes || []).map(function(yAxisOptions) { |
| 3957 | return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; |
| 3958 | }) |
| 3959 | ); |
| 3960 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3961 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3962 | if (options.scale) { |
| 3963 | items.push({ |
| 3964 | options: options.scale, |
| 3965 | dtype: 'radialLinear', |
| 3966 | isDefault: true, |
| 3967 | dposition: 'chartArea' |
| 3968 | }); |
| 3969 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3970 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3971 | helpers.each(items, function(item) { |
| 3972 | var scaleOptions = item.options; |
| 3973 | var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); |
| 3974 | var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); |
| 3975 | if (!scaleClass) { |
| 3976 | return; |
| 3977 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3978 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3979 | if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { |
| 3980 | scaleOptions.position = item.dposition; |
| 3981 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3982 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3983 | var scale = new scaleClass({ |
| 3984 | id: scaleOptions.id, |
| 3985 | options: scaleOptions, |
| 3986 | ctx: me.ctx, |
| 3987 | chart: me |
| 3988 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3989 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3990 | scales[scale.id] = scale; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3991 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 3992 | // TODO(SB): I think we should be able to remove this custom case (options.scale) |
| 3993 | // and consider it as a regular scale part of the "scales"" map only! This would |
| 3994 | // make the logic easier and remove some useless? custom code. |
| 3995 | if (item.isDefault) { |
| 3996 | me.scale = scale; |
| 3997 | } |
| 3998 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 3999 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4000 | Chart.scaleService.addScalesToLayout(this); |
| 4001 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4002 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4003 | buildOrUpdateControllers: function() { |
| 4004 | var me = this; |
| 4005 | var types = []; |
| 4006 | var newControllers = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4007 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4008 | helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
| 4009 | var meta = me.getDatasetMeta(datasetIndex); |
| 4010 | if (!meta.type) { |
| 4011 | meta.type = dataset.type || me.config.type; |
| 4012 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4013 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4014 | types.push(meta.type); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4015 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4016 | if (meta.controller) { |
| 4017 | meta.controller.updateIndex(datasetIndex); |
| 4018 | } else { |
| 4019 | var ControllerClass = Chart.controllers[meta.type]; |
| 4020 | if (ControllerClass === undefined) { |
| 4021 | throw new Error('"' + meta.type + '" is not a chart type.'); |
| 4022 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4023 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4024 | meta.controller = new ControllerClass(me, datasetIndex); |
| 4025 | newControllers.push(meta.controller); |
| 4026 | } |
| 4027 | }, me); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4028 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4029 | if (types.length > 1) { |
| 4030 | for (var i = 1; i < types.length; i++) { |
| 4031 | if (types[i] !== types[i - 1]) { |
| 4032 | me.isCombo = true; |
| 4033 | break; |
| 4034 | } |
| 4035 | } |
| 4036 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4037 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4038 | return newControllers; |
| 4039 | }, |
| 4040 | |
| 4041 | /** |
| 4042 | * Reset the elements of all datasets |
| 4043 | * @private |
| 4044 | */ |
| 4045 | resetElements: function() { |
| 4046 | var me = this; |
| 4047 | helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
| 4048 | me.getDatasetMeta(datasetIndex).controller.reset(); |
| 4049 | }, me); |
| 4050 | }, |
| 4051 | |
| 4052 | /** |
| 4053 | * Resets the chart back to it's state before the initial animation |
| 4054 | */ |
| 4055 | reset: function() { |
| 4056 | this.resetElements(); |
| 4057 | this.tooltip.initialize(); |
| 4058 | }, |
| 4059 | |
| 4060 | update: function(animationDuration, lazy) { |
| 4061 | var me = this; |
| 4062 | |
| 4063 | updateConfig(me); |
| 4064 | |
| 4065 | if (plugins.notify(me, 'beforeUpdate') === false) { |
| 4066 | return; |
| 4067 | } |
| 4068 | |
| 4069 | // In case the entire data object changed |
| 4070 | me.tooltip._data = me.data; |
| 4071 | |
| 4072 | // Make sure dataset controllers are updated and new controllers are reset |
| 4073 | var newControllers = me.buildOrUpdateControllers(); |
| 4074 | |
| 4075 | // Make sure all dataset controllers have correct meta data counts |
| 4076 | helpers.each(me.data.datasets, function(dataset, datasetIndex) { |
| 4077 | me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); |
| 4078 | }, me); |
| 4079 | |
| 4080 | me.updateLayout(); |
| 4081 | |
| 4082 | // Can only reset the new controllers after the scales have been updated |
| 4083 | helpers.each(newControllers, function(controller) { |
| 4084 | controller.reset(); |
| 4085 | }); |
| 4086 | |
| 4087 | me.updateDatasets(); |
| 4088 | |
| 4089 | // Do this before render so that any plugins that need final scale updates can use it |
| 4090 | plugins.notify(me, 'afterUpdate'); |
| 4091 | |
| 4092 | if (me._bufferedRender) { |
| 4093 | me._bufferedRequest = { |
| 4094 | lazy: lazy, |
| 4095 | duration: animationDuration |
| 4096 | }; |
| 4097 | } else { |
| 4098 | me.render(animationDuration, lazy); |
| 4099 | } |
| 4100 | }, |
| 4101 | |
| 4102 | /** |
| 4103 | * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` |
| 4104 | * hook, in which case, plugins will not be called on `afterLayout`. |
| 4105 | * @private |
| 4106 | */ |
| 4107 | updateLayout: function() { |
| 4108 | var me = this; |
| 4109 | |
| 4110 | if (plugins.notify(me, 'beforeLayout') === false) { |
| 4111 | return; |
| 4112 | } |
| 4113 | |
| 4114 | Chart.layoutService.update(this, this.width, this.height); |
| 4115 | |
| 4116 | /** |
| 4117 | * Provided for backward compatibility, use `afterLayout` instead. |
| 4118 | * @method IPlugin#afterScaleUpdate |
| 4119 | * @deprecated since version 2.5.0 |
| 4120 | * @todo remove at version 3 |
| 4121 | * @private |
| 4122 | */ |
| 4123 | plugins.notify(me, 'afterScaleUpdate'); |
| 4124 | plugins.notify(me, 'afterLayout'); |
| 4125 | }, |
| 4126 | |
| 4127 | /** |
| 4128 | * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` |
| 4129 | * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. |
| 4130 | * @private |
| 4131 | */ |
| 4132 | updateDatasets: function() { |
| 4133 | var me = this; |
| 4134 | |
| 4135 | if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { |
| 4136 | return; |
| 4137 | } |
| 4138 | |
| 4139 | for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { |
| 4140 | me.updateDataset(i); |
| 4141 | } |
| 4142 | |
| 4143 | plugins.notify(me, 'afterDatasetsUpdate'); |
| 4144 | }, |
| 4145 | |
| 4146 | /** |
| 4147 | * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` |
| 4148 | * hook, in which case, plugins will not be called on `afterDatasetUpdate`. |
| 4149 | * @private |
| 4150 | */ |
| 4151 | updateDataset: function(index) { |
| 4152 | var me = this; |
| 4153 | var meta = me.getDatasetMeta(index); |
| 4154 | var args = { |
| 4155 | meta: meta, |
| 4156 | index: index |
| 4157 | }; |
| 4158 | |
| 4159 | if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { |
| 4160 | return; |
| 4161 | } |
| 4162 | |
| 4163 | meta.controller.update(); |
| 4164 | |
| 4165 | plugins.notify(me, 'afterDatasetUpdate', [args]); |
| 4166 | }, |
| 4167 | |
| 4168 | render: function(duration, lazy) { |
| 4169 | var me = this; |
| 4170 | |
| 4171 | if (plugins.notify(me, 'beforeRender') === false) { |
| 4172 | return; |
| 4173 | } |
| 4174 | |
| 4175 | var animationOptions = me.options.animation; |
| 4176 | var onComplete = function(animation) { |
| 4177 | plugins.notify(me, 'afterRender'); |
| 4178 | helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); |
| 4179 | }; |
| 4180 | |
| 4181 | if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { |
| 4182 | var animation = new Chart.Animation({ |
| 4183 | numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps |
| 4184 | easing: animationOptions.easing, |
| 4185 | |
| 4186 | render: function(chart, animationObject) { |
| 4187 | var easingFunction = helpers.easingEffects[animationObject.easing]; |
| 4188 | var currentStep = animationObject.currentStep; |
| 4189 | var stepDecimal = currentStep / animationObject.numSteps; |
| 4190 | |
| 4191 | chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); |
| 4192 | }, |
| 4193 | |
| 4194 | onAnimationProgress: animationOptions.onProgress, |
| 4195 | onAnimationComplete: onComplete |
| 4196 | }); |
| 4197 | |
| 4198 | Chart.animationService.addAnimation(me, animation, duration, lazy); |
| 4199 | } else { |
| 4200 | me.draw(); |
| 4201 | |
| 4202 | // See https://github.com/chartjs/Chart.js/issues/3781 |
| 4203 | onComplete(new Chart.Animation({numSteps: 0, chart: me})); |
| 4204 | } |
| 4205 | |
| 4206 | return me; |
| 4207 | }, |
| 4208 | |
| 4209 | draw: function(easingValue) { |
| 4210 | var me = this; |
| 4211 | |
| 4212 | me.clear(); |
| 4213 | |
| 4214 | if (easingValue === undefined || easingValue === null) { |
| 4215 | easingValue = 1; |
| 4216 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4217 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4218 | me.transition(easingValue); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4219 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4220 | if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { |
| 4221 | return; |
| 4222 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4223 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4224 | // Draw all the scales |
| 4225 | helpers.each(me.boxes, function(box) { |
| 4226 | box.draw(me.chartArea); |
| 4227 | }, me); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4228 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4229 | if (me.scale) { |
| 4230 | me.scale.draw(); |
| 4231 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4232 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4233 | me.drawDatasets(easingValue); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4234 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4235 | // Finally draw the tooltip |
| 4236 | me.tooltip.draw(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4237 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4238 | plugins.notify(me, 'afterDraw', [easingValue]); |
| 4239 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4240 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4241 | /** |
| 4242 | * @private |
| 4243 | */ |
| 4244 | transition: function(easingValue) { |
| 4245 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4246 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4247 | for (var i=0, ilen=(me.data.datasets || []).length; i<ilen; ++i) { |
| 4248 | if (me.isDatasetVisible(i)) { |
| 4249 | me.getDatasetMeta(i).controller.transition(easingValue); |
| 4250 | } |
| 4251 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4252 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4253 | me.tooltip.transition(easingValue); |
| 4254 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4255 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4256 | /** |
| 4257 | * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` |
| 4258 | * hook, in which case, plugins will not be called on `afterDatasetsDraw`. |
| 4259 | * @private |
| 4260 | */ |
| 4261 | drawDatasets: function(easingValue) { |
| 4262 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4263 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4264 | if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { |
| 4265 | return; |
| 4266 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4267 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4268 | // Draw datasets reversed to support proper line stacking |
| 4269 | for (var i=(me.data.datasets || []).length - 1; i >= 0; --i) { |
| 4270 | if (me.isDatasetVisible(i)) { |
| 4271 | me.drawDataset(i, easingValue); |
| 4272 | } |
| 4273 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4274 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4275 | plugins.notify(me, 'afterDatasetsDraw', [easingValue]); |
| 4276 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4277 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4278 | /** |
| 4279 | * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` |
| 4280 | * hook, in which case, plugins will not be called on `afterDatasetDraw`. |
| 4281 | * @private |
| 4282 | */ |
| 4283 | drawDataset: function(index, easingValue) { |
| 4284 | var me = this; |
| 4285 | var meta = me.getDatasetMeta(index); |
| 4286 | var args = { |
| 4287 | meta: meta, |
| 4288 | index: index, |
| 4289 | easingValue: easingValue |
| 4290 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4291 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4292 | if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { |
| 4293 | return; |
| 4294 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4295 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4296 | meta.controller.draw(easingValue); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4297 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4298 | plugins.notify(me, 'afterDatasetDraw', [args]); |
| 4299 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4300 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4301 | // Get the single element that was clicked on |
| 4302 | // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw |
| 4303 | getElementAtEvent: function(e) { |
| 4304 | return Chart.Interaction.modes.single(this, e); |
| 4305 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4306 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4307 | getElementsAtEvent: function(e) { |
| 4308 | return Chart.Interaction.modes.label(this, e, {intersect: true}); |
| 4309 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4310 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4311 | getElementsAtXAxis: function(e) { |
| 4312 | return Chart.Interaction.modes['x-axis'](this, e, {intersect: true}); |
| 4313 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4314 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4315 | getElementsAtEventForMode: function(e, mode, options) { |
| 4316 | var method = Chart.Interaction.modes[mode]; |
| 4317 | if (typeof method === 'function') { |
| 4318 | return method(this, e, options); |
| 4319 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4320 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4321 | return []; |
| 4322 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4323 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4324 | getDatasetAtEvent: function(e) { |
| 4325 | return Chart.Interaction.modes.dataset(this, e, {intersect: true}); |
| 4326 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4327 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4328 | getDatasetMeta: function(datasetIndex) { |
| 4329 | var me = this; |
| 4330 | var dataset = me.data.datasets[datasetIndex]; |
| 4331 | if (!dataset._meta) { |
| 4332 | dataset._meta = {}; |
| 4333 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4334 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4335 | var meta = dataset._meta[me.id]; |
| 4336 | if (!meta) { |
| 4337 | meta = dataset._meta[me.id] = { |
| 4338 | type: null, |
| 4339 | data: [], |
| 4340 | dataset: null, |
| 4341 | controller: null, |
| 4342 | hidden: null, // See isDatasetVisible() comment |
| 4343 | xAxisID: null, |
| 4344 | yAxisID: null |
| 4345 | }; |
| 4346 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4347 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4348 | return meta; |
| 4349 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4350 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4351 | getVisibleDatasetCount: function() { |
| 4352 | var count = 0; |
| 4353 | for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) { |
| 4354 | if (this.isDatasetVisible(i)) { |
| 4355 | count++; |
| 4356 | } |
| 4357 | } |
| 4358 | return count; |
| 4359 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4360 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4361 | isDatasetVisible: function(datasetIndex) { |
| 4362 | var meta = this.getDatasetMeta(datasetIndex); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4363 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4364 | // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, |
| 4365 | // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. |
| 4366 | return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden; |
| 4367 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4368 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4369 | generateLegend: function() { |
| 4370 | return this.options.legendCallback(this); |
| 4371 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4372 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4373 | destroy: function() { |
| 4374 | var me = this; |
| 4375 | var canvas = me.canvas; |
| 4376 | var meta, i, ilen; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4377 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4378 | me.stop(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4379 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4380 | // dataset controllers need to cleanup associated data |
| 4381 | for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { |
| 4382 | meta = me.getDatasetMeta(i); |
| 4383 | if (meta.controller) { |
| 4384 | meta.controller.destroy(); |
| 4385 | meta.controller = null; |
| 4386 | } |
| 4387 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4388 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4389 | if (canvas) { |
| 4390 | me.unbindEvents(); |
| 4391 | helpers.clear(me); |
| 4392 | platform.releaseContext(me.ctx); |
| 4393 | me.canvas = null; |
| 4394 | me.ctx = null; |
| 4395 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4396 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4397 | plugins.notify(me, 'destroy'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4398 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4399 | delete Chart.instances[me.id]; |
| 4400 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4401 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4402 | toBase64Image: function() { |
| 4403 | return this.canvas.toDataURL.apply(this.canvas, arguments); |
| 4404 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4405 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4406 | initToolTip: function() { |
| 4407 | var me = this; |
| 4408 | me.tooltip = new Chart.Tooltip({ |
| 4409 | _chart: me, |
| 4410 | _chartInstance: me, // deprecated, backward compatibility |
| 4411 | _data: me.data, |
| 4412 | _options: me.options.tooltips |
| 4413 | }, me); |
| 4414 | me.tooltip.initialize(); |
| 4415 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4416 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4417 | /** |
| 4418 | * @private |
| 4419 | */ |
| 4420 | bindEvents: function() { |
| 4421 | var me = this; |
| 4422 | var listeners = me._listeners = {}; |
| 4423 | var listener = function() { |
| 4424 | me.eventHandler.apply(me, arguments); |
| 4425 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4426 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4427 | helpers.each(me.options.events, function(type) { |
| 4428 | platform.addEventListener(me, type, listener); |
| 4429 | listeners[type] = listener; |
| 4430 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4431 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4432 | // Responsiveness is currently based on the use of an iframe, however this method causes |
| 4433 | // performance issues and could be troublesome when used with ad blockers. So make sure |
| 4434 | // that the user is still able to create a chart without iframe when responsive is false. |
| 4435 | // See https://github.com/chartjs/Chart.js/issues/2210 |
| 4436 | if (me.options.responsive) { |
| 4437 | listener = function() { |
| 4438 | me.resize(); |
| 4439 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4440 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4441 | platform.addEventListener(me, 'resize', listener); |
| 4442 | listeners.resize = listener; |
| 4443 | } |
| 4444 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4445 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4446 | /** |
| 4447 | * @private |
| 4448 | */ |
| 4449 | unbindEvents: function() { |
| 4450 | var me = this; |
| 4451 | var listeners = me._listeners; |
| 4452 | if (!listeners) { |
| 4453 | return; |
| 4454 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4455 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4456 | delete me._listeners; |
| 4457 | helpers.each(listeners, function(listener, type) { |
| 4458 | platform.removeEventListener(me, type, listener); |
| 4459 | }); |
| 4460 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4461 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4462 | updateHoverStyle: function(elements, mode, enabled) { |
| 4463 | var method = enabled? 'setHoverStyle' : 'removeHoverStyle'; |
| 4464 | var element, i, ilen; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4465 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4466 | for (i=0, ilen=elements.length; i<ilen; ++i) { |
| 4467 | element = elements[i]; |
| 4468 | if (element) { |
| 4469 | this.getDatasetMeta(element._datasetIndex).controller[method](element); |
| 4470 | } |
| 4471 | } |
| 4472 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4473 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4474 | /** |
| 4475 | * @private |
| 4476 | */ |
| 4477 | eventHandler: function(e) { |
| 4478 | var me = this; |
| 4479 | var tooltip = me.tooltip; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4480 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4481 | if (plugins.notify(me, 'beforeEvent', [e]) === false) { |
| 4482 | return; |
| 4483 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4484 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4485 | // Buffer any update calls so that renders do not occur |
| 4486 | me._bufferedRender = true; |
| 4487 | me._bufferedRequest = null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4488 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4489 | var changed = me.handleEvent(e); |
| 4490 | changed |= tooltip && tooltip.handleEvent(e); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4491 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4492 | plugins.notify(me, 'afterEvent', [e]); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4493 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4494 | var bufferedRequest = me._bufferedRequest; |
| 4495 | if (bufferedRequest) { |
| 4496 | // If we have an update that was triggered, we need to do a normal render |
| 4497 | me.render(bufferedRequest.duration, bufferedRequest.lazy); |
| 4498 | } else if (changed && !me.animating) { |
| 4499 | // If entering, leaving, or changing elements, animate the change via pivot |
| 4500 | me.stop(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4501 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4502 | // We only need to render at this point. Updating will cause scales to be |
| 4503 | // recomputed generating flicker & using more memory than necessary. |
| 4504 | me.render(me.options.hover.animationDuration, true); |
| 4505 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4506 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4507 | me._bufferedRender = false; |
| 4508 | me._bufferedRequest = null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4509 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4510 | return me; |
| 4511 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4512 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4513 | /** |
| 4514 | * Handle an event |
| 4515 | * @private |
| 4516 | * @param {IEvent} event the event to handle |
| 4517 | * @return {Boolean} true if the chart needs to re-render |
| 4518 | */ |
| 4519 | handleEvent: function(e) { |
| 4520 | var me = this; |
| 4521 | var options = me.options || {}; |
| 4522 | var hoverOptions = options.hover; |
| 4523 | var changed = false; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4524 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4525 | me.lastActive = me.lastActive || []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4526 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4527 | // Find Active Elements for hover and tooltips |
| 4528 | if (e.type === 'mouseout') { |
| 4529 | me.active = []; |
| 4530 | } else { |
| 4531 | me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); |
| 4532 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4533 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4534 | // On Hover hook |
| 4535 | if (hoverOptions.onHover) { |
| 4536 | // Need to call with native event here to not break backwards compatibility |
| 4537 | hoverOptions.onHover.call(me, e.native, me.active); |
| 4538 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4539 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4540 | if (e.type === 'mouseup' || e.type === 'click') { |
| 4541 | if (options.onClick) { |
| 4542 | // Use e.native here for backwards compatibility |
| 4543 | options.onClick.call(me, e.native, me.active); |
| 4544 | } |
| 4545 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4546 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4547 | // Remove styling for last active (even if it may still be active) |
| 4548 | if (me.lastActive.length) { |
| 4549 | me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); |
| 4550 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4551 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4552 | // Built in hover styling |
| 4553 | if (me.active.length && hoverOptions.mode) { |
| 4554 | me.updateHoverStyle(me.active, hoverOptions.mode, true); |
| 4555 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4556 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4557 | changed = !helpers.arrayEquals(me.active, me.lastActive); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4558 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4559 | // Remember Last Actives |
| 4560 | me.lastActive = me.active; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4561 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4562 | return changed; |
| 4563 | } |
| 4564 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4565 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4566 | /** |
| 4567 | * Provided for backward compatibility, use Chart instead. |
| 4568 | * @class Chart.Controller |
| 4569 | * @deprecated since version 2.6.0 |
| 4570 | * @todo remove at version 3 |
| 4571 | * @private |
| 4572 | */ |
| 4573 | Chart.Controller = Chart; |
| 4574 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4575 | |
| 4576 | },{}],24:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4577 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4578 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4579 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4580 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4581 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4582 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4583 | var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4584 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4585 | /** |
| 4586 | * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', |
| 4587 | * 'unshift') and notify the listener AFTER the array has been altered. Listeners are |
| 4588 | * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. |
| 4589 | */ |
| 4590 | function listenArrayEvents(array, listener) { |
| 4591 | if (array._chartjs) { |
| 4592 | array._chartjs.listeners.push(listener); |
| 4593 | return; |
| 4594 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4595 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4596 | Object.defineProperty(array, '_chartjs', { |
| 4597 | configurable: true, |
| 4598 | enumerable: false, |
| 4599 | value: { |
| 4600 | listeners: [listener] |
| 4601 | } |
| 4602 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4603 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4604 | arrayEvents.forEach(function(key) { |
| 4605 | var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); |
| 4606 | var base = array[key]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4607 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4608 | Object.defineProperty(array, key, { |
| 4609 | configurable: true, |
| 4610 | enumerable: false, |
| 4611 | value: function() { |
| 4612 | var args = Array.prototype.slice.call(arguments); |
| 4613 | var res = base.apply(this, args); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4614 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4615 | helpers.each(array._chartjs.listeners, function(object) { |
| 4616 | if (typeof object[method] === 'function') { |
| 4617 | object[method].apply(object, args); |
| 4618 | } |
| 4619 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4620 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4621 | return res; |
| 4622 | } |
| 4623 | }); |
| 4624 | }); |
| 4625 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4626 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4627 | /** |
| 4628 | * Removes the given array event listener and cleanup extra attached properties (such as |
| 4629 | * the _chartjs stub and overridden methods) if array doesn't have any more listeners. |
| 4630 | */ |
| 4631 | function unlistenArrayEvents(array, listener) { |
| 4632 | var stub = array._chartjs; |
| 4633 | if (!stub) { |
| 4634 | return; |
| 4635 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4636 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4637 | var listeners = stub.listeners; |
| 4638 | var index = listeners.indexOf(listener); |
| 4639 | if (index !== -1) { |
| 4640 | listeners.splice(index, 1); |
| 4641 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4642 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4643 | if (listeners.length > 0) { |
| 4644 | return; |
| 4645 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4646 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4647 | arrayEvents.forEach(function(key) { |
| 4648 | delete array[key]; |
| 4649 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4650 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4651 | delete array._chartjs; |
| 4652 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4653 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4654 | // Base class for all dataset controllers (line, bar, etc) |
| 4655 | Chart.DatasetController = function(chart, datasetIndex) { |
| 4656 | this.initialize(chart, datasetIndex); |
| 4657 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4658 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4659 | helpers.extend(Chart.DatasetController.prototype, { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4660 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4661 | /** |
| 4662 | * Element type used to generate a meta dataset (e.g. Chart.element.Line). |
| 4663 | * @type {Chart.core.element} |
| 4664 | */ |
| 4665 | datasetElementType: null, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4666 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4667 | /** |
| 4668 | * Element type used to generate a meta data (e.g. Chart.element.Point). |
| 4669 | * @type {Chart.core.element} |
| 4670 | */ |
| 4671 | dataElementType: null, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4672 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4673 | initialize: function(chart, datasetIndex) { |
| 4674 | var me = this; |
| 4675 | me.chart = chart; |
| 4676 | me.index = datasetIndex; |
| 4677 | me.linkScales(); |
| 4678 | me.addElements(); |
| 4679 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4680 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4681 | updateIndex: function(datasetIndex) { |
| 4682 | this.index = datasetIndex; |
| 4683 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4684 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4685 | linkScales: function() { |
| 4686 | var me = this; |
| 4687 | var meta = me.getMeta(); |
| 4688 | var dataset = me.getDataset(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4689 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4690 | if (meta.xAxisID === null) { |
| 4691 | meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; |
| 4692 | } |
| 4693 | if (meta.yAxisID === null) { |
| 4694 | meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; |
| 4695 | } |
| 4696 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4697 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4698 | getDataset: function() { |
| 4699 | return this.chart.data.datasets[this.index]; |
| 4700 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4701 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4702 | getMeta: function() { |
| 4703 | return this.chart.getDatasetMeta(this.index); |
| 4704 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4705 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4706 | getScaleForId: function(scaleID) { |
| 4707 | return this.chart.scales[scaleID]; |
| 4708 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4709 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4710 | reset: function() { |
| 4711 | this.update(true); |
| 4712 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4713 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4714 | /** |
| 4715 | * @private |
| 4716 | */ |
| 4717 | destroy: function() { |
| 4718 | if (this._data) { |
| 4719 | unlistenArrayEvents(this._data, this); |
| 4720 | } |
| 4721 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4722 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4723 | createMetaDataset: function() { |
| 4724 | var me = this; |
| 4725 | var type = me.datasetElementType; |
| 4726 | return type && new type({ |
| 4727 | _chart: me.chart, |
| 4728 | _datasetIndex: me.index |
| 4729 | }); |
| 4730 | }, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4731 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4732 | createMetaData: function(index) { |
| 4733 | var me = this; |
| 4734 | var type = me.dataElementType; |
| 4735 | return type && new type({ |
| 4736 | _chart: me.chart, |
| 4737 | _datasetIndex: me.index, |
| 4738 | _index: index |
| 4739 | }); |
| 4740 | }, |
| 4741 | |
| 4742 | addElements: function() { |
| 4743 | var me = this; |
| 4744 | var meta = me.getMeta(); |
| 4745 | var data = me.getDataset().data || []; |
| 4746 | var metaData = meta.data; |
| 4747 | var i, ilen; |
| 4748 | |
| 4749 | for (i=0, ilen=data.length; i<ilen; ++i) { |
| 4750 | metaData[i] = metaData[i] || me.createMetaData(i); |
| 4751 | } |
| 4752 | |
| 4753 | meta.dataset = meta.dataset || me.createMetaDataset(); |
| 4754 | }, |
| 4755 | |
| 4756 | addElementAndReset: function(index) { |
| 4757 | var element = this.createMetaData(index); |
| 4758 | this.getMeta().data.splice(index, 0, element); |
| 4759 | this.updateElement(element, index, true); |
| 4760 | }, |
| 4761 | |
| 4762 | buildOrUpdateElements: function() { |
| 4763 | var me = this; |
| 4764 | var dataset = me.getDataset(); |
| 4765 | var data = dataset.data || (dataset.data = []); |
| 4766 | |
| 4767 | // In order to correctly handle data addition/deletion animation (an thus simulate |
| 4768 | // real-time charts), we need to monitor these data modifications and synchronize |
| 4769 | // the internal meta data accordingly. |
| 4770 | if (me._data !== data) { |
| 4771 | if (me._data) { |
| 4772 | // This case happens when the user replaced the data array instance. |
| 4773 | unlistenArrayEvents(me._data, me); |
| 4774 | } |
| 4775 | |
| 4776 | listenArrayEvents(data, me); |
| 4777 | me._data = data; |
| 4778 | } |
| 4779 | |
| 4780 | // Re-sync meta data in case the user replaced the data array or if we missed |
| 4781 | // any updates and so make sure that we handle number of datapoints changing. |
| 4782 | me.resyncElements(); |
| 4783 | }, |
| 4784 | |
| 4785 | update: helpers.noop, |
| 4786 | |
| 4787 | transition: function(easingValue) { |
| 4788 | var meta = this.getMeta(); |
| 4789 | var elements = meta.data || []; |
| 4790 | var ilen = elements.length; |
| 4791 | var i = 0; |
| 4792 | |
| 4793 | for (; i<ilen; ++i) { |
| 4794 | elements[i].transition(easingValue); |
| 4795 | } |
| 4796 | |
| 4797 | if (meta.dataset) { |
| 4798 | meta.dataset.transition(easingValue); |
| 4799 | } |
| 4800 | }, |
| 4801 | |
| 4802 | draw: function() { |
| 4803 | var meta = this.getMeta(); |
| 4804 | var elements = meta.data || []; |
| 4805 | var ilen = elements.length; |
| 4806 | var i = 0; |
| 4807 | |
| 4808 | if (meta.dataset) { |
| 4809 | meta.dataset.draw(); |
| 4810 | } |
| 4811 | |
| 4812 | for (; i<ilen; ++i) { |
| 4813 | elements[i].draw(); |
| 4814 | } |
| 4815 | }, |
| 4816 | |
| 4817 | removeHoverStyle: function(element, elementOpts) { |
| 4818 | var dataset = this.chart.data.datasets[element._datasetIndex], |
| 4819 | index = element._index, |
| 4820 | custom = element.custom || {}, |
| 4821 | valueOrDefault = helpers.getValueAtIndexOrDefault, |
| 4822 | model = element._model; |
| 4823 | |
| 4824 | model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); |
| 4825 | model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); |
| 4826 | model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); |
| 4827 | }, |
| 4828 | |
| 4829 | setHoverStyle: function(element) { |
| 4830 | var dataset = this.chart.data.datasets[element._datasetIndex], |
| 4831 | index = element._index, |
| 4832 | custom = element.custom || {}, |
| 4833 | valueOrDefault = helpers.getValueAtIndexOrDefault, |
| 4834 | getHoverColor = helpers.getHoverColor, |
| 4835 | model = element._model; |
| 4836 | |
| 4837 | model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); |
| 4838 | model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); |
| 4839 | model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); |
| 4840 | }, |
| 4841 | |
| 4842 | /** |
| 4843 | * @private |
| 4844 | */ |
| 4845 | resyncElements: function() { |
| 4846 | var me = this; |
| 4847 | var meta = me.getMeta(); |
| 4848 | var data = me.getDataset().data; |
| 4849 | var numMeta = meta.data.length; |
| 4850 | var numData = data.length; |
| 4851 | |
| 4852 | if (numData < numMeta) { |
| 4853 | meta.data.splice(numData, numMeta - numData); |
| 4854 | } else if (numData > numMeta) { |
| 4855 | me.insertElements(numMeta, numData - numMeta); |
| 4856 | } |
| 4857 | }, |
| 4858 | |
| 4859 | /** |
| 4860 | * @private |
| 4861 | */ |
| 4862 | insertElements: function(start, count) { |
| 4863 | for (var i=0; i<count; ++i) { |
| 4864 | this.addElementAndReset(start + i); |
| 4865 | } |
| 4866 | }, |
| 4867 | |
| 4868 | /** |
| 4869 | * @private |
| 4870 | */ |
| 4871 | onDataPush: function() { |
| 4872 | this.insertElements(this.getDataset().data.length-1, arguments.length); |
| 4873 | }, |
| 4874 | |
| 4875 | /** |
| 4876 | * @private |
| 4877 | */ |
| 4878 | onDataPop: function() { |
| 4879 | this.getMeta().data.pop(); |
| 4880 | }, |
| 4881 | |
| 4882 | /** |
| 4883 | * @private |
| 4884 | */ |
| 4885 | onDataShift: function() { |
| 4886 | this.getMeta().data.shift(); |
| 4887 | }, |
| 4888 | |
| 4889 | /** |
| 4890 | * @private |
| 4891 | */ |
| 4892 | onDataSplice: function(start, count) { |
| 4893 | this.getMeta().data.splice(start, count); |
| 4894 | this.insertElements(start, arguments.length - 2); |
| 4895 | }, |
| 4896 | |
| 4897 | /** |
| 4898 | * @private |
| 4899 | */ |
| 4900 | onDataUnshift: function() { |
| 4901 | this.insertElements(0, arguments.length); |
| 4902 | } |
| 4903 | }); |
| 4904 | |
| 4905 | Chart.DatasetController.extend = helpers.inherits; |
| 4906 | }; |
| 4907 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 4908 | },{}],25:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4909 | 'use strict'; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4910 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4911 | var color = require(3); |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4912 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4913 | module.exports = function(Chart) { |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4914 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4915 | var helpers = Chart.helpers; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4916 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4917 | function interpolate(start, view, model, ease) { |
| 4918 | var keys = Object.keys(model); |
| 4919 | var i, ilen, key, actual, origin, target, type, c0, c1; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4920 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4921 | for (i=0, ilen=keys.length; i<ilen; ++i) { |
| 4922 | key = keys[i]; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4923 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4924 | target = model[key]; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4925 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4926 | // if a value is added to the model after pivot() has been called, the view |
| 4927 | // doesn't contain it, so let's initialize the view to the target value. |
| 4928 | if (!view.hasOwnProperty(key)) { |
| 4929 | view[key] = target; |
| 4930 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4931 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4932 | actual = view[key]; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4933 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4934 | if (actual === target || key[0] === '_') { |
| 4935 | continue; |
| 4936 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4937 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4938 | if (!start.hasOwnProperty(key)) { |
| 4939 | start[key] = actual; |
| 4940 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4941 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4942 | origin = start[key]; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4943 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4944 | type = typeof(target); |
| 4945 | |
| 4946 | if (type === typeof(origin)) { |
| 4947 | if (type === 'string') { |
| 4948 | c0 = color(origin); |
| 4949 | if (c0.valid) { |
| 4950 | c1 = color(target); |
| 4951 | if (c1.valid) { |
| 4952 | view[key] = c1.mix(c0, ease).rgbString(); |
| 4953 | continue; |
| 4954 | } |
| 4955 | } |
| 4956 | } else if (type === 'number' && isFinite(origin) && isFinite(target)) { |
| 4957 | view[key] = origin + (target - origin) * ease; |
| 4958 | continue; |
| 4959 | } |
| 4960 | } |
| 4961 | |
| 4962 | view[key] = target; |
| 4963 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 4964 | } |
| 4965 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 4966 | Chart.elements = {}; |
| 4967 | |
| 4968 | Chart.Element = function(configuration) { |
| 4969 | helpers.extend(this, configuration); |
| 4970 | this.initialize.apply(this, arguments); |
| 4971 | }; |
| 4972 | |
| 4973 | helpers.extend(Chart.Element.prototype, { |
| 4974 | |
| 4975 | initialize: function() { |
| 4976 | this.hidden = false; |
| 4977 | }, |
| 4978 | |
| 4979 | pivot: function() { |
| 4980 | var me = this; |
| 4981 | if (!me._view) { |
| 4982 | me._view = helpers.clone(me._model); |
| 4983 | } |
| 4984 | me._start = {}; |
| 4985 | return me; |
| 4986 | }, |
| 4987 | |
| 4988 | transition: function(ease) { |
| 4989 | var me = this; |
| 4990 | var model = me._model; |
| 4991 | var start = me._start; |
| 4992 | var view = me._view; |
| 4993 | |
| 4994 | // No animation -> No Transition |
| 4995 | if (!model || ease === 1) { |
| 4996 | me._view = model; |
| 4997 | me._start = null; |
| 4998 | return me; |
| 4999 | } |
| 5000 | |
| 5001 | if (!view) { |
| 5002 | view = me._view = {}; |
| 5003 | } |
| 5004 | |
| 5005 | if (!start) { |
| 5006 | start = me._start = {}; |
| 5007 | } |
| 5008 | |
| 5009 | interpolate(start, view, model, ease); |
| 5010 | |
| 5011 | return me; |
| 5012 | }, |
| 5013 | |
| 5014 | tooltipPosition: function() { |
| 5015 | return { |
| 5016 | x: this._model.x, |
| 5017 | y: this._model.y |
| 5018 | }; |
| 5019 | }, |
| 5020 | |
| 5021 | hasValue: function() { |
| 5022 | return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); |
| 5023 | } |
| 5024 | }); |
| 5025 | |
| 5026 | Chart.Element.extend = helpers.inherits; |
| 5027 | }; |
| 5028 | |
| 5029 | },{"3":3}],26:[function(require,module,exports){ |
| 5030 | /* global window: false */ |
| 5031 | /* global document: false */ |
| 5032 | 'use strict'; |
| 5033 | |
| 5034 | var color = require(3); |
| 5035 | |
| 5036 | module.exports = function(Chart) { |
| 5037 | // Global Chart helpers object for utility methods and classes |
| 5038 | var helpers = Chart.helpers = {}; |
| 5039 | |
| 5040 | // -- Basic js utility methods |
| 5041 | helpers.each = function(loopable, callback, self, reverse) { |
| 5042 | // Check to see if null or undefined firstly. |
| 5043 | var i, len; |
| 5044 | if (helpers.isArray(loopable)) { |
| 5045 | len = loopable.length; |
| 5046 | if (reverse) { |
| 5047 | for (i = len - 1; i >= 0; i--) { |
| 5048 | callback.call(self, loopable[i], i); |
| 5049 | } |
| 5050 | } else { |
| 5051 | for (i = 0; i < len; i++) { |
| 5052 | callback.call(self, loopable[i], i); |
| 5053 | } |
| 5054 | } |
| 5055 | } else if (typeof loopable === 'object') { |
| 5056 | var keys = Object.keys(loopable); |
| 5057 | len = keys.length; |
| 5058 | for (i = 0; i < len; i++) { |
| 5059 | callback.call(self, loopable[keys[i]], keys[i]); |
| 5060 | } |
| 5061 | } |
| 5062 | }; |
| 5063 | helpers.clone = function(obj) { |
| 5064 | var objClone = {}; |
| 5065 | helpers.each(obj, function(value, key) { |
| 5066 | if (helpers.isArray(value)) { |
| 5067 | objClone[key] = value.slice(0); |
| 5068 | } else if (typeof value === 'object' && value !== null) { |
| 5069 | objClone[key] = helpers.clone(value); |
| 5070 | } else { |
| 5071 | objClone[key] = value; |
| 5072 | } |
| 5073 | }); |
| 5074 | return objClone; |
| 5075 | }; |
| 5076 | helpers.extend = function(base) { |
| 5077 | var setFn = function(value, key) { |
| 5078 | base[key] = value; |
| 5079 | }; |
| 5080 | for (var i = 1, ilen = arguments.length; i < ilen; i++) { |
| 5081 | helpers.each(arguments[i], setFn); |
| 5082 | } |
| 5083 | return base; |
| 5084 | }; |
| 5085 | // Need a special merge function to chart configs since they are now grouped |
| 5086 | helpers.configMerge = function(_base) { |
| 5087 | var base = helpers.clone(_base); |
| 5088 | helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) { |
| 5089 | helpers.each(extension, function(value, key) { |
| 5090 | var baseHasProperty = base.hasOwnProperty(key); |
| 5091 | var baseVal = baseHasProperty ? base[key] : {}; |
| 5092 | |
| 5093 | if (key === 'scales') { |
| 5094 | // Scale config merging is complex. Add our own function here for that |
| 5095 | base[key] = helpers.scaleMerge(baseVal, value); |
| 5096 | } else if (key === 'scale') { |
| 5097 | // Used in polar area & radar charts since there is only one scale |
| 5098 | base[key] = helpers.configMerge(baseVal, Chart.scaleService.getScaleDefaults(value.type), value); |
| 5099 | } else if (baseHasProperty |
| 5100 | && typeof baseVal === 'object' |
| 5101 | && !helpers.isArray(baseVal) |
| 5102 | && baseVal !== null |
| 5103 | && typeof value === 'object' |
| 5104 | && !helpers.isArray(value)) { |
| 5105 | // If we are overwriting an object with an object, do a merge of the properties. |
| 5106 | base[key] = helpers.configMerge(baseVal, value); |
| 5107 | } else { |
| 5108 | // can just overwrite the value in this case |
| 5109 | base[key] = value; |
| 5110 | } |
| 5111 | }); |
| 5112 | }); |
| 5113 | |
| 5114 | return base; |
| 5115 | }; |
| 5116 | helpers.scaleMerge = function(_base, extension) { |
| 5117 | var base = helpers.clone(_base); |
| 5118 | |
| 5119 | helpers.each(extension, function(value, key) { |
| 5120 | if (key === 'xAxes' || key === 'yAxes') { |
| 5121 | // These properties are arrays of items |
| 5122 | if (base.hasOwnProperty(key)) { |
| 5123 | helpers.each(value, function(valueObj, index) { |
| 5124 | var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); |
| 5125 | var axisDefaults = Chart.scaleService.getScaleDefaults(axisType); |
| 5126 | if (index >= base[key].length || !base[key][index].type) { |
| 5127 | base[key].push(helpers.configMerge(axisDefaults, valueObj)); |
| 5128 | } else if (valueObj.type && valueObj.type !== base[key][index].type) { |
| 5129 | // Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults |
| 5130 | base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj); |
| 5131 | } else { |
| 5132 | // Type is the same |
| 5133 | base[key][index] = helpers.configMerge(base[key][index], valueObj); |
| 5134 | } |
| 5135 | }); |
| 5136 | } else { |
| 5137 | base[key] = []; |
| 5138 | helpers.each(value, function(valueObj) { |
| 5139 | var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear'); |
| 5140 | base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj)); |
| 5141 | }); |
| 5142 | } |
| 5143 | } else if (base.hasOwnProperty(key) && typeof base[key] === 'object' && base[key] !== null && typeof value === 'object') { |
| 5144 | // If we are overwriting an object with an object, do a merge of the properties. |
| 5145 | base[key] = helpers.configMerge(base[key], value); |
| 5146 | |
| 5147 | } else { |
| 5148 | // can just overwrite the value in this case |
| 5149 | base[key] = value; |
| 5150 | } |
| 5151 | }); |
| 5152 | |
| 5153 | return base; |
| 5154 | }; |
| 5155 | helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) { |
| 5156 | if (value === undefined || value === null) { |
| 5157 | return defaultValue; |
| 5158 | } |
| 5159 | |
| 5160 | if (helpers.isArray(value)) { |
| 5161 | return index < value.length ? value[index] : defaultValue; |
| 5162 | } |
| 5163 | |
| 5164 | return value; |
| 5165 | }; |
| 5166 | helpers.getValueOrDefault = function(value, defaultValue) { |
| 5167 | return value === undefined ? defaultValue : value; |
| 5168 | }; |
| 5169 | helpers.indexOf = Array.prototype.indexOf? |
| 5170 | function(array, item) { |
| 5171 | return array.indexOf(item); |
| 5172 | }: |
| 5173 | function(array, item) { |
| 5174 | for (var i = 0, ilen = array.length; i < ilen; ++i) { |
| 5175 | if (array[i] === item) { |
| 5176 | return i; |
| 5177 | } |
| 5178 | } |
| 5179 | return -1; |
| 5180 | }; |
| 5181 | helpers.where = function(collection, filterCallback) { |
| 5182 | if (helpers.isArray(collection) && Array.prototype.filter) { |
| 5183 | return collection.filter(filterCallback); |
| 5184 | } |
| 5185 | var filtered = []; |
| 5186 | |
| 5187 | helpers.each(collection, function(item) { |
| 5188 | if (filterCallback(item)) { |
| 5189 | filtered.push(item); |
| 5190 | } |
| 5191 | }); |
| 5192 | |
| 5193 | return filtered; |
| 5194 | }; |
| 5195 | helpers.findIndex = Array.prototype.findIndex? |
| 5196 | function(array, callback, scope) { |
| 5197 | return array.findIndex(callback, scope); |
| 5198 | } : |
| 5199 | function(array, callback, scope) { |
| 5200 | scope = scope === undefined? array : scope; |
| 5201 | for (var i = 0, ilen = array.length; i < ilen; ++i) { |
| 5202 | if (callback.call(scope, array[i], i, array)) { |
| 5203 | return i; |
| 5204 | } |
| 5205 | } |
| 5206 | return -1; |
| 5207 | }; |
| 5208 | helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { |
| 5209 | // Default to start of the array |
| 5210 | if (startIndex === undefined || startIndex === null) { |
| 5211 | startIndex = -1; |
| 5212 | } |
| 5213 | for (var i = startIndex + 1; i < arrayToSearch.length; i++) { |
| 5214 | var currentItem = arrayToSearch[i]; |
| 5215 | if (filterCallback(currentItem)) { |
| 5216 | return currentItem; |
| 5217 | } |
| 5218 | } |
| 5219 | }; |
| 5220 | helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { |
| 5221 | // Default to end of the array |
| 5222 | if (startIndex === undefined || startIndex === null) { |
| 5223 | startIndex = arrayToSearch.length; |
| 5224 | } |
| 5225 | for (var i = startIndex - 1; i >= 0; i--) { |
| 5226 | var currentItem = arrayToSearch[i]; |
| 5227 | if (filterCallback(currentItem)) { |
| 5228 | return currentItem; |
| 5229 | } |
| 5230 | } |
| 5231 | }; |
| 5232 | helpers.inherits = function(extensions) { |
| 5233 | // Basic javascript inheritance based on the model created in Backbone.js |
| 5234 | var me = this; |
| 5235 | var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { |
| 5236 | return me.apply(this, arguments); |
| 5237 | }; |
| 5238 | |
| 5239 | var Surrogate = function() { |
| 5240 | this.constructor = ChartElement; |
| 5241 | }; |
| 5242 | Surrogate.prototype = me.prototype; |
| 5243 | ChartElement.prototype = new Surrogate(); |
| 5244 | |
| 5245 | ChartElement.extend = helpers.inherits; |
| 5246 | |
| 5247 | if (extensions) { |
| 5248 | helpers.extend(ChartElement.prototype, extensions); |
| 5249 | } |
| 5250 | |
| 5251 | ChartElement.__super__ = me.prototype; |
| 5252 | |
| 5253 | return ChartElement; |
| 5254 | }; |
| 5255 | helpers.noop = function() {}; |
| 5256 | helpers.uid = (function() { |
| 5257 | var id = 0; |
| 5258 | return function() { |
| 5259 | return id++; |
| 5260 | }; |
| 5261 | }()); |
| 5262 | // -- Math methods |
| 5263 | helpers.isNumber = function(n) { |
| 5264 | return !isNaN(parseFloat(n)) && isFinite(n); |
| 5265 | }; |
| 5266 | helpers.almostEquals = function(x, y, epsilon) { |
| 5267 | return Math.abs(x - y) < epsilon; |
| 5268 | }; |
| 5269 | helpers.almostWhole = function(x, epsilon) { |
| 5270 | var rounded = Math.round(x); |
| 5271 | return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); |
| 5272 | }; |
| 5273 | helpers.max = function(array) { |
| 5274 | return array.reduce(function(max, value) { |
| 5275 | if (!isNaN(value)) { |
| 5276 | return Math.max(max, value); |
| 5277 | } |
| 5278 | return max; |
| 5279 | }, Number.NEGATIVE_INFINITY); |
| 5280 | }; |
| 5281 | helpers.min = function(array) { |
| 5282 | return array.reduce(function(min, value) { |
| 5283 | if (!isNaN(value)) { |
| 5284 | return Math.min(min, value); |
| 5285 | } |
| 5286 | return min; |
| 5287 | }, Number.POSITIVE_INFINITY); |
| 5288 | }; |
| 5289 | helpers.sign = Math.sign? |
| 5290 | function(x) { |
| 5291 | return Math.sign(x); |
| 5292 | } : |
| 5293 | function(x) { |
| 5294 | x = +x; // convert to a number |
| 5295 | if (x === 0 || isNaN(x)) { |
| 5296 | return x; |
| 5297 | } |
| 5298 | return x > 0 ? 1 : -1; |
| 5299 | }; |
| 5300 | helpers.log10 = Math.log10? |
| 5301 | function(x) { |
| 5302 | return Math.log10(x); |
| 5303 | } : |
| 5304 | function(x) { |
| 5305 | return Math.log(x) / Math.LN10; |
| 5306 | }; |
| 5307 | helpers.toRadians = function(degrees) { |
| 5308 | return degrees * (Math.PI / 180); |
| 5309 | }; |
| 5310 | helpers.toDegrees = function(radians) { |
| 5311 | return radians * (180 / Math.PI); |
| 5312 | }; |
| 5313 | // Gets the angle from vertical upright to the point about a centre. |
| 5314 | helpers.getAngleFromPoint = function(centrePoint, anglePoint) { |
| 5315 | var distanceFromXCenter = anglePoint.x - centrePoint.x, |
| 5316 | distanceFromYCenter = anglePoint.y - centrePoint.y, |
| 5317 | radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); |
| 5318 | |
| 5319 | var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); |
| 5320 | |
| 5321 | if (angle < (-0.5 * Math.PI)) { |
| 5322 | angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] |
| 5323 | } |
| 5324 | |
| 5325 | return { |
| 5326 | angle: angle, |
| 5327 | distance: radialDistanceFromCenter |
| 5328 | }; |
| 5329 | }; |
| 5330 | helpers.distanceBetweenPoints = function(pt1, pt2) { |
| 5331 | return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); |
| 5332 | }; |
| 5333 | helpers.aliasPixel = function(pixelWidth) { |
| 5334 | return (pixelWidth % 2 === 0) ? 0 : 0.5; |
| 5335 | }; |
| 5336 | helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { |
| 5337 | // Props to Rob Spencer at scaled innovation for his post on splining between points |
| 5338 | // http://scaledinnovation.com/analytics/splines/aboutSplines.html |
| 5339 | |
| 5340 | // This function must also respect "skipped" points |
| 5341 | |
| 5342 | var previous = firstPoint.skip ? middlePoint : firstPoint, |
| 5343 | current = middlePoint, |
| 5344 | next = afterPoint.skip ? middlePoint : afterPoint; |
| 5345 | |
| 5346 | var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); |
| 5347 | var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); |
| 5348 | |
| 5349 | var s01 = d01 / (d01 + d12); |
| 5350 | var s12 = d12 / (d01 + d12); |
| 5351 | |
| 5352 | // If all points are the same, s01 & s02 will be inf |
| 5353 | s01 = isNaN(s01) ? 0 : s01; |
| 5354 | s12 = isNaN(s12) ? 0 : s12; |
| 5355 | |
| 5356 | var fa = t * s01; // scaling factor for triangle Ta |
| 5357 | var fb = t * s12; |
| 5358 | |
| 5359 | return { |
| 5360 | previous: { |
| 5361 | x: current.x - fa * (next.x - previous.x), |
| 5362 | y: current.y - fa * (next.y - previous.y) |
| 5363 | }, |
| 5364 | next: { |
| 5365 | x: current.x + fb * (next.x - previous.x), |
| 5366 | y: current.y + fb * (next.y - previous.y) |
| 5367 | } |
| 5368 | }; |
| 5369 | }; |
| 5370 | helpers.EPSILON = Number.EPSILON || 1e-14; |
| 5371 | helpers.splineCurveMonotone = function(points) { |
| 5372 | // This function calculates Bézier control points in a similar way than |splineCurve|, |
| 5373 | // but preserves monotonicity of the provided data and ensures no local extremums are added |
| 5374 | // between the dataset discrete points due to the interpolation. |
| 5375 | // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation |
| 5376 | |
| 5377 | var pointsWithTangents = (points || []).map(function(point) { |
| 5378 | return { |
| 5379 | model: point._model, |
| 5380 | deltaK: 0, |
| 5381 | mK: 0 |
| 5382 | }; |
| 5383 | }); |
| 5384 | |
| 5385 | // Calculate slopes (deltaK) and initialize tangents (mK) |
| 5386 | var pointsLen = pointsWithTangents.length; |
| 5387 | var i, pointBefore, pointCurrent, pointAfter; |
| 5388 | for (i = 0; i < pointsLen; ++i) { |
| 5389 | pointCurrent = pointsWithTangents[i]; |
| 5390 | if (pointCurrent.model.skip) { |
| 5391 | continue; |
| 5392 | } |
| 5393 | |
| 5394 | pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; |
| 5395 | pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; |
| 5396 | if (pointAfter && !pointAfter.model.skip) { |
| 5397 | var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); |
| 5398 | |
| 5399 | // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 |
| 5400 | pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; |
| 5401 | } |
| 5402 | |
| 5403 | if (!pointBefore || pointBefore.model.skip) { |
| 5404 | pointCurrent.mK = pointCurrent.deltaK; |
| 5405 | } else if (!pointAfter || pointAfter.model.skip) { |
| 5406 | pointCurrent.mK = pointBefore.deltaK; |
| 5407 | } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { |
| 5408 | pointCurrent.mK = 0; |
| 5409 | } else { |
| 5410 | pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; |
| 5411 | } |
| 5412 | } |
| 5413 | |
| 5414 | // Adjust tangents to ensure monotonic properties |
| 5415 | var alphaK, betaK, tauK, squaredMagnitude; |
| 5416 | for (i = 0; i < pointsLen - 1; ++i) { |
| 5417 | pointCurrent = pointsWithTangents[i]; |
| 5418 | pointAfter = pointsWithTangents[i + 1]; |
| 5419 | if (pointCurrent.model.skip || pointAfter.model.skip) { |
| 5420 | continue; |
| 5421 | } |
| 5422 | |
| 5423 | if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { |
| 5424 | pointCurrent.mK = pointAfter.mK = 0; |
| 5425 | continue; |
| 5426 | } |
| 5427 | |
| 5428 | alphaK = pointCurrent.mK / pointCurrent.deltaK; |
| 5429 | betaK = pointAfter.mK / pointCurrent.deltaK; |
| 5430 | squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); |
| 5431 | if (squaredMagnitude <= 9) { |
| 5432 | continue; |
| 5433 | } |
| 5434 | |
| 5435 | tauK = 3 / Math.sqrt(squaredMagnitude); |
| 5436 | pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; |
| 5437 | pointAfter.mK = betaK * tauK * pointCurrent.deltaK; |
| 5438 | } |
| 5439 | |
| 5440 | // Compute control points |
| 5441 | var deltaX; |
| 5442 | for (i = 0; i < pointsLen; ++i) { |
| 5443 | pointCurrent = pointsWithTangents[i]; |
| 5444 | if (pointCurrent.model.skip) { |
| 5445 | continue; |
| 5446 | } |
| 5447 | |
| 5448 | pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; |
| 5449 | pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; |
| 5450 | if (pointBefore && !pointBefore.model.skip) { |
| 5451 | deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; |
| 5452 | pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; |
| 5453 | pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; |
| 5454 | } |
| 5455 | if (pointAfter && !pointAfter.model.skip) { |
| 5456 | deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; |
| 5457 | pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; |
| 5458 | pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; |
| 5459 | } |
| 5460 | } |
| 5461 | }; |
| 5462 | helpers.nextItem = function(collection, index, loop) { |
| 5463 | if (loop) { |
| 5464 | return index >= collection.length - 1 ? collection[0] : collection[index + 1]; |
| 5465 | } |
| 5466 | return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; |
| 5467 | }; |
| 5468 | helpers.previousItem = function(collection, index, loop) { |
| 5469 | if (loop) { |
| 5470 | return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; |
| 5471 | } |
| 5472 | return index <= 0 ? collection[0] : collection[index - 1]; |
| 5473 | }; |
| 5474 | // Implementation of the nice number algorithm used in determining where axis labels will go |
| 5475 | helpers.niceNum = function(range, round) { |
| 5476 | var exponent = Math.floor(helpers.log10(range)); |
| 5477 | var fraction = range / Math.pow(10, exponent); |
| 5478 | var niceFraction; |
| 5479 | |
| 5480 | if (round) { |
| 5481 | if (fraction < 1.5) { |
| 5482 | niceFraction = 1; |
| 5483 | } else if (fraction < 3) { |
| 5484 | niceFraction = 2; |
| 5485 | } else if (fraction < 7) { |
| 5486 | niceFraction = 5; |
| 5487 | } else { |
| 5488 | niceFraction = 10; |
| 5489 | } |
| 5490 | } else if (fraction <= 1.0) { |
| 5491 | niceFraction = 1; |
| 5492 | } else if (fraction <= 2) { |
| 5493 | niceFraction = 2; |
| 5494 | } else if (fraction <= 5) { |
| 5495 | niceFraction = 5; |
| 5496 | } else { |
| 5497 | niceFraction = 10; |
| 5498 | } |
| 5499 | |
| 5500 | return niceFraction * Math.pow(10, exponent); |
| 5501 | }; |
| 5502 | // Easing functions adapted from Robert Penner's easing equations |
| 5503 | // http://www.robertpenner.com/easing/ |
| 5504 | var easingEffects = helpers.easingEffects = { |
| 5505 | linear: function(t) { |
| 5506 | return t; |
| 5507 | }, |
| 5508 | easeInQuad: function(t) { |
| 5509 | return t * t; |
| 5510 | }, |
| 5511 | easeOutQuad: function(t) { |
| 5512 | return -1 * t * (t - 2); |
| 5513 | }, |
| 5514 | easeInOutQuad: function(t) { |
| 5515 | if ((t /= 1 / 2) < 1) { |
| 5516 | return 1 / 2 * t * t; |
| 5517 | } |
| 5518 | return -1 / 2 * ((--t) * (t - 2) - 1); |
| 5519 | }, |
| 5520 | easeInCubic: function(t) { |
| 5521 | return t * t * t; |
| 5522 | }, |
| 5523 | easeOutCubic: function(t) { |
| 5524 | return 1 * ((t = t / 1 - 1) * t * t + 1); |
| 5525 | }, |
| 5526 | easeInOutCubic: function(t) { |
| 5527 | if ((t /= 1 / 2) < 1) { |
| 5528 | return 1 / 2 * t * t * t; |
| 5529 | } |
| 5530 | return 1 / 2 * ((t -= 2) * t * t + 2); |
| 5531 | }, |
| 5532 | easeInQuart: function(t) { |
| 5533 | return t * t * t * t; |
| 5534 | }, |
| 5535 | easeOutQuart: function(t) { |
| 5536 | return -1 * ((t = t / 1 - 1) * t * t * t - 1); |
| 5537 | }, |
| 5538 | easeInOutQuart: function(t) { |
| 5539 | if ((t /= 1 / 2) < 1) { |
| 5540 | return 1 / 2 * t * t * t * t; |
| 5541 | } |
| 5542 | return -1 / 2 * ((t -= 2) * t * t * t - 2); |
| 5543 | }, |
| 5544 | easeInQuint: function(t) { |
| 5545 | return 1 * (t /= 1) * t * t * t * t; |
| 5546 | }, |
| 5547 | easeOutQuint: function(t) { |
| 5548 | return 1 * ((t = t / 1 - 1) * t * t * t * t + 1); |
| 5549 | }, |
| 5550 | easeInOutQuint: function(t) { |
| 5551 | if ((t /= 1 / 2) < 1) { |
| 5552 | return 1 / 2 * t * t * t * t * t; |
| 5553 | } |
| 5554 | return 1 / 2 * ((t -= 2) * t * t * t * t + 2); |
| 5555 | }, |
| 5556 | easeInSine: function(t) { |
| 5557 | return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1; |
| 5558 | }, |
| 5559 | easeOutSine: function(t) { |
| 5560 | return 1 * Math.sin(t / 1 * (Math.PI / 2)); |
| 5561 | }, |
| 5562 | easeInOutSine: function(t) { |
| 5563 | return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1); |
| 5564 | }, |
| 5565 | easeInExpo: function(t) { |
| 5566 | return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1)); |
| 5567 | }, |
| 5568 | easeOutExpo: function(t) { |
| 5569 | return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1); |
| 5570 | }, |
| 5571 | easeInOutExpo: function(t) { |
| 5572 | if (t === 0) { |
| 5573 | return 0; |
| 5574 | } |
| 5575 | if (t === 1) { |
| 5576 | return 1; |
| 5577 | } |
| 5578 | if ((t /= 1 / 2) < 1) { |
| 5579 | return 1 / 2 * Math.pow(2, 10 * (t - 1)); |
| 5580 | } |
| 5581 | return 1 / 2 * (-Math.pow(2, -10 * --t) + 2); |
| 5582 | }, |
| 5583 | easeInCirc: function(t) { |
| 5584 | if (t >= 1) { |
| 5585 | return t; |
| 5586 | } |
| 5587 | return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1); |
| 5588 | }, |
| 5589 | easeOutCirc: function(t) { |
| 5590 | return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t); |
| 5591 | }, |
| 5592 | easeInOutCirc: function(t) { |
| 5593 | if ((t /= 1 / 2) < 1) { |
| 5594 | return -1 / 2 * (Math.sqrt(1 - t * t) - 1); |
| 5595 | } |
| 5596 | return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1); |
| 5597 | }, |
| 5598 | easeInElastic: function(t) { |
| 5599 | var s = 1.70158; |
| 5600 | var p = 0; |
| 5601 | var a = 1; |
| 5602 | if (t === 0) { |
| 5603 | return 0; |
| 5604 | } |
| 5605 | if ((t /= 1) === 1) { |
| 5606 | return 1; |
| 5607 | } |
| 5608 | if (!p) { |
| 5609 | p = 1 * 0.3; |
| 5610 | } |
| 5611 | if (a < Math.abs(1)) { |
| 5612 | a = 1; |
| 5613 | s = p / 4; |
| 5614 | } else { |
| 5615 | s = p / (2 * Math.PI) * Math.asin(1 / a); |
| 5616 | } |
| 5617 | return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); |
| 5618 | }, |
| 5619 | easeOutElastic: function(t) { |
| 5620 | var s = 1.70158; |
| 5621 | var p = 0; |
| 5622 | var a = 1; |
| 5623 | if (t === 0) { |
| 5624 | return 0; |
| 5625 | } |
| 5626 | if ((t /= 1) === 1) { |
| 5627 | return 1; |
| 5628 | } |
| 5629 | if (!p) { |
| 5630 | p = 1 * 0.3; |
| 5631 | } |
| 5632 | if (a < Math.abs(1)) { |
| 5633 | a = 1; |
| 5634 | s = p / 4; |
| 5635 | } else { |
| 5636 | s = p / (2 * Math.PI) * Math.asin(1 / a); |
| 5637 | } |
| 5638 | return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1; |
| 5639 | }, |
| 5640 | easeInOutElastic: function(t) { |
| 5641 | var s = 1.70158; |
| 5642 | var p = 0; |
| 5643 | var a = 1; |
| 5644 | if (t === 0) { |
| 5645 | return 0; |
| 5646 | } |
| 5647 | if ((t /= 1 / 2) === 2) { |
| 5648 | return 1; |
| 5649 | } |
| 5650 | if (!p) { |
| 5651 | p = 1 * (0.3 * 1.5); |
| 5652 | } |
| 5653 | if (a < Math.abs(1)) { |
| 5654 | a = 1; |
| 5655 | s = p / 4; |
| 5656 | } else { |
| 5657 | s = p / (2 * Math.PI) * Math.asin(1 / a); |
| 5658 | } |
| 5659 | if (t < 1) { |
| 5660 | return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p)); |
| 5661 | } |
| 5662 | return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1; |
| 5663 | }, |
| 5664 | easeInBack: function(t) { |
| 5665 | var s = 1.70158; |
| 5666 | return 1 * (t /= 1) * t * ((s + 1) * t - s); |
| 5667 | }, |
| 5668 | easeOutBack: function(t) { |
| 5669 | var s = 1.70158; |
| 5670 | return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1); |
| 5671 | }, |
| 5672 | easeInOutBack: function(t) { |
| 5673 | var s = 1.70158; |
| 5674 | if ((t /= 1 / 2) < 1) { |
| 5675 | return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)); |
| 5676 | } |
| 5677 | return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); |
| 5678 | }, |
| 5679 | easeInBounce: function(t) { |
| 5680 | return 1 - easingEffects.easeOutBounce(1 - t); |
| 5681 | }, |
| 5682 | easeOutBounce: function(t) { |
| 5683 | if ((t /= 1) < (1 / 2.75)) { |
| 5684 | return 1 * (7.5625 * t * t); |
| 5685 | } else if (t < (2 / 2.75)) { |
| 5686 | return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75); |
| 5687 | } else if (t < (2.5 / 2.75)) { |
| 5688 | return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375); |
| 5689 | } |
| 5690 | return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375); |
| 5691 | }, |
| 5692 | easeInOutBounce: function(t) { |
| 5693 | if (t < 1 / 2) { |
| 5694 | return easingEffects.easeInBounce(t * 2) * 0.5; |
| 5695 | } |
| 5696 | return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5; |
| 5697 | } |
| 5698 | }; |
| 5699 | // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ |
| 5700 | helpers.requestAnimFrame = (function() { |
| 5701 | if (typeof window === 'undefined') { |
| 5702 | return function(callback) { |
| 5703 | callback(); |
| 5704 | }; |
| 5705 | } |
| 5706 | return window.requestAnimationFrame || |
| 5707 | window.webkitRequestAnimationFrame || |
| 5708 | window.mozRequestAnimationFrame || |
| 5709 | window.oRequestAnimationFrame || |
| 5710 | window.msRequestAnimationFrame || |
| 5711 | function(callback) { |
| 5712 | return window.setTimeout(callback, 1000 / 60); |
| 5713 | }; |
| 5714 | }()); |
| 5715 | // -- DOM methods |
| 5716 | helpers.getRelativePosition = function(evt, chart) { |
| 5717 | var mouseX, mouseY; |
| 5718 | var e = evt.originalEvent || evt, |
| 5719 | canvas = evt.currentTarget || evt.srcElement, |
| 5720 | boundingRect = canvas.getBoundingClientRect(); |
| 5721 | |
| 5722 | var touches = e.touches; |
| 5723 | if (touches && touches.length > 0) { |
| 5724 | mouseX = touches[0].clientX; |
| 5725 | mouseY = touches[0].clientY; |
| 5726 | |
| 5727 | } else { |
| 5728 | mouseX = e.clientX; |
| 5729 | mouseY = e.clientY; |
| 5730 | } |
| 5731 | |
| 5732 | // Scale mouse coordinates into canvas coordinates |
| 5733 | // by following the pattern laid out by 'jerryj' in the comments of |
| 5734 | // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ |
| 5735 | var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); |
| 5736 | var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); |
| 5737 | var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); |
| 5738 | var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); |
| 5739 | var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; |
| 5740 | var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; |
| 5741 | |
| 5742 | // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However |
| 5743 | // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here |
| 5744 | mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); |
| 5745 | mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); |
| 5746 | |
| 5747 | return { |
| 5748 | x: mouseX, |
| 5749 | y: mouseY |
| 5750 | }; |
| 5751 | |
| 5752 | }; |
| 5753 | helpers.addEvent = function(node, eventType, method) { |
| 5754 | if (node.addEventListener) { |
| 5755 | node.addEventListener(eventType, method); |
| 5756 | } else if (node.attachEvent) { |
| 5757 | node.attachEvent('on' + eventType, method); |
| 5758 | } else { |
| 5759 | node['on' + eventType] = method; |
| 5760 | } |
| 5761 | }; |
| 5762 | helpers.removeEvent = function(node, eventType, handler) { |
| 5763 | if (node.removeEventListener) { |
| 5764 | node.removeEventListener(eventType, handler, false); |
| 5765 | } else if (node.detachEvent) { |
| 5766 | node.detachEvent('on' + eventType, handler); |
| 5767 | } else { |
| 5768 | node['on' + eventType] = helpers.noop; |
| 5769 | } |
| 5770 | }; |
| 5771 | |
| 5772 | // Private helper function to convert max-width/max-height values that may be percentages into a number |
| 5773 | function parseMaxStyle(styleValue, node, parentProperty) { |
| 5774 | var valueInPixels; |
| 5775 | if (typeof(styleValue) === 'string') { |
| 5776 | valueInPixels = parseInt(styleValue, 10); |
| 5777 | |
| 5778 | if (styleValue.indexOf('%') !== -1) { |
| 5779 | // percentage * size in dimension |
| 5780 | valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; |
| 5781 | } |
| 5782 | } else { |
| 5783 | valueInPixels = styleValue; |
| 5784 | } |
| 5785 | |
| 5786 | return valueInPixels; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5787 | } |
| 5788 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5789 | /** |
| 5790 | * Returns if the given value contains an effective constraint. |
| 5791 | * @private |
| 5792 | */ |
| 5793 | function isConstrainedValue(value) { |
| 5794 | return value !== undefined && value !== null && value !== 'none'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5795 | } |
| 5796 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5797 | // Private helper to get a constraint dimension |
| 5798 | // @param domNode : the node to check the constraint on |
| 5799 | // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) |
| 5800 | // @param percentageProperty : property of parent to use when calculating width as a percentage |
| 5801 | // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser |
| 5802 | function getConstraintDimension(domNode, maxStyle, percentageProperty) { |
| 5803 | var view = document.defaultView; |
| 5804 | var parentNode = domNode.parentNode; |
| 5805 | var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; |
| 5806 | var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; |
| 5807 | var hasCNode = isConstrainedValue(constrainedNode); |
| 5808 | var hasCContainer = isConstrainedValue(constrainedContainer); |
| 5809 | var infinity = Number.POSITIVE_INFINITY; |
| 5810 | |
| 5811 | if (hasCNode || hasCContainer) { |
| 5812 | return Math.min( |
| 5813 | hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, |
| 5814 | hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); |
| 5815 | } |
| 5816 | |
| 5817 | return 'none'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5818 | } |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5819 | // returns Number or undefined if no constraint |
| 5820 | helpers.getConstraintWidth = function(domNode) { |
| 5821 | return getConstraintDimension(domNode, 'max-width', 'clientWidth'); |
| 5822 | }; |
| 5823 | // returns Number or undefined if no constraint |
| 5824 | helpers.getConstraintHeight = function(domNode) { |
| 5825 | return getConstraintDimension(domNode, 'max-height', 'clientHeight'); |
| 5826 | }; |
| 5827 | helpers.getMaximumWidth = function(domNode) { |
| 5828 | var container = domNode.parentNode; |
| 5829 | var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); |
| 5830 | var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); |
| 5831 | var w = container.clientWidth - paddingLeft - paddingRight; |
| 5832 | var cw = helpers.getConstraintWidth(domNode); |
| 5833 | return isNaN(cw)? w : Math.min(w, cw); |
| 5834 | }; |
| 5835 | helpers.getMaximumHeight = function(domNode) { |
| 5836 | var container = domNode.parentNode; |
| 5837 | var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); |
| 5838 | var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); |
| 5839 | var h = container.clientHeight - paddingTop - paddingBottom; |
| 5840 | var ch = helpers.getConstraintHeight(domNode); |
| 5841 | return isNaN(ch)? h : Math.min(h, ch); |
| 5842 | }; |
| 5843 | helpers.getStyle = function(el, property) { |
| 5844 | return el.currentStyle ? |
| 5845 | el.currentStyle[property] : |
| 5846 | document.defaultView.getComputedStyle(el, null).getPropertyValue(property); |
| 5847 | }; |
| 5848 | helpers.retinaScale = function(chart) { |
| 5849 | var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1; |
| 5850 | if (pixelRatio === 1) { |
| 5851 | return; |
| 5852 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5853 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5854 | var canvas = chart.canvas; |
| 5855 | var height = chart.height; |
| 5856 | var width = chart.width; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5857 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5858 | canvas.height = height * pixelRatio; |
| 5859 | canvas.width = width * pixelRatio; |
| 5860 | chart.ctx.scale(pixelRatio, pixelRatio); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5861 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5862 | // If no style has been set on the canvas, the render size is used as display size, |
| 5863 | // making the chart visually bigger, so let's enforce it to the "correct" values. |
| 5864 | // See https://github.com/chartjs/Chart.js/issues/3575 |
| 5865 | canvas.style.height = height + 'px'; |
| 5866 | canvas.style.width = width + 'px'; |
| 5867 | }; |
| 5868 | // -- Canvas methods |
| 5869 | helpers.clear = function(chart) { |
| 5870 | chart.ctx.clearRect(0, 0, chart.width, chart.height); |
| 5871 | }; |
| 5872 | helpers.fontString = function(pixelSize, fontStyle, fontFamily) { |
| 5873 | return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; |
| 5874 | }; |
| 5875 | helpers.longestText = function(ctx, font, arrayOfThings, cache) { |
| 5876 | cache = cache || {}; |
| 5877 | var data = cache.data = cache.data || {}; |
| 5878 | var gc = cache.garbageCollect = cache.garbageCollect || []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5879 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5880 | if (cache.font !== font) { |
| 5881 | data = cache.data = {}; |
| 5882 | gc = cache.garbageCollect = []; |
| 5883 | cache.font = font; |
| 5884 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5885 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5886 | ctx.font = font; |
| 5887 | var longest = 0; |
| 5888 | helpers.each(arrayOfThings, function(thing) { |
| 5889 | // Undefined strings and arrays should not be measured |
| 5890 | if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { |
| 5891 | longest = helpers.measureText(ctx, data, gc, longest, thing); |
| 5892 | } else if (helpers.isArray(thing)) { |
| 5893 | // if it is an array lets measure each element |
| 5894 | // to do maybe simplify this function a bit so we can do this more recursively? |
| 5895 | helpers.each(thing, function(nestedThing) { |
| 5896 | // Undefined strings and arrays should not be measured |
| 5897 | if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { |
| 5898 | longest = helpers.measureText(ctx, data, gc, longest, nestedThing); |
| 5899 | } |
| 5900 | }); |
| 5901 | } |
| 5902 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5903 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5904 | var gcLen = gc.length / 2; |
| 5905 | if (gcLen > arrayOfThings.length) { |
| 5906 | for (var i = 0; i < gcLen; i++) { |
| 5907 | delete data[gc[i]]; |
| 5908 | } |
| 5909 | gc.splice(0, gcLen); |
| 5910 | } |
| 5911 | return longest; |
| 5912 | }; |
| 5913 | helpers.measureText = function(ctx, data, gc, longest, string) { |
| 5914 | var textWidth = data[string]; |
| 5915 | if (!textWidth) { |
| 5916 | textWidth = data[string] = ctx.measureText(string).width; |
| 5917 | gc.push(string); |
| 5918 | } |
| 5919 | if (textWidth > longest) { |
| 5920 | longest = textWidth; |
| 5921 | } |
| 5922 | return longest; |
| 5923 | }; |
| 5924 | helpers.numberOfLabelLines = function(arrayOfThings) { |
| 5925 | var numberOfLines = 1; |
| 5926 | helpers.each(arrayOfThings, function(thing) { |
| 5927 | if (helpers.isArray(thing)) { |
| 5928 | if (thing.length > numberOfLines) { |
| 5929 | numberOfLines = thing.length; |
| 5930 | } |
| 5931 | } |
| 5932 | }); |
| 5933 | return numberOfLines; |
| 5934 | }; |
| 5935 | helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) { |
| 5936 | ctx.beginPath(); |
| 5937 | ctx.moveTo(x + radius, y); |
| 5938 | ctx.lineTo(x + width - radius, y); |
| 5939 | ctx.quadraticCurveTo(x + width, y, x + width, y + radius); |
| 5940 | ctx.lineTo(x + width, y + height - radius); |
| 5941 | ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); |
| 5942 | ctx.lineTo(x + radius, y + height); |
| 5943 | ctx.quadraticCurveTo(x, y + height, x, y + height - radius); |
| 5944 | ctx.lineTo(x, y + radius); |
| 5945 | ctx.quadraticCurveTo(x, y, x + radius, y); |
| 5946 | ctx.closePath(); |
| 5947 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5948 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5949 | helpers.color = !color? |
| 5950 | function(value) { |
| 5951 | console.error('Color.js not found!'); |
| 5952 | return value; |
| 5953 | } : |
| 5954 | function(value) { |
| 5955 | /* global CanvasGradient */ |
| 5956 | if (value instanceof CanvasGradient) { |
| 5957 | value = Chart.defaults.global.defaultColor; |
| 5958 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5959 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5960 | return color(value); |
| 5961 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5962 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5963 | helpers.isArray = Array.isArray? |
| 5964 | function(obj) { |
| 5965 | return Array.isArray(obj); |
| 5966 | } : |
| 5967 | function(obj) { |
| 5968 | return Object.prototype.toString.call(obj) === '[object Array]'; |
| 5969 | }; |
| 5970 | // ! @see http://stackoverflow.com/a/14853974 |
| 5971 | helpers.arrayEquals = function(a0, a1) { |
| 5972 | var i, ilen, v0, v1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5973 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5974 | if (!a0 || !a1 || a0.length !== a1.length) { |
| 5975 | return false; |
| 5976 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5977 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5978 | for (i = 0, ilen=a0.length; i < ilen; ++i) { |
| 5979 | v0 = a0[i]; |
| 5980 | v1 = a1[i]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5981 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5982 | if (v0 instanceof Array && v1 instanceof Array) { |
| 5983 | if (!helpers.arrayEquals(v0, v1)) { |
| 5984 | return false; |
| 5985 | } |
| 5986 | } else if (v0 !== v1) { |
| 5987 | // NOTE: two different object instances will never be equal: {x:20} != {x:20} |
| 5988 | return false; |
| 5989 | } |
| 5990 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 5991 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 5992 | return true; |
| 5993 | }; |
| 5994 | helpers.callback = function(fn, args, thisArg) { |
| 5995 | if (fn && typeof fn.call === 'function') { |
| 5996 | fn.apply(thisArg, args); |
| 5997 | } |
| 5998 | }; |
| 5999 | helpers.getHoverColor = function(colorValue) { |
| 6000 | /* global CanvasPattern */ |
| 6001 | return (colorValue instanceof CanvasPattern) ? |
| 6002 | colorValue : |
| 6003 | helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); |
| 6004 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6005 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6006 | /** |
| 6007 | * Provided for backward compatibility, use Chart.helpers#callback instead. |
| 6008 | * @function Chart.helpers#callCallback |
| 6009 | * @deprecated since version 2.6.0 |
| 6010 | * @todo remove at version 3 |
| 6011 | */ |
| 6012 | helpers.callCallback = helpers.callback; |
| 6013 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6014 | |
| 6015 | },{"3":3}],27:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6016 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6017 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6018 | module.exports = function(Chart) { |
| 6019 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6020 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6021 | /** |
| 6022 | * Helper function to get relative position for an event |
| 6023 | * @param {Event|IEvent} event - The event to get the position for |
| 6024 | * @param {Chart} chart - The chart |
| 6025 | * @returns {Point} the event position |
| 6026 | */ |
| 6027 | function getRelativePosition(e, chart) { |
| 6028 | if (e.native) { |
| 6029 | return { |
| 6030 | x: e.x, |
| 6031 | y: e.y |
| 6032 | }; |
| 6033 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6034 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6035 | return helpers.getRelativePosition(e, chart); |
| 6036 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6037 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6038 | /** |
| 6039 | * Helper function to traverse all of the visible elements in the chart |
| 6040 | * @param chart {chart} the chart |
| 6041 | * @param handler {Function} the callback to execute for each visible item |
| 6042 | */ |
| 6043 | function parseVisibleItems(chart, handler) { |
| 6044 | var datasets = chart.data.datasets; |
| 6045 | var meta, i, j, ilen, jlen; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6046 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6047 | for (i = 0, ilen = datasets.length; i < ilen; ++i) { |
| 6048 | if (!chart.isDatasetVisible(i)) { |
| 6049 | continue; |
| 6050 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6051 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6052 | meta = chart.getDatasetMeta(i); |
| 6053 | for (j = 0, jlen = meta.data.length; j < jlen; ++j) { |
| 6054 | var element = meta.data[j]; |
| 6055 | if (!element._view.skip) { |
| 6056 | handler(element); |
| 6057 | } |
| 6058 | } |
| 6059 | } |
| 6060 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6061 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6062 | /** |
| 6063 | * Helper function to get the items that intersect the event position |
| 6064 | * @param items {ChartElement[]} elements to filter |
| 6065 | * @param position {Point} the point to be nearest to |
| 6066 | * @return {ChartElement[]} the nearest items |
| 6067 | */ |
| 6068 | function getIntersectItems(chart, position) { |
| 6069 | var elements = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6070 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6071 | parseVisibleItems(chart, function(element) { |
| 6072 | if (element.inRange(position.x, position.y)) { |
| 6073 | elements.push(element); |
| 6074 | } |
| 6075 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6076 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6077 | return elements; |
| 6078 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6079 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6080 | /** |
| 6081 | * Helper function to get the items nearest to the event position considering all visible items in teh chart |
| 6082 | * @param chart {Chart} the chart to look at elements from |
| 6083 | * @param position {Point} the point to be nearest to |
| 6084 | * @param intersect {Boolean} if true, only consider items that intersect the position |
| 6085 | * @param distanceMetric {Function} Optional function to provide the distance between |
| 6086 | * @return {ChartElement[]} the nearest items |
| 6087 | */ |
| 6088 | function getNearestItems(chart, position, intersect, distanceMetric) { |
| 6089 | var minDistance = Number.POSITIVE_INFINITY; |
| 6090 | var nearestItems = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6091 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6092 | if (!distanceMetric) { |
| 6093 | distanceMetric = helpers.distanceBetweenPoints; |
| 6094 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6095 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6096 | parseVisibleItems(chart, function(element) { |
| 6097 | if (intersect && !element.inRange(position.x, position.y)) { |
| 6098 | return; |
| 6099 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6100 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6101 | var center = element.getCenterPoint(); |
| 6102 | var distance = distanceMetric(position, center); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6103 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6104 | if (distance < minDistance) { |
| 6105 | nearestItems = [element]; |
| 6106 | minDistance = distance; |
| 6107 | } else if (distance === minDistance) { |
| 6108 | // Can have multiple items at the same distance in which case we sort by size |
| 6109 | nearestItems.push(element); |
| 6110 | } |
| 6111 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6112 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6113 | return nearestItems; |
| 6114 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6115 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6116 | function indexMode(chart, e, options) { |
| 6117 | var position = getRelativePosition(e, chart); |
| 6118 | var distanceMetric = function(pt1, pt2) { |
| 6119 | return Math.abs(pt1.x - pt2.x); |
| 6120 | }; |
| 6121 | var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); |
| 6122 | var elements = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6123 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6124 | if (!items.length) { |
| 6125 | return []; |
| 6126 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6127 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6128 | chart.data.datasets.forEach(function(dataset, datasetIndex) { |
| 6129 | if (chart.isDatasetVisible(datasetIndex)) { |
| 6130 | var meta = chart.getDatasetMeta(datasetIndex), |
| 6131 | element = meta.data[items[0]._index]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6132 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6133 | // don't count items that are skipped (null data) |
| 6134 | if (element && !element._view.skip) { |
| 6135 | elements.push(element); |
| 6136 | } |
| 6137 | } |
| 6138 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6139 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6140 | return elements; |
| 6141 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6142 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6143 | /** |
| 6144 | * @interface IInteractionOptions |
| 6145 | */ |
| 6146 | /** |
| 6147 | * If true, only consider items that intersect the point |
| 6148 | * @name IInterfaceOptions#boolean |
| 6149 | * @type Boolean |
| 6150 | */ |
| 6151 | |
| 6152 | /** |
| 6153 | * Contains interaction related functions |
| 6154 | * @namespace Chart.Interaction |
| 6155 | */ |
| 6156 | Chart.Interaction = { |
| 6157 | // Helper function for different modes |
| 6158 | modes: { |
| 6159 | single: function(chart, e) { |
| 6160 | var position = getRelativePosition(e, chart); |
| 6161 | var elements = []; |
| 6162 | |
| 6163 | parseVisibleItems(chart, function(element) { |
| 6164 | if (element.inRange(position.x, position.y)) { |
| 6165 | elements.push(element); |
| 6166 | return elements; |
| 6167 | } |
| 6168 | }); |
| 6169 | |
| 6170 | return elements.slice(0, 1); |
| 6171 | }, |
| 6172 | |
| 6173 | /** |
| 6174 | * @function Chart.Interaction.modes.label |
| 6175 | * @deprecated since version 2.4.0 |
| 6176 | * @todo remove at version 3 |
| 6177 | * @private |
| 6178 | */ |
| 6179 | label: indexMode, |
| 6180 | |
| 6181 | /** |
| 6182 | * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something |
| 6183 | * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item |
| 6184 | * @function Chart.Interaction.modes.index |
| 6185 | * @since v2.4.0 |
| 6186 | * @param chart {chart} the chart we are returning items from |
| 6187 | * @param e {Event} the event we are find things at |
| 6188 | * @param options {IInteractionOptions} options to use during interaction |
| 6189 | * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 6190 | */ |
| 6191 | index: indexMode, |
| 6192 | |
| 6193 | /** |
| 6194 | * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something |
| 6195 | * If the options.intersect is false, we find the nearest item and return the items in that dataset |
| 6196 | * @function Chart.Interaction.modes.dataset |
| 6197 | * @param chart {chart} the chart we are returning items from |
| 6198 | * @param e {Event} the event we are find things at |
| 6199 | * @param options {IInteractionOptions} options to use during interaction |
| 6200 | * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 6201 | */ |
| 6202 | dataset: function(chart, e, options) { |
| 6203 | var position = getRelativePosition(e, chart); |
| 6204 | var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false); |
| 6205 | |
| 6206 | if (items.length > 0) { |
| 6207 | items = chart.getDatasetMeta(items[0]._datasetIndex).data; |
| 6208 | } |
| 6209 | |
| 6210 | return items; |
| 6211 | }, |
| 6212 | |
| 6213 | /** |
| 6214 | * @function Chart.Interaction.modes.x-axis |
| 6215 | * @deprecated since version 2.4.0. Use index mode and intersect == true |
| 6216 | * @todo remove at version 3 |
| 6217 | * @private |
| 6218 | */ |
| 6219 | 'x-axis': function(chart, e) { |
| 6220 | return indexMode(chart, e, true); |
| 6221 | }, |
| 6222 | |
| 6223 | /** |
| 6224 | * Point mode returns all elements that hit test based on the event position |
| 6225 | * of the event |
| 6226 | * @function Chart.Interaction.modes.intersect |
| 6227 | * @param chart {chart} the chart we are returning items from |
| 6228 | * @param e {Event} the event we are find things at |
| 6229 | * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 6230 | */ |
| 6231 | point: function(chart, e) { |
| 6232 | var position = getRelativePosition(e, chart); |
| 6233 | return getIntersectItems(chart, position); |
| 6234 | }, |
| 6235 | |
| 6236 | /** |
| 6237 | * nearest mode returns the element closest to the point |
| 6238 | * @function Chart.Interaction.modes.intersect |
| 6239 | * @param chart {chart} the chart we are returning items from |
| 6240 | * @param e {Event} the event we are find things at |
| 6241 | * @param options {IInteractionOptions} options to use |
| 6242 | * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 6243 | */ |
| 6244 | nearest: function(chart, e, options) { |
| 6245 | var position = getRelativePosition(e, chart); |
| 6246 | var nearestItems = getNearestItems(chart, position, options.intersect); |
| 6247 | |
| 6248 | // We have multiple items at the same distance from the event. Now sort by smallest |
| 6249 | if (nearestItems.length > 1) { |
| 6250 | nearestItems.sort(function(a, b) { |
| 6251 | var sizeA = a.getArea(); |
| 6252 | var sizeB = b.getArea(); |
| 6253 | var ret = sizeA - sizeB; |
| 6254 | |
| 6255 | if (ret === 0) { |
| 6256 | // if equal sort by dataset index |
| 6257 | ret = a._datasetIndex - b._datasetIndex; |
| 6258 | } |
| 6259 | |
| 6260 | return ret; |
| 6261 | }); |
| 6262 | } |
| 6263 | |
| 6264 | // Return only 1 item |
| 6265 | return nearestItems.slice(0, 1); |
| 6266 | }, |
| 6267 | |
| 6268 | /** |
| 6269 | * x mode returns the elements that hit-test at the current x coordinate |
| 6270 | * @function Chart.Interaction.modes.x |
| 6271 | * @param chart {chart} the chart we are returning items from |
| 6272 | * @param e {Event} the event we are find things at |
| 6273 | * @param options {IInteractionOptions} options to use |
| 6274 | * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 6275 | */ |
| 6276 | x: function(chart, e, options) { |
| 6277 | var position = getRelativePosition(e, chart); |
| 6278 | var items = []; |
| 6279 | var intersectsItem = false; |
| 6280 | |
| 6281 | parseVisibleItems(chart, function(element) { |
| 6282 | if (element.inXRange(position.x)) { |
| 6283 | items.push(element); |
| 6284 | } |
| 6285 | |
| 6286 | if (element.inRange(position.x, position.y)) { |
| 6287 | intersectsItem = true; |
| 6288 | } |
| 6289 | }); |
| 6290 | |
| 6291 | // If we want to trigger on an intersect and we don't have any items |
| 6292 | // that intersect the position, return nothing |
| 6293 | if (options.intersect && !intersectsItem) { |
| 6294 | items = []; |
| 6295 | } |
| 6296 | return items; |
| 6297 | }, |
| 6298 | |
| 6299 | /** |
| 6300 | * y mode returns the elements that hit-test at the current y coordinate |
| 6301 | * @function Chart.Interaction.modes.y |
| 6302 | * @param chart {chart} the chart we are returning items from |
| 6303 | * @param e {Event} the event we are find things at |
| 6304 | * @param options {IInteractionOptions} options to use |
| 6305 | * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned |
| 6306 | */ |
| 6307 | y: function(chart, e, options) { |
| 6308 | var position = getRelativePosition(e, chart); |
| 6309 | var items = []; |
| 6310 | var intersectsItem = false; |
| 6311 | |
| 6312 | parseVisibleItems(chart, function(element) { |
| 6313 | if (element.inYRange(position.y)) { |
| 6314 | items.push(element); |
| 6315 | } |
| 6316 | |
| 6317 | if (element.inRange(position.x, position.y)) { |
| 6318 | intersectsItem = true; |
| 6319 | } |
| 6320 | }); |
| 6321 | |
| 6322 | // If we want to trigger on an intersect and we don't have any items |
| 6323 | // that intersect the position, return nothing |
| 6324 | if (options.intersect && !intersectsItem) { |
| 6325 | items = []; |
| 6326 | } |
| 6327 | return items; |
| 6328 | } |
| 6329 | } |
| 6330 | }; |
| 6331 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6332 | |
| 6333 | },{}],28:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6334 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6335 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6336 | module.exports = function() { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6337 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6338 | // Occupy the global variable of Chart, and create a simple base class |
| 6339 | var Chart = function(item, config) { |
| 6340 | this.construct(item, config); |
| 6341 | return this; |
| 6342 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6343 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6344 | // Globally expose the defaults to allow for user updating/changing |
| 6345 | Chart.defaults = { |
| 6346 | global: { |
| 6347 | responsive: true, |
| 6348 | responsiveAnimationDuration: 0, |
| 6349 | maintainAspectRatio: true, |
| 6350 | events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], |
| 6351 | hover: { |
| 6352 | onHover: null, |
| 6353 | mode: 'nearest', |
| 6354 | intersect: true, |
| 6355 | animationDuration: 400 |
| 6356 | }, |
| 6357 | onClick: null, |
| 6358 | defaultColor: 'rgba(0,0,0,0.1)', |
| 6359 | defaultFontColor: '#666', |
| 6360 | defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", |
| 6361 | defaultFontSize: 12, |
| 6362 | defaultFontStyle: 'normal', |
| 6363 | showLines: true, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6364 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6365 | // Element defaults defined in element extensions |
| 6366 | elements: {}, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6367 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6368 | // Legend callback string |
| 6369 | legendCallback: function(chart) { |
| 6370 | var text = []; |
| 6371 | text.push('<ul class="' + chart.id + '-legend">'); |
| 6372 | for (var i = 0; i < chart.data.datasets.length; i++) { |
| 6373 | text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); |
| 6374 | if (chart.data.datasets[i].label) { |
| 6375 | text.push(chart.data.datasets[i].label); |
| 6376 | } |
| 6377 | text.push('</li>'); |
| 6378 | } |
| 6379 | text.push('</ul>'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6380 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6381 | return text.join(''); |
| 6382 | } |
| 6383 | } |
| 6384 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6385 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6386 | Chart.Chart = Chart; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6387 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6388 | return Chart; |
| 6389 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6390 | |
| 6391 | },{}],29:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6392 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6393 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6394 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6395 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6396 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6397 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6398 | function filterByPosition(array, position) { |
| 6399 | return helpers.where(array, function(v) { |
| 6400 | return v.position === position; |
| 6401 | }); |
| 6402 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6403 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6404 | function sortByWeight(array, reverse) { |
| 6405 | array.forEach(function(v, i) { |
| 6406 | v._tmpIndex_ = i; |
| 6407 | return v; |
| 6408 | }); |
| 6409 | array.sort(function(a, b) { |
| 6410 | var v0 = reverse ? b : a; |
| 6411 | var v1 = reverse ? a : b; |
| 6412 | return v0.weight === v1.weight ? |
| 6413 | v0._tmpIndex_ - v1._tmpIndex_ : |
| 6414 | v0.weight - v1.weight; |
| 6415 | }); |
| 6416 | array.forEach(function(v) { |
| 6417 | delete v._tmpIndex_; |
| 6418 | }); |
| 6419 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6420 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6421 | /** |
| 6422 | * @interface ILayoutItem |
| 6423 | * @prop {String} position - The position of the item in the chart layout. Possible values are |
| 6424 | * 'left', 'top', 'right', 'bottom', and 'chartArea' |
| 6425 | * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area |
| 6426 | * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down |
| 6427 | * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) |
| 6428 | * @prop {Function} update - Takes two parameters: width and height. Returns size of item |
| 6429 | * @prop {Function} getPadding - Returns an object with padding on the edges |
| 6430 | * @prop {Number} width - Width of item. Must be valid after update() |
| 6431 | * @prop {Number} height - Height of item. Must be valid after update() |
| 6432 | * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update |
| 6433 | * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update |
| 6434 | * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update |
| 6435 | * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update |
| 6436 | */ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6437 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6438 | // The layout service is very self explanatory. It's responsible for the layout within a chart. |
| 6439 | // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need |
| 6440 | // It is this service's responsibility of carrying out that layout. |
| 6441 | Chart.layoutService = { |
| 6442 | defaults: {}, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6443 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6444 | /** |
| 6445 | * Register a box to a chart. |
| 6446 | * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. |
| 6447 | * @param {Chart} chart - the chart to use |
| 6448 | * @param {ILayoutItem} item - the item to add to be layed out |
| 6449 | */ |
| 6450 | addBox: function(chart, item) { |
| 6451 | if (!chart.boxes) { |
| 6452 | chart.boxes = []; |
| 6453 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6454 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6455 | // initialize item with default values |
| 6456 | item.fullWidth = item.fullWidth || false; |
| 6457 | item.position = item.position || 'top'; |
| 6458 | item.weight = item.weight || 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6459 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6460 | chart.boxes.push(item); |
| 6461 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6462 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6463 | /** |
| 6464 | * Remove a layoutItem from a chart |
| 6465 | * @param {Chart} chart - the chart to remove the box from |
| 6466 | * @param {Object} layoutItem - the item to remove from the layout |
| 6467 | */ |
| 6468 | removeBox: function(chart, layoutItem) { |
| 6469 | var index = chart.boxes? chart.boxes.indexOf(layoutItem) : -1; |
| 6470 | if (index !== -1) { |
| 6471 | chart.boxes.splice(index, 1); |
| 6472 | } |
| 6473 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6474 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6475 | /** |
| 6476 | * Sets (or updates) options on the given `item`. |
| 6477 | * @param {Chart} chart - the chart in which the item lives (or will be added to) |
| 6478 | * @param {Object} item - the item to configure with the given options |
| 6479 | * @param {Object} options - the new item options. |
| 6480 | */ |
| 6481 | configure: function(chart, item, options) { |
| 6482 | var props = ['fullWidth', 'position', 'weight']; |
| 6483 | var ilen = props.length; |
| 6484 | var i = 0; |
| 6485 | var prop; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6486 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6487 | for (; i<ilen; ++i) { |
| 6488 | prop = props[i]; |
| 6489 | if (options.hasOwnProperty(prop)) { |
| 6490 | item[prop] = options[prop]; |
| 6491 | } |
| 6492 | } |
| 6493 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6494 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6495 | /** |
| 6496 | * Fits boxes of the given chart into the given size by having each box measure itself |
| 6497 | * then running a fitting algorithm |
| 6498 | * @param {Chart} chart - the chart |
| 6499 | * @param {Number} width - the width to fit into |
| 6500 | * @param {Number} height - the height to fit into |
| 6501 | */ |
| 6502 | update: function(chart, width, height) { |
| 6503 | if (!chart) { |
| 6504 | return; |
| 6505 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6506 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6507 | var layoutOptions = chart.options.layout; |
| 6508 | var padding = layoutOptions ? layoutOptions.padding : null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6509 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6510 | var leftPadding = 0; |
| 6511 | var rightPadding = 0; |
| 6512 | var topPadding = 0; |
| 6513 | var bottomPadding = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6514 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6515 | if (!isNaN(padding)) { |
| 6516 | // options.layout.padding is a number. assign to all |
| 6517 | leftPadding = padding; |
| 6518 | rightPadding = padding; |
| 6519 | topPadding = padding; |
| 6520 | bottomPadding = padding; |
| 6521 | } else { |
| 6522 | leftPadding = padding.left || 0; |
| 6523 | rightPadding = padding.right || 0; |
| 6524 | topPadding = padding.top || 0; |
| 6525 | bottomPadding = padding.bottom || 0; |
| 6526 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6527 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6528 | var leftBoxes = filterByPosition(chart.boxes, 'left'); |
| 6529 | var rightBoxes = filterByPosition(chart.boxes, 'right'); |
| 6530 | var topBoxes = filterByPosition(chart.boxes, 'top'); |
| 6531 | var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); |
| 6532 | var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6533 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6534 | // Sort boxes by weight. A higher weight is further away from the chart area |
| 6535 | sortByWeight(leftBoxes, true); |
| 6536 | sortByWeight(rightBoxes, false); |
| 6537 | sortByWeight(topBoxes, true); |
| 6538 | sortByWeight(bottomBoxes, false); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6539 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6540 | // Essentially we now have any number of boxes on each of the 4 sides. |
| 6541 | // Our canvas looks like the following. |
| 6542 | // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and |
| 6543 | // B1 is the bottom axis |
| 6544 | // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays |
| 6545 | // These locations are single-box locations only, when trying to register a chartArea location that is already taken, |
| 6546 | // an error will be thrown. |
| 6547 | // |
| 6548 | // |----------------------------------------------------| |
| 6549 | // | T1 (Full Width) | |
| 6550 | // |----------------------------------------------------| |
| 6551 | // | | | T2 | | |
| 6552 | // | |----|-------------------------------------|----| |
| 6553 | // | | | C1 | | C2 | | |
| 6554 | // | | |----| |----| | |
| 6555 | // | | | | | |
| 6556 | // | L1 | L2 | ChartArea (C0) | R1 | |
| 6557 | // | | | | | |
| 6558 | // | | |----| |----| | |
| 6559 | // | | | C3 | | C4 | | |
| 6560 | // | |----|-------------------------------------|----| |
| 6561 | // | | | B1 | | |
| 6562 | // |----------------------------------------------------| |
| 6563 | // | B2 (Full Width) | |
| 6564 | // |----------------------------------------------------| |
| 6565 | // |
| 6566 | // What we do to find the best sizing, we do the following |
| 6567 | // 1. Determine the minimum size of the chart area. |
| 6568 | // 2. Split the remaining width equally between each vertical axis |
| 6569 | // 3. Split the remaining height equally between each horizontal axis |
| 6570 | // 4. Give each layout the maximum size it can be. The layout will return it's minimum size |
| 6571 | // 5. Adjust the sizes of each axis based on it's minimum reported size. |
| 6572 | // 6. Refit each axis |
| 6573 | // 7. Position each axis in the final location |
| 6574 | // 8. Tell the chart the final location of the chart area |
| 6575 | // 9. Tell any axes that overlay the chart area the positions of the chart area |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6576 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6577 | // Step 1 |
| 6578 | var chartWidth = width - leftPadding - rightPadding; |
| 6579 | var chartHeight = height - topPadding - bottomPadding; |
| 6580 | var chartAreaWidth = chartWidth / 2; // min 50% |
| 6581 | var chartAreaHeight = chartHeight / 2; // min 50% |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6582 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6583 | // Step 2 |
| 6584 | var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6585 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6586 | // Step 3 |
| 6587 | var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6588 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6589 | // Step 4 |
| 6590 | var maxChartAreaWidth = chartWidth; |
| 6591 | var maxChartAreaHeight = chartHeight; |
| 6592 | var minBoxSizes = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6593 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6594 | function getMinimumBoxSize(box) { |
| 6595 | var minSize; |
| 6596 | var isHorizontal = box.isHorizontal(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6597 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6598 | if (isHorizontal) { |
| 6599 | minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); |
| 6600 | maxChartAreaHeight -= minSize.height; |
| 6601 | } else { |
| 6602 | minSize = box.update(verticalBoxWidth, chartAreaHeight); |
| 6603 | maxChartAreaWidth -= minSize.width; |
| 6604 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6605 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6606 | minBoxSizes.push({ |
| 6607 | horizontal: isHorizontal, |
| 6608 | minSize: minSize, |
| 6609 | box: box, |
| 6610 | }); |
| 6611 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6612 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6613 | helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6614 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6615 | // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) |
| 6616 | var maxHorizontalLeftPadding = 0; |
| 6617 | var maxHorizontalRightPadding = 0; |
| 6618 | var maxVerticalTopPadding = 0; |
| 6619 | var maxVerticalBottomPadding = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6620 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6621 | helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { |
| 6622 | if (horizontalBox.getPadding) { |
| 6623 | var boxPadding = horizontalBox.getPadding(); |
| 6624 | maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); |
| 6625 | maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); |
| 6626 | } |
| 6627 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6628 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6629 | helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { |
| 6630 | if (verticalBox.getPadding) { |
| 6631 | var boxPadding = verticalBox.getPadding(); |
| 6632 | maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); |
| 6633 | maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); |
| 6634 | } |
| 6635 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6636 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6637 | // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could |
| 6638 | // be if the axes are drawn at their minimum sizes. |
| 6639 | // Steps 5 & 6 |
| 6640 | var totalLeftBoxesWidth = leftPadding; |
| 6641 | var totalRightBoxesWidth = rightPadding; |
| 6642 | var totalTopBoxesHeight = topPadding; |
| 6643 | var totalBottomBoxesHeight = bottomPadding; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6644 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6645 | // Function to fit a box |
| 6646 | function fitBox(box) { |
| 6647 | var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { |
| 6648 | return minBox.box === box; |
| 6649 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6650 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6651 | if (minBoxSize) { |
| 6652 | if (box.isHorizontal()) { |
| 6653 | var scaleMargin = { |
| 6654 | left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), |
| 6655 | right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), |
| 6656 | top: 0, |
| 6657 | bottom: 0 |
| 6658 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6659 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6660 | // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends |
| 6661 | // on the margin. Sometimes they need to increase in size slightly |
| 6662 | box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); |
| 6663 | } else { |
| 6664 | box.update(minBoxSize.minSize.width, maxChartAreaHeight); |
| 6665 | } |
| 6666 | } |
| 6667 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6668 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6669 | // Update, and calculate the left and right margins for the horizontal boxes |
| 6670 | helpers.each(leftBoxes.concat(rightBoxes), fitBox); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6671 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6672 | helpers.each(leftBoxes, function(box) { |
| 6673 | totalLeftBoxesWidth += box.width; |
| 6674 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6675 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6676 | helpers.each(rightBoxes, function(box) { |
| 6677 | totalRightBoxesWidth += box.width; |
| 6678 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6679 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6680 | // Set the Left and Right margins for the horizontal boxes |
| 6681 | helpers.each(topBoxes.concat(bottomBoxes), fitBox); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6682 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6683 | // Figure out how much margin is on the top and bottom of the vertical boxes |
| 6684 | helpers.each(topBoxes, function(box) { |
| 6685 | totalTopBoxesHeight += box.height; |
| 6686 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6687 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6688 | helpers.each(bottomBoxes, function(box) { |
| 6689 | totalBottomBoxesHeight += box.height; |
| 6690 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6691 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6692 | function finalFitVerticalBox(box) { |
| 6693 | var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { |
| 6694 | return minSize.box === box; |
| 6695 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6696 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6697 | var scaleMargin = { |
| 6698 | left: 0, |
| 6699 | right: 0, |
| 6700 | top: totalTopBoxesHeight, |
| 6701 | bottom: totalBottomBoxesHeight |
| 6702 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6703 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6704 | if (minBoxSize) { |
| 6705 | box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); |
| 6706 | } |
| 6707 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6708 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6709 | // Let the left layout know the final margin |
| 6710 | helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6711 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6712 | // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) |
| 6713 | totalLeftBoxesWidth = leftPadding; |
| 6714 | totalRightBoxesWidth = rightPadding; |
| 6715 | totalTopBoxesHeight = topPadding; |
| 6716 | totalBottomBoxesHeight = bottomPadding; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6717 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6718 | helpers.each(leftBoxes, function(box) { |
| 6719 | totalLeftBoxesWidth += box.width; |
| 6720 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6721 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6722 | helpers.each(rightBoxes, function(box) { |
| 6723 | totalRightBoxesWidth += box.width; |
| 6724 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6725 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6726 | helpers.each(topBoxes, function(box) { |
| 6727 | totalTopBoxesHeight += box.height; |
| 6728 | }); |
| 6729 | helpers.each(bottomBoxes, function(box) { |
| 6730 | totalBottomBoxesHeight += box.height; |
| 6731 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6732 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6733 | // We may be adding some padding to account for rotated x axis labels |
| 6734 | var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); |
| 6735 | totalLeftBoxesWidth += leftPaddingAddition; |
| 6736 | totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6737 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6738 | var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); |
| 6739 | totalTopBoxesHeight += topPaddingAddition; |
| 6740 | totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6741 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6742 | // Figure out if our chart area changed. This would occur if the dataset layout label rotation |
| 6743 | // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do |
| 6744 | // without calling `fit` again |
| 6745 | var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; |
| 6746 | var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6747 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6748 | if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { |
| 6749 | helpers.each(leftBoxes, function(box) { |
| 6750 | box.height = newMaxChartAreaHeight; |
| 6751 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6752 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6753 | helpers.each(rightBoxes, function(box) { |
| 6754 | box.height = newMaxChartAreaHeight; |
| 6755 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6756 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6757 | helpers.each(topBoxes, function(box) { |
| 6758 | if (!box.fullWidth) { |
| 6759 | box.width = newMaxChartAreaWidth; |
| 6760 | } |
| 6761 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6762 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6763 | helpers.each(bottomBoxes, function(box) { |
| 6764 | if (!box.fullWidth) { |
| 6765 | box.width = newMaxChartAreaWidth; |
| 6766 | } |
| 6767 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6768 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6769 | maxChartAreaHeight = newMaxChartAreaHeight; |
| 6770 | maxChartAreaWidth = newMaxChartAreaWidth; |
| 6771 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6772 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6773 | // Step 7 - Position the boxes |
| 6774 | var left = leftPadding + leftPaddingAddition; |
| 6775 | var top = topPadding + topPaddingAddition; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6776 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6777 | function placeBox(box) { |
| 6778 | if (box.isHorizontal()) { |
| 6779 | box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; |
| 6780 | box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; |
| 6781 | box.top = top; |
| 6782 | box.bottom = top + box.height; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6783 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6784 | // Move to next point |
| 6785 | top = box.bottom; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6786 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6787 | } else { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6788 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6789 | box.left = left; |
| 6790 | box.right = left + box.width; |
| 6791 | box.top = totalTopBoxesHeight; |
| 6792 | box.bottom = totalTopBoxesHeight + maxChartAreaHeight; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6793 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6794 | // Move to next point |
| 6795 | left = box.right; |
| 6796 | } |
| 6797 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6798 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6799 | helpers.each(leftBoxes.concat(topBoxes), placeBox); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6800 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6801 | // Account for chart width and height |
| 6802 | left += maxChartAreaWidth; |
| 6803 | top += maxChartAreaHeight; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6804 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6805 | helpers.each(rightBoxes, placeBox); |
| 6806 | helpers.each(bottomBoxes, placeBox); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6807 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6808 | // Step 8 |
| 6809 | chart.chartArea = { |
| 6810 | left: totalLeftBoxesWidth, |
| 6811 | top: totalTopBoxesHeight, |
| 6812 | right: totalLeftBoxesWidth + maxChartAreaWidth, |
| 6813 | bottom: totalTopBoxesHeight + maxChartAreaHeight |
| 6814 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6815 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6816 | // Step 9 |
| 6817 | helpers.each(chartAreaBoxes, function(box) { |
| 6818 | box.left = chart.chartArea.left; |
| 6819 | box.top = chart.chartArea.top; |
| 6820 | box.right = chart.chartArea.right; |
| 6821 | box.bottom = chart.chartArea.bottom; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6822 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6823 | box.update(maxChartAreaWidth, maxChartAreaHeight); |
| 6824 | }); |
| 6825 | } |
| 6826 | }; |
| 6827 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6828 | |
| 6829 | },{}],30:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6830 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6831 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6832 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6833 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6834 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6835 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6836 | Chart.defaults.global.plugins = {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6837 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6838 | /** |
| 6839 | * The plugin service singleton |
| 6840 | * @namespace Chart.plugins |
| 6841 | * @since 2.1.0 |
| 6842 | */ |
| 6843 | Chart.plugins = { |
| 6844 | /** |
| 6845 | * Globally registered plugins. |
| 6846 | * @private |
| 6847 | */ |
| 6848 | _plugins: [], |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6849 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6850 | /** |
| 6851 | * This identifier is used to invalidate the descriptors cache attached to each chart |
| 6852 | * when a global plugin is registered or unregistered. In this case, the cache ID is |
| 6853 | * incremented and descriptors are regenerated during following API calls. |
| 6854 | * @private |
| 6855 | */ |
| 6856 | _cacheId: 0, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6857 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6858 | /** |
| 6859 | * Registers the given plugin(s) if not already registered. |
| 6860 | * @param {Array|Object} plugins plugin instance(s). |
| 6861 | */ |
| 6862 | register: function(plugins) { |
| 6863 | var p = this._plugins; |
| 6864 | ([]).concat(plugins).forEach(function(plugin) { |
| 6865 | if (p.indexOf(plugin) === -1) { |
| 6866 | p.push(plugin); |
| 6867 | } |
| 6868 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6869 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6870 | this._cacheId++; |
| 6871 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6872 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6873 | /** |
| 6874 | * Unregisters the given plugin(s) only if registered. |
| 6875 | * @param {Array|Object} plugins plugin instance(s). |
| 6876 | */ |
| 6877 | unregister: function(plugins) { |
| 6878 | var p = this._plugins; |
| 6879 | ([]).concat(plugins).forEach(function(plugin) { |
| 6880 | var idx = p.indexOf(plugin); |
| 6881 | if (idx !== -1) { |
| 6882 | p.splice(idx, 1); |
| 6883 | } |
| 6884 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6885 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6886 | this._cacheId++; |
| 6887 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6888 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6889 | /** |
| 6890 | * Remove all registered plugins. |
| 6891 | * @since 2.1.5 |
| 6892 | */ |
| 6893 | clear: function() { |
| 6894 | this._plugins = []; |
| 6895 | this._cacheId++; |
| 6896 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6897 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6898 | /** |
| 6899 | * Returns the number of registered plugins? |
| 6900 | * @returns {Number} |
| 6901 | * @since 2.1.5 |
| 6902 | */ |
| 6903 | count: function() { |
| 6904 | return this._plugins.length; |
| 6905 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6906 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6907 | /** |
| 6908 | * Returns all registered plugin instances. |
| 6909 | * @returns {Array} array of plugin objects. |
| 6910 | * @since 2.1.5 |
| 6911 | */ |
| 6912 | getAll: function() { |
| 6913 | return this._plugins; |
| 6914 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6915 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6916 | /** |
| 6917 | * Calls enabled plugins for `chart` on the specified hook and with the given args. |
| 6918 | * This method immediately returns as soon as a plugin explicitly returns false. The |
| 6919 | * returned value can be used, for instance, to interrupt the current action. |
| 6920 | * @param {Object} chart - The chart instance for which plugins should be called. |
| 6921 | * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). |
| 6922 | * @param {Array} [args] - Extra arguments to apply to the hook call. |
| 6923 | * @returns {Boolean} false if any of the plugins return false, else returns true. |
| 6924 | */ |
| 6925 | notify: function(chart, hook, args) { |
| 6926 | var descriptors = this.descriptors(chart); |
| 6927 | var ilen = descriptors.length; |
| 6928 | var i, descriptor, plugin, params, method; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6929 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6930 | for (i=0; i<ilen; ++i) { |
| 6931 | descriptor = descriptors[i]; |
| 6932 | plugin = descriptor.plugin; |
| 6933 | method = plugin[hook]; |
| 6934 | if (typeof method === 'function') { |
| 6935 | params = [chart].concat(args || []); |
| 6936 | params.push(descriptor.options); |
| 6937 | if (method.apply(plugin, params) === false) { |
| 6938 | return false; |
| 6939 | } |
| 6940 | } |
| 6941 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6942 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6943 | return true; |
| 6944 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6945 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6946 | /** |
| 6947 | * Returns descriptors of enabled plugins for the given chart. |
| 6948 | * @returns {Array} [{ plugin, options }] |
| 6949 | * @private |
| 6950 | */ |
| 6951 | descriptors: function(chart) { |
| 6952 | var cache = chart._plugins || (chart._plugins = {}); |
| 6953 | if (cache.id === this._cacheId) { |
| 6954 | return cache.descriptors; |
| 6955 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6956 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6957 | var plugins = []; |
| 6958 | var descriptors = []; |
| 6959 | var config = (chart && chart.config) || {}; |
| 6960 | var defaults = Chart.defaults.global.plugins; |
| 6961 | var options = (config.options && config.options.plugins) || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6962 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6963 | this._plugins.concat(config.plugins || []).forEach(function(plugin) { |
| 6964 | var idx = plugins.indexOf(plugin); |
| 6965 | if (idx !== -1) { |
| 6966 | return; |
| 6967 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 6968 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 6969 | var id = plugin.id; |
| 6970 | var opts = options[id]; |
| 6971 | if (opts === false) { |
| 6972 | return; |
| 6973 | } |
| 6974 | |
| 6975 | if (opts === true) { |
| 6976 | opts = helpers.clone(defaults[id]); |
| 6977 | } |
| 6978 | |
| 6979 | plugins.push(plugin); |
| 6980 | descriptors.push({ |
| 6981 | plugin: plugin, |
| 6982 | options: opts || {} |
| 6983 | }); |
| 6984 | }); |
| 6985 | |
| 6986 | cache.descriptors = descriptors; |
| 6987 | cache.id = this._cacheId; |
| 6988 | return descriptors; |
| 6989 | } |
| 6990 | }; |
| 6991 | |
| 6992 | /** |
| 6993 | * Plugin extension hooks. |
| 6994 | * @interface IPlugin |
| 6995 | * @since 2.1.0 |
| 6996 | */ |
| 6997 | /** |
| 6998 | * @method IPlugin#beforeInit |
| 6999 | * @desc Called before initializing `chart`. |
| 7000 | * @param {Chart.Controller} chart - The chart instance. |
| 7001 | * @param {Object} options - The plugin options. |
| 7002 | */ |
| 7003 | /** |
| 7004 | * @method IPlugin#afterInit |
| 7005 | * @desc Called after `chart` has been initialized and before the first update. |
| 7006 | * @param {Chart.Controller} chart - The chart instance. |
| 7007 | * @param {Object} options - The plugin options. |
| 7008 | */ |
| 7009 | /** |
| 7010 | * @method IPlugin#beforeUpdate |
| 7011 | * @desc Called before updating `chart`. If any plugin returns `false`, the update |
| 7012 | * is cancelled (and thus subsequent render(s)) until another `update` is triggered. |
| 7013 | * @param {Chart.Controller} chart - The chart instance. |
| 7014 | * @param {Object} options - The plugin options. |
| 7015 | * @returns {Boolean} `false` to cancel the chart update. |
| 7016 | */ |
| 7017 | /** |
| 7018 | * @method IPlugin#afterUpdate |
| 7019 | * @desc Called after `chart` has been updated and before rendering. Note that this |
| 7020 | * hook will not be called if the chart update has been previously cancelled. |
| 7021 | * @param {Chart.Controller} chart - The chart instance. |
| 7022 | * @param {Object} options - The plugin options. |
| 7023 | */ |
| 7024 | /** |
| 7025 | * @method IPlugin#beforeDatasetsUpdate |
| 7026 | * @desc Called before updating the `chart` datasets. If any plugin returns `false`, |
| 7027 | * the datasets update is cancelled until another `update` is triggered. |
| 7028 | * @param {Chart.Controller} chart - The chart instance. |
| 7029 | * @param {Object} options - The plugin options. |
| 7030 | * @returns {Boolean} false to cancel the datasets update. |
| 7031 | * @since version 2.1.5 |
| 7032 | */ |
| 7033 | /** |
| 7034 | * @method IPlugin#afterDatasetsUpdate |
| 7035 | * @desc Called after the `chart` datasets have been updated. Note that this hook |
| 7036 | * will not be called if the datasets update has been previously cancelled. |
| 7037 | * @param {Chart.Controller} chart - The chart instance. |
| 7038 | * @param {Object} options - The plugin options. |
| 7039 | * @since version 2.1.5 |
| 7040 | */ |
| 7041 | /** |
| 7042 | * @method IPlugin#beforeDatasetUpdate |
| 7043 | * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin |
| 7044 | * returns `false`, the datasets update is cancelled until another `update` is triggered. |
| 7045 | * @param {Chart} chart - The chart instance. |
| 7046 | * @param {Object} args - The call arguments. |
| 7047 | * @param {Object} args.index - The dataset index. |
| 7048 | * @param {Number} args.meta - The dataset metadata. |
| 7049 | * @param {Object} options - The plugin options. |
| 7050 | * @returns {Boolean} `false` to cancel the chart datasets drawing. |
| 7051 | */ |
| 7052 | /** |
| 7053 | * @method IPlugin#afterDatasetUpdate |
| 7054 | * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note |
| 7055 | * that this hook will not be called if the datasets update has been previously cancelled. |
| 7056 | * @param {Chart} chart - The chart instance. |
| 7057 | * @param {Object} args - The call arguments. |
| 7058 | * @param {Object} args.index - The dataset index. |
| 7059 | * @param {Number} args.meta - The dataset metadata. |
| 7060 | * @param {Object} options - The plugin options. |
| 7061 | */ |
| 7062 | /** |
| 7063 | * @method IPlugin#beforeLayout |
| 7064 | * @desc Called before laying out `chart`. If any plugin returns `false`, |
| 7065 | * the layout update is cancelled until another `update` is triggered. |
| 7066 | * @param {Chart.Controller} chart - The chart instance. |
| 7067 | * @param {Object} options - The plugin options. |
| 7068 | * @returns {Boolean} `false` to cancel the chart layout. |
| 7069 | */ |
| 7070 | /** |
| 7071 | * @method IPlugin#afterLayout |
| 7072 | * @desc Called after the `chart` has been layed out. Note that this hook will not |
| 7073 | * be called if the layout update has been previously cancelled. |
| 7074 | * @param {Chart.Controller} chart - The chart instance. |
| 7075 | * @param {Object} options - The plugin options. |
| 7076 | */ |
| 7077 | /** |
| 7078 | * @method IPlugin#beforeRender |
| 7079 | * @desc Called before rendering `chart`. If any plugin returns `false`, |
| 7080 | * the rendering is cancelled until another `render` is triggered. |
| 7081 | * @param {Chart.Controller} chart - The chart instance. |
| 7082 | * @param {Object} options - The plugin options. |
| 7083 | * @returns {Boolean} `false` to cancel the chart rendering. |
| 7084 | */ |
| 7085 | /** |
| 7086 | * @method IPlugin#afterRender |
| 7087 | * @desc Called after the `chart` has been fully rendered (and animation completed). Note |
| 7088 | * that this hook will not be called if the rendering has been previously cancelled. |
| 7089 | * @param {Chart.Controller} chart - The chart instance. |
| 7090 | * @param {Object} options - The plugin options. |
| 7091 | */ |
| 7092 | /** |
| 7093 | * @method IPlugin#beforeDraw |
| 7094 | * @desc Called before drawing `chart` at every animation frame specified by the given |
| 7095 | * easing value. If any plugin returns `false`, the frame drawing is cancelled until |
| 7096 | * another `render` is triggered. |
| 7097 | * @param {Chart.Controller} chart - The chart instance. |
| 7098 | * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 7099 | * @param {Object} options - The plugin options. |
| 7100 | * @returns {Boolean} `false` to cancel the chart drawing. |
| 7101 | */ |
| 7102 | /** |
| 7103 | * @method IPlugin#afterDraw |
| 7104 | * @desc Called after the `chart` has been drawn for the specific easing value. Note |
| 7105 | * that this hook will not be called if the drawing has been previously cancelled. |
| 7106 | * @param {Chart.Controller} chart - The chart instance. |
| 7107 | * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 7108 | * @param {Object} options - The plugin options. |
| 7109 | */ |
| 7110 | /** |
| 7111 | * @method IPlugin#beforeDatasetsDraw |
| 7112 | * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, |
| 7113 | * the datasets drawing is cancelled until another `render` is triggered. |
| 7114 | * @param {Chart.Controller} chart - The chart instance. |
| 7115 | * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 7116 | * @param {Object} options - The plugin options. |
| 7117 | * @returns {Boolean} `false` to cancel the chart datasets drawing. |
| 7118 | */ |
| 7119 | /** |
| 7120 | * @method IPlugin#afterDatasetsDraw |
| 7121 | * @desc Called after the `chart` datasets have been drawn. Note that this hook |
| 7122 | * will not be called if the datasets drawing has been previously cancelled. |
| 7123 | * @param {Chart.Controller} chart - The chart instance. |
| 7124 | * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. |
| 7125 | * @param {Object} options - The plugin options. |
| 7126 | */ |
| 7127 | /** |
| 7128 | * @method IPlugin#beforeDatasetDraw |
| 7129 | * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets |
| 7130 | * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing |
| 7131 | * is cancelled until another `render` is triggered. |
| 7132 | * @param {Chart} chart - The chart instance. |
| 7133 | * @param {Object} args - The call arguments. |
| 7134 | * @param {Object} args.index - The dataset index. |
| 7135 | * @param {Number} args.meta - The dataset metadata. |
| 7136 | * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. |
| 7137 | * @param {Object} options - The plugin options. |
| 7138 | * @returns {Boolean} `false` to cancel the chart datasets drawing. |
| 7139 | */ |
| 7140 | /** |
| 7141 | * @method IPlugin#afterDatasetDraw |
| 7142 | * @desc Called after the `chart` datasets at the given `args.index` have been drawn |
| 7143 | * (datasets are drawn in the reverse order). Note that this hook will not be called |
| 7144 | * if the datasets drawing has been previously cancelled. |
| 7145 | * @param {Chart} chart - The chart instance. |
| 7146 | * @param {Object} args - The call arguments. |
| 7147 | * @param {Object} args.index - The dataset index. |
| 7148 | * @param {Number} args.meta - The dataset metadata. |
| 7149 | * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. |
| 7150 | * @param {Object} options - The plugin options. |
| 7151 | */ |
| 7152 | /** |
| 7153 | * @method IPlugin#beforeEvent |
| 7154 | * @desc Called before processing the specified `event`. If any plugin returns `false`, |
| 7155 | * the event will be discarded. |
| 7156 | * @param {Chart.Controller} chart - The chart instance. |
| 7157 | * @param {IEvent} event - The event object. |
| 7158 | * @param {Object} options - The plugin options. |
| 7159 | */ |
| 7160 | /** |
| 7161 | * @method IPlugin#afterEvent |
| 7162 | * @desc Called after the `event` has been consumed. Note that this hook |
| 7163 | * will not be called if the `event` has been previously discarded. |
| 7164 | * @param {Chart.Controller} chart - The chart instance. |
| 7165 | * @param {IEvent} event - The event object. |
| 7166 | * @param {Object} options - The plugin options. |
| 7167 | */ |
| 7168 | /** |
| 7169 | * @method IPlugin#resize |
| 7170 | * @desc Called after the chart as been resized. |
| 7171 | * @param {Chart.Controller} chart - The chart instance. |
| 7172 | * @param {Number} size - The new canvas display size (eq. canvas.style width & height). |
| 7173 | * @param {Object} options - The plugin options. |
| 7174 | */ |
| 7175 | /** |
| 7176 | * @method IPlugin#destroy |
| 7177 | * @desc Called after the chart as been destroyed. |
| 7178 | * @param {Chart.Controller} chart - The chart instance. |
| 7179 | * @param {Object} options - The plugin options. |
| 7180 | */ |
| 7181 | |
| 7182 | /** |
| 7183 | * Provided for backward compatibility, use Chart.plugins instead |
| 7184 | * @namespace Chart.pluginService |
| 7185 | * @deprecated since version 2.1.5 |
| 7186 | * @todo remove at version 3 |
| 7187 | * @private |
| 7188 | */ |
| 7189 | Chart.pluginService = Chart.plugins; |
| 7190 | |
| 7191 | /** |
| 7192 | * Provided for backward compatibility, inheriting from Chart.PlugingBase has no |
| 7193 | * effect, instead simply create/register plugins via plain JavaScript objects. |
| 7194 | * @interface Chart.PluginBase |
| 7195 | * @deprecated since version 2.5.0 |
| 7196 | * @todo remove at version 3 |
| 7197 | * @private |
| 7198 | */ |
| 7199 | Chart.PluginBase = Chart.Element.extend({}); |
| 7200 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7201 | |
| 7202 | },{}],31:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7203 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7204 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7205 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7206 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7207 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7208 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7209 | Chart.defaults.scale = { |
| 7210 | display: true, |
| 7211 | position: 'left', |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7212 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7213 | // grid line settings |
| 7214 | gridLines: { |
| 7215 | display: true, |
| 7216 | color: 'rgba(0, 0, 0, 0.1)', |
| 7217 | lineWidth: 1, |
| 7218 | drawBorder: true, |
| 7219 | drawOnChartArea: true, |
| 7220 | drawTicks: true, |
| 7221 | tickMarkLength: 10, |
| 7222 | zeroLineWidth: 1, |
| 7223 | zeroLineColor: 'rgba(0,0,0,0.25)', |
| 7224 | zeroLineBorderDash: [], |
| 7225 | zeroLineBorderDashOffset: 0.0, |
| 7226 | offsetGridLines: false, |
| 7227 | borderDash: [], |
| 7228 | borderDashOffset: 0.0 |
| 7229 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7230 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7231 | // scale label |
| 7232 | scaleLabel: { |
| 7233 | // actual label |
| 7234 | labelString: '', |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7235 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7236 | // display property |
| 7237 | display: false |
| 7238 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7239 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7240 | // label settings |
| 7241 | ticks: { |
| 7242 | beginAtZero: false, |
| 7243 | minRotation: 0, |
| 7244 | maxRotation: 50, |
| 7245 | mirror: false, |
| 7246 | padding: 0, |
| 7247 | reverse: false, |
| 7248 | display: true, |
| 7249 | autoSkip: true, |
| 7250 | autoSkipPadding: 0, |
| 7251 | labelOffset: 0, |
| 7252 | // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. |
| 7253 | callback: Chart.Ticks.formatters.values |
| 7254 | } |
| 7255 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7256 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7257 | function computeTextSize(context, tick, font) { |
| 7258 | return helpers.isArray(tick) ? |
| 7259 | helpers.longestText(context, font, tick) : |
| 7260 | context.measureText(tick).width; |
| 7261 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7262 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7263 | function parseFontOptions(options) { |
| 7264 | var getValueOrDefault = helpers.getValueOrDefault; |
| 7265 | var globalDefaults = Chart.defaults.global; |
| 7266 | var size = getValueOrDefault(options.fontSize, globalDefaults.defaultFontSize); |
| 7267 | var style = getValueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); |
| 7268 | var family = getValueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7269 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7270 | return { |
| 7271 | size: size, |
| 7272 | style: style, |
| 7273 | family: family, |
| 7274 | font: helpers.fontString(size, style, family) |
| 7275 | }; |
| 7276 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7277 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7278 | Chart.Scale = Chart.Element.extend({ |
| 7279 | /** |
| 7280 | * Get the padding needed for the scale |
| 7281 | * @method getPadding |
| 7282 | * @private |
| 7283 | * @returns {Padding} the necessary padding |
| 7284 | */ |
| 7285 | getPadding: function() { |
| 7286 | var me = this; |
| 7287 | return { |
| 7288 | left: me.paddingLeft || 0, |
| 7289 | top: me.paddingTop || 0, |
| 7290 | right: me.paddingRight || 0, |
| 7291 | bottom: me.paddingBottom || 0 |
| 7292 | }; |
| 7293 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7294 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7295 | // These methods are ordered by lifecyle. Utilities then follow. |
| 7296 | // Any function defined here is inherited by all scale types. |
| 7297 | // Any function can be extended by the scale type |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7298 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7299 | beforeUpdate: function() { |
| 7300 | helpers.callback(this.options.beforeUpdate, [this]); |
| 7301 | }, |
| 7302 | update: function(maxWidth, maxHeight, margins) { |
| 7303 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7304 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7305 | // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
| 7306 | me.beforeUpdate(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7307 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7308 | // Absorb the master measurements |
| 7309 | me.maxWidth = maxWidth; |
| 7310 | me.maxHeight = maxHeight; |
| 7311 | me.margins = helpers.extend({ |
| 7312 | left: 0, |
| 7313 | right: 0, |
| 7314 | top: 0, |
| 7315 | bottom: 0 |
| 7316 | }, margins); |
| 7317 | me.longestTextCache = me.longestTextCache || {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7318 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7319 | // Dimensions |
| 7320 | me.beforeSetDimensions(); |
| 7321 | me.setDimensions(); |
| 7322 | me.afterSetDimensions(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7323 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7324 | // Data min/max |
| 7325 | me.beforeDataLimits(); |
| 7326 | me.determineDataLimits(); |
| 7327 | me.afterDataLimits(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7328 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7329 | // Ticks |
| 7330 | me.beforeBuildTicks(); |
| 7331 | me.buildTicks(); |
| 7332 | me.afterBuildTicks(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7333 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7334 | me.beforeTickToLabelConversion(); |
| 7335 | me.convertTicksToLabels(); |
| 7336 | me.afterTickToLabelConversion(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7337 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7338 | // Tick Rotation |
| 7339 | me.beforeCalculateTickRotation(); |
| 7340 | me.calculateTickRotation(); |
| 7341 | me.afterCalculateTickRotation(); |
| 7342 | // Fit |
| 7343 | me.beforeFit(); |
| 7344 | me.fit(); |
| 7345 | me.afterFit(); |
| 7346 | // |
| 7347 | me.afterUpdate(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7348 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7349 | return me.minSize; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7350 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7351 | }, |
| 7352 | afterUpdate: function() { |
| 7353 | helpers.callback(this.options.afterUpdate, [this]); |
| 7354 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7355 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7356 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7357 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7358 | beforeSetDimensions: function() { |
| 7359 | helpers.callback(this.options.beforeSetDimensions, [this]); |
| 7360 | }, |
| 7361 | setDimensions: function() { |
| 7362 | var me = this; |
| 7363 | // Set the unconstrained dimension before label rotation |
| 7364 | if (me.isHorizontal()) { |
| 7365 | // Reset position before calculating rotation |
| 7366 | me.width = me.maxWidth; |
| 7367 | me.left = 0; |
| 7368 | me.right = me.width; |
| 7369 | } else { |
| 7370 | me.height = me.maxHeight; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7371 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7372 | // Reset position before calculating rotation |
| 7373 | me.top = 0; |
| 7374 | me.bottom = me.height; |
| 7375 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7376 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7377 | // Reset padding |
| 7378 | me.paddingLeft = 0; |
| 7379 | me.paddingTop = 0; |
| 7380 | me.paddingRight = 0; |
| 7381 | me.paddingBottom = 0; |
| 7382 | }, |
| 7383 | afterSetDimensions: function() { |
| 7384 | helpers.callback(this.options.afterSetDimensions, [this]); |
| 7385 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7386 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7387 | // Data limits |
| 7388 | beforeDataLimits: function() { |
| 7389 | helpers.callback(this.options.beforeDataLimits, [this]); |
| 7390 | }, |
| 7391 | determineDataLimits: helpers.noop, |
| 7392 | afterDataLimits: function() { |
| 7393 | helpers.callback(this.options.afterDataLimits, [this]); |
| 7394 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7395 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7396 | // |
| 7397 | beforeBuildTicks: function() { |
| 7398 | helpers.callback(this.options.beforeBuildTicks, [this]); |
| 7399 | }, |
| 7400 | buildTicks: helpers.noop, |
| 7401 | afterBuildTicks: function() { |
| 7402 | helpers.callback(this.options.afterBuildTicks, [this]); |
| 7403 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7404 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7405 | beforeTickToLabelConversion: function() { |
| 7406 | helpers.callback(this.options.beforeTickToLabelConversion, [this]); |
| 7407 | }, |
| 7408 | convertTicksToLabels: function() { |
| 7409 | var me = this; |
| 7410 | // Convert ticks to strings |
| 7411 | var tickOpts = me.options.ticks; |
| 7412 | me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback); |
| 7413 | }, |
| 7414 | afterTickToLabelConversion: function() { |
| 7415 | helpers.callback(this.options.afterTickToLabelConversion, [this]); |
| 7416 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7417 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7418 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7419 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7420 | beforeCalculateTickRotation: function() { |
| 7421 | helpers.callback(this.options.beforeCalculateTickRotation, [this]); |
| 7422 | }, |
| 7423 | calculateTickRotation: function() { |
| 7424 | var me = this; |
| 7425 | var context = me.ctx; |
| 7426 | var tickOpts = me.options.ticks; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7427 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7428 | // Get the width of each grid by calculating the difference |
| 7429 | // between x offsets between 0 and 1. |
| 7430 | var tickFont = parseFontOptions(tickOpts); |
| 7431 | context.font = tickFont.font; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7432 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7433 | var labelRotation = tickOpts.minRotation || 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7434 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7435 | if (me.options.display && me.isHorizontal()) { |
| 7436 | var originalLabelWidth = helpers.longestText(context, tickFont.font, me.ticks, me.longestTextCache); |
| 7437 | var labelWidth = originalLabelWidth; |
| 7438 | var cosRotation; |
| 7439 | var sinRotation; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7440 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7441 | // Allow 3 pixels x2 padding either side for label readability |
| 7442 | var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7443 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7444 | // Max label rotation can be set or default to 90 - also act as a loop counter |
| 7445 | while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { |
| 7446 | var angleRadians = helpers.toRadians(labelRotation); |
| 7447 | cosRotation = Math.cos(angleRadians); |
| 7448 | sinRotation = Math.sin(angleRadians); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7449 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7450 | if (sinRotation * originalLabelWidth > me.maxHeight) { |
| 7451 | // go back one step |
| 7452 | labelRotation--; |
| 7453 | break; |
| 7454 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7455 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7456 | labelRotation++; |
| 7457 | labelWidth = cosRotation * originalLabelWidth; |
| 7458 | } |
| 7459 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7460 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7461 | me.labelRotation = labelRotation; |
| 7462 | }, |
| 7463 | afterCalculateTickRotation: function() { |
| 7464 | helpers.callback(this.options.afterCalculateTickRotation, [this]); |
| 7465 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7466 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7467 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7468 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7469 | beforeFit: function() { |
| 7470 | helpers.callback(this.options.beforeFit, [this]); |
| 7471 | }, |
| 7472 | fit: function() { |
| 7473 | var me = this; |
| 7474 | // Reset |
| 7475 | var minSize = me.minSize = { |
| 7476 | width: 0, |
| 7477 | height: 0 |
| 7478 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7479 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7480 | var opts = me.options; |
| 7481 | var tickOpts = opts.ticks; |
| 7482 | var scaleLabelOpts = opts.scaleLabel; |
| 7483 | var gridLineOpts = opts.gridLines; |
| 7484 | var display = opts.display; |
| 7485 | var isHorizontal = me.isHorizontal(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7486 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7487 | var tickFont = parseFontOptions(tickOpts); |
| 7488 | var scaleLabelFontSize = parseFontOptions(scaleLabelOpts).size * 1.5; |
| 7489 | var tickMarkLength = opts.gridLines.tickMarkLength; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7490 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7491 | // Width |
| 7492 | if (isHorizontal) { |
| 7493 | // subtract the margins to line up with the chartArea if we are a full width scale |
| 7494 | minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; |
| 7495 | } else { |
| 7496 | minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; |
| 7497 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7498 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7499 | // height |
| 7500 | if (isHorizontal) { |
| 7501 | minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; |
| 7502 | } else { |
| 7503 | minSize.height = me.maxHeight; // fill all the height |
| 7504 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7505 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7506 | // Are we showing a title for the scale? |
| 7507 | if (scaleLabelOpts.display && display) { |
| 7508 | if (isHorizontal) { |
| 7509 | minSize.height += scaleLabelFontSize; |
| 7510 | } else { |
| 7511 | minSize.width += scaleLabelFontSize; |
| 7512 | } |
| 7513 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7514 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7515 | // Don't bother fitting the ticks if we are not showing them |
| 7516 | if (tickOpts.display && display) { |
| 7517 | var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, me.ticks, me.longestTextCache); |
| 7518 | var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks); |
| 7519 | var lineSpace = tickFont.size * 0.5; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7520 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7521 | if (isHorizontal) { |
| 7522 | // A horizontal axis is more constrained by the height. |
| 7523 | me.longestLabelWidth = largestTextWidth; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7524 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7525 | var angleRadians = helpers.toRadians(me.labelRotation); |
| 7526 | var cosRotation = Math.cos(angleRadians); |
| 7527 | var sinRotation = Math.sin(angleRadians); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7528 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7529 | // TODO - improve this calculation |
| 7530 | var labelHeight = (sinRotation * largestTextWidth) |
| 7531 | + (tickFont.size * tallestLabelHeightInLines) |
| 7532 | + (lineSpace * tallestLabelHeightInLines); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7533 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7534 | minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight); |
| 7535 | me.ctx.font = tickFont.font; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7536 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7537 | var firstTick = me.ticks[0]; |
| 7538 | var firstLabelWidth = computeTextSize(me.ctx, firstTick, tickFont.font); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7539 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7540 | var lastTick = me.ticks[me.ticks.length - 1]; |
| 7541 | var lastLabelWidth = computeTextSize(me.ctx, lastTick, tickFont.font); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7542 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7543 | // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated |
| 7544 | // by the font height |
| 7545 | if (me.labelRotation !== 0) { |
| 7546 | me.paddingLeft = opts.position === 'bottom'? (cosRotation * firstLabelWidth) + 3: (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges |
| 7547 | me.paddingRight = opts.position === 'bottom'? (cosRotation * lineSpace) + 3: (cosRotation * lastLabelWidth) + 3; |
| 7548 | } else { |
| 7549 | me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges |
| 7550 | me.paddingRight = lastLabelWidth / 2 + 3; |
| 7551 | } |
| 7552 | } else { |
| 7553 | // A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first |
| 7554 | // Account for padding |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7555 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7556 | if (tickOpts.mirror) { |
| 7557 | largestTextWidth = 0; |
| 7558 | } else { |
| 7559 | largestTextWidth += me.options.ticks.padding; |
| 7560 | } |
| 7561 | minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); |
| 7562 | me.paddingTop = tickFont.size / 2; |
| 7563 | me.paddingBottom = tickFont.size / 2; |
| 7564 | } |
| 7565 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7566 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7567 | me.handleMargins(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7568 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7569 | me.width = minSize.width; |
| 7570 | me.height = minSize.height; |
| 7571 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7572 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7573 | /** |
| 7574 | * Handle margins and padding interactions |
| 7575 | * @private |
| 7576 | */ |
| 7577 | handleMargins: function() { |
| 7578 | var me = this; |
| 7579 | if (me.margins) { |
| 7580 | me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); |
| 7581 | me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); |
| 7582 | me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); |
| 7583 | me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); |
| 7584 | } |
| 7585 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7586 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7587 | afterFit: function() { |
| 7588 | helpers.callback(this.options.afterFit, [this]); |
| 7589 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7590 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7591 | // Shared Methods |
| 7592 | isHorizontal: function() { |
| 7593 | return this.options.position === 'top' || this.options.position === 'bottom'; |
| 7594 | }, |
| 7595 | isFullWidth: function() { |
| 7596 | return (this.options.fullWidth); |
| 7597 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7598 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7599 | // 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 |
| 7600 | getRightValue: function(rawValue) { |
| 7601 | // Null and undefined values first |
| 7602 | if (rawValue === null || typeof(rawValue) === 'undefined') { |
| 7603 | return NaN; |
| 7604 | } |
| 7605 | // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values |
| 7606 | if (typeof(rawValue) === 'number' && !isFinite(rawValue)) { |
| 7607 | return NaN; |
| 7608 | } |
| 7609 | // If it is in fact an object, dive in one more level |
| 7610 | if (typeof(rawValue) === 'object') { |
| 7611 | if ((rawValue instanceof Date) || (rawValue.isValid)) { |
| 7612 | return rawValue; |
| 7613 | } |
| 7614 | return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y); |
| 7615 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7616 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7617 | // Value is good, return it |
| 7618 | return rawValue; |
| 7619 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7620 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7621 | // Used to get the value to display in the tooltip for the data at the given index |
| 7622 | // function getLabelForIndex(index, datasetIndex) |
| 7623 | getLabelForIndex: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7624 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7625 | // Used to get data value locations. Value can either be an index or a numerical value |
| 7626 | getPixelForValue: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7627 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7628 | // Used to get the data value from a given pixel. This is the inverse of getPixelForValue |
| 7629 | getValueForPixel: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7630 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7631 | // Used for tick location, should |
| 7632 | getPixelForTick: function(index, includeOffset) { |
| 7633 | var me = this; |
| 7634 | if (me.isHorizontal()) { |
| 7635 | var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
| 7636 | var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); |
| 7637 | var pixel = (tickWidth * index) + me.paddingLeft; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7638 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7639 | if (includeOffset) { |
| 7640 | pixel += tickWidth / 2; |
| 7641 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7642 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7643 | var finalVal = me.left + Math.round(pixel); |
| 7644 | finalVal += me.isFullWidth() ? me.margins.left : 0; |
| 7645 | return finalVal; |
| 7646 | } |
| 7647 | var innerHeight = me.height - (me.paddingTop + me.paddingBottom); |
| 7648 | return me.top + (index * (innerHeight / (me.ticks.length - 1))); |
| 7649 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7650 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7651 | // Utility for getting the pixel location of a percentage of scale |
| 7652 | getPixelForDecimal: function(decimal /* , includeOffset*/) { |
| 7653 | var me = this; |
| 7654 | if (me.isHorizontal()) { |
| 7655 | var innerWidth = me.width - (me.paddingLeft + me.paddingRight); |
| 7656 | var valueOffset = (innerWidth * decimal) + me.paddingLeft; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7657 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7658 | var finalVal = me.left + Math.round(valueOffset); |
| 7659 | finalVal += me.isFullWidth() ? me.margins.left : 0; |
| 7660 | return finalVal; |
| 7661 | } |
| 7662 | return me.top + (decimal * me.height); |
| 7663 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7664 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7665 | getBasePixel: function() { |
| 7666 | return this.getPixelForValue(this.getBaseValue()); |
| 7667 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7668 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7669 | getBaseValue: function() { |
| 7670 | var me = this; |
| 7671 | var min = me.min; |
| 7672 | var max = me.max; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7673 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7674 | return me.beginAtZero ? 0: |
| 7675 | min < 0 && max < 0? max : |
| 7676 | min > 0 && max > 0? min : |
| 7677 | 0; |
| 7678 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7679 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7680 | // Actually draw the scale on the canvas |
| 7681 | // @param {rectangle} chartArea : the area of the chart to draw full grid lines on |
| 7682 | draw: function(chartArea) { |
| 7683 | var me = this; |
| 7684 | var options = me.options; |
| 7685 | if (!options.display) { |
| 7686 | return; |
| 7687 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7688 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7689 | var context = me.ctx; |
| 7690 | var globalDefaults = Chart.defaults.global; |
| 7691 | var optionTicks = options.ticks; |
| 7692 | var gridLines = options.gridLines; |
| 7693 | var scaleLabel = options.scaleLabel; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7694 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7695 | var isRotated = me.labelRotation !== 0; |
| 7696 | var skipRatio; |
| 7697 | var useAutoskipper = optionTicks.autoSkip; |
| 7698 | var isHorizontal = me.isHorizontal(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7699 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7700 | // figure out the maximum number of gridlines to show |
| 7701 | var maxTicks; |
| 7702 | if (optionTicks.maxTicksLimit) { |
| 7703 | maxTicks = optionTicks.maxTicksLimit; |
| 7704 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7705 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7706 | var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); |
| 7707 | var tickFont = parseFontOptions(optionTicks); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7708 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7709 | var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7710 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7711 | var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); |
| 7712 | var scaleLabelFont = parseFontOptions(scaleLabel); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7713 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7714 | var labelRotationRadians = helpers.toRadians(me.labelRotation); |
| 7715 | var cosRotation = Math.cos(labelRotationRadians); |
| 7716 | var longestRotatedLabel = me.longestLabelWidth * cosRotation; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7717 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7718 | // Make sure we draw text in the correct color and font |
| 7719 | context.fillStyle = tickFontColor; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7720 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7721 | var itemsToDraw = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7722 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7723 | if (isHorizontal) { |
| 7724 | skipRatio = false; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7725 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7726 | if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) { |
| 7727 | skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight))); |
| 7728 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7729 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7730 | // if they defined a max number of optionTicks, |
| 7731 | // increase skipRatio until that number is met |
| 7732 | if (maxTicks && me.ticks.length > maxTicks) { |
| 7733 | while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) { |
| 7734 | if (!skipRatio) { |
| 7735 | skipRatio = 1; |
| 7736 | } |
| 7737 | skipRatio += 1; |
| 7738 | } |
| 7739 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7740 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7741 | if (!useAutoskipper) { |
| 7742 | skipRatio = false; |
| 7743 | } |
| 7744 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7745 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7746 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7747 | var xTickStart = options.position === 'right' ? me.left : me.right - tl; |
| 7748 | var xTickEnd = options.position === 'right' ? me.left + tl : me.right; |
| 7749 | var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl; |
| 7750 | var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 7751 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7752 | helpers.each(me.ticks, function(label, index) { |
| 7753 | // If the callback returned a null or undefined value, do not draw this line |
| 7754 | if (label === undefined || label === null) { |
| 7755 | return; |
| 7756 | } |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 7757 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7758 | var isLastTick = me.ticks.length === index + 1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7759 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7760 | // Since we always show the last tick,we need may need to hide the last shown one before |
| 7761 | var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length); |
| 7762 | if (shouldSkip && !isLastTick || (label === undefined || label === null)) { |
| 7763 | return; |
| 7764 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7765 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7766 | var lineWidth, lineColor, borderDash, borderDashOffset; |
| 7767 | if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) { |
| 7768 | // Draw the first index specially |
| 7769 | lineWidth = gridLines.zeroLineWidth; |
| 7770 | lineColor = gridLines.zeroLineColor; |
| 7771 | borderDash = gridLines.zeroLineBorderDash; |
| 7772 | borderDashOffset = gridLines.zeroLineBorderDashOffset; |
| 7773 | } else { |
| 7774 | lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index); |
| 7775 | lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index); |
| 7776 | borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash); |
| 7777 | borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); |
| 7778 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7779 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7780 | // Common properties |
| 7781 | var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; |
| 7782 | var textAlign = 'middle'; |
| 7783 | var textBaseline = 'middle'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7784 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7785 | if (isHorizontal) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7786 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7787 | if (options.position === 'bottom') { |
| 7788 | // bottom |
| 7789 | textBaseline = !isRotated? 'top':'middle'; |
| 7790 | textAlign = !isRotated? 'center': 'right'; |
| 7791 | labelY = me.top + tl; |
| 7792 | } else { |
| 7793 | // top |
| 7794 | textBaseline = !isRotated? 'bottom':'middle'; |
| 7795 | textAlign = !isRotated? 'center': 'left'; |
| 7796 | labelY = me.bottom - tl; |
| 7797 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7798 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7799 | var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines |
| 7800 | labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7801 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7802 | tx1 = tx2 = x1 = x2 = xLineValue; |
| 7803 | ty1 = yTickStart; |
| 7804 | ty2 = yTickEnd; |
| 7805 | y1 = chartArea.top; |
| 7806 | y2 = chartArea.bottom; |
| 7807 | } else { |
| 7808 | var isLeft = options.position === 'left'; |
| 7809 | var tickPadding = optionTicks.padding; |
| 7810 | var labelXOffset; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7811 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7812 | if (optionTicks.mirror) { |
| 7813 | textAlign = isLeft ? 'left' : 'right'; |
| 7814 | labelXOffset = tickPadding; |
| 7815 | } else { |
| 7816 | textAlign = isLeft ? 'right' : 'left'; |
| 7817 | labelXOffset = tl + tickPadding; |
| 7818 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7819 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7820 | labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7821 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7822 | var yLineValue = me.getPixelForTick(index); // xvalues for grid lines |
| 7823 | yLineValue += helpers.aliasPixel(lineWidth); |
| 7824 | labelY = me.getPixelForTick(index, gridLines.offsetGridLines); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7825 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7826 | tx1 = xTickStart; |
| 7827 | tx2 = xTickEnd; |
| 7828 | x1 = chartArea.left; |
| 7829 | x2 = chartArea.right; |
| 7830 | ty1 = ty2 = y1 = y2 = yLineValue; |
| 7831 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7832 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7833 | itemsToDraw.push({ |
| 7834 | tx1: tx1, |
| 7835 | ty1: ty1, |
| 7836 | tx2: tx2, |
| 7837 | ty2: ty2, |
| 7838 | x1: x1, |
| 7839 | y1: y1, |
| 7840 | x2: x2, |
| 7841 | y2: y2, |
| 7842 | labelX: labelX, |
| 7843 | labelY: labelY, |
| 7844 | glWidth: lineWidth, |
| 7845 | glColor: lineColor, |
| 7846 | glBorderDash: borderDash, |
| 7847 | glBorderDashOffset: borderDashOffset, |
| 7848 | rotation: -1 * labelRotationRadians, |
| 7849 | label: label, |
| 7850 | textBaseline: textBaseline, |
| 7851 | textAlign: textAlign |
| 7852 | }); |
| 7853 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7854 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7855 | // Draw all of the tick labels, tick marks, and grid lines at the correct places |
| 7856 | helpers.each(itemsToDraw, function(itemToDraw) { |
| 7857 | if (gridLines.display) { |
| 7858 | context.save(); |
| 7859 | context.lineWidth = itemToDraw.glWidth; |
| 7860 | context.strokeStyle = itemToDraw.glColor; |
| 7861 | if (context.setLineDash) { |
| 7862 | context.setLineDash(itemToDraw.glBorderDash); |
| 7863 | context.lineDashOffset = itemToDraw.glBorderDashOffset; |
| 7864 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7865 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7866 | context.beginPath(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7867 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7868 | if (gridLines.drawTicks) { |
| 7869 | context.moveTo(itemToDraw.tx1, itemToDraw.ty1); |
| 7870 | context.lineTo(itemToDraw.tx2, itemToDraw.ty2); |
| 7871 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7872 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7873 | if (gridLines.drawOnChartArea) { |
| 7874 | context.moveTo(itemToDraw.x1, itemToDraw.y1); |
| 7875 | context.lineTo(itemToDraw.x2, itemToDraw.y2); |
| 7876 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7877 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7878 | context.stroke(); |
| 7879 | context.restore(); |
| 7880 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7881 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7882 | if (optionTicks.display) { |
| 7883 | context.save(); |
| 7884 | context.translate(itemToDraw.labelX, itemToDraw.labelY); |
| 7885 | context.rotate(itemToDraw.rotation); |
| 7886 | context.font = tickFont.font; |
| 7887 | context.textBaseline = itemToDraw.textBaseline; |
| 7888 | context.textAlign = itemToDraw.textAlign; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7889 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7890 | var label = itemToDraw.label; |
| 7891 | if (helpers.isArray(label)) { |
| 7892 | for (var i = 0, y = 0; i < label.length; ++i) { |
| 7893 | // We just make sure the multiline element is a string here.. |
| 7894 | context.fillText('' + label[i], 0, y); |
| 7895 | // apply same lineSpacing as calculated @ L#320 |
| 7896 | y += (tickFont.size * 1.5); |
| 7897 | } |
| 7898 | } else { |
| 7899 | context.fillText(label, 0, 0); |
| 7900 | } |
| 7901 | context.restore(); |
| 7902 | } |
| 7903 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7904 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7905 | if (scaleLabel.display) { |
| 7906 | // Draw the scale label |
| 7907 | var scaleLabelX; |
| 7908 | var scaleLabelY; |
| 7909 | var rotation = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7910 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7911 | if (isHorizontal) { |
| 7912 | scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width |
| 7913 | scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFont.size / 2) : me.top + (scaleLabelFont.size / 2); |
| 7914 | } else { |
| 7915 | var isLeft = options.position === 'left'; |
| 7916 | scaleLabelX = isLeft ? me.left + (scaleLabelFont.size / 2) : me.right - (scaleLabelFont.size / 2); |
| 7917 | scaleLabelY = me.top + ((me.bottom - me.top) / 2); |
| 7918 | rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; |
| 7919 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7920 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7921 | context.save(); |
| 7922 | context.translate(scaleLabelX, scaleLabelY); |
| 7923 | context.rotate(rotation); |
| 7924 | context.textAlign = 'center'; |
| 7925 | context.textBaseline = 'middle'; |
| 7926 | context.fillStyle = scaleLabelFontColor; // render in correct colour |
| 7927 | context.font = scaleLabelFont.font; |
| 7928 | context.fillText(scaleLabel.labelString, 0, 0); |
| 7929 | context.restore(); |
| 7930 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7931 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7932 | if (gridLines.drawBorder) { |
| 7933 | // Draw the line at the edge of the axis |
| 7934 | context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0); |
| 7935 | context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0); |
| 7936 | var x1 = me.left, |
| 7937 | x2 = me.right, |
| 7938 | y1 = me.top, |
| 7939 | y2 = me.bottom; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7940 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7941 | var aliasPixel = helpers.aliasPixel(context.lineWidth); |
| 7942 | if (isHorizontal) { |
| 7943 | y1 = y2 = options.position === 'top' ? me.bottom : me.top; |
| 7944 | y1 += aliasPixel; |
| 7945 | y2 += aliasPixel; |
| 7946 | } else { |
| 7947 | x1 = x2 = options.position === 'left' ? me.right : me.left; |
| 7948 | x1 += aliasPixel; |
| 7949 | x2 += aliasPixel; |
| 7950 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7951 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7952 | context.beginPath(); |
| 7953 | context.moveTo(x1, y1); |
| 7954 | context.lineTo(x2, y2); |
| 7955 | context.stroke(); |
| 7956 | } |
| 7957 | } |
| 7958 | }); |
| 7959 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7960 | |
| 7961 | },{}],32:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7962 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7963 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7964 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7965 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7966 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7967 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7968 | Chart.scaleService = { |
| 7969 | // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then |
| 7970 | // use the new chart options to grab the correct scale |
| 7971 | constructors: {}, |
| 7972 | // Use a registration function so that we can move to an ES6 map when we no longer need to support |
| 7973 | // old browsers |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 7974 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 7975 | // Scale config defaults |
| 7976 | defaults: {}, |
| 7977 | registerScaleType: function(type, scaleConstructor, defaults) { |
| 7978 | this.constructors[type] = scaleConstructor; |
| 7979 | this.defaults[type] = helpers.clone(defaults); |
| 7980 | }, |
| 7981 | getScaleConstructor: function(type) { |
| 7982 | return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; |
| 7983 | }, |
| 7984 | getScaleDefaults: function(type) { |
| 7985 | // Return the scale defaults merged with the global settings so that we always use the latest ones |
| 7986 | return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {}; |
| 7987 | }, |
| 7988 | updateScaleDefaults: function(type, additions) { |
| 7989 | var defaults = this.defaults; |
| 7990 | if (defaults.hasOwnProperty(type)) { |
| 7991 | defaults[type] = helpers.extend(defaults[type], additions); |
| 7992 | } |
| 7993 | }, |
| 7994 | addScalesToLayout: function(chart) { |
| 7995 | // Adds each scale to the chart.boxes array to be sized accordingly |
| 7996 | helpers.each(chart.scales, function(scale) { |
| 7997 | // Set ILayoutItem parameters for backwards compatibility |
| 7998 | scale.fullWidth = scale.options.fullWidth; |
| 7999 | scale.position = scale.options.position; |
| 8000 | scale.weight = scale.options.weight; |
| 8001 | Chart.layoutService.addBox(chart, scale); |
| 8002 | }); |
| 8003 | } |
| 8004 | }; |
| 8005 | }; |
| 8006 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8007 | },{}],33:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8008 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8009 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8010 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8011 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8012 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8013 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8014 | /** |
| 8015 | * Namespace to hold static tick generation functions |
| 8016 | * @namespace Chart.Ticks |
| 8017 | */ |
| 8018 | Chart.Ticks = { |
| 8019 | /** |
| 8020 | * Namespace to hold generators for different types of ticks |
| 8021 | * @namespace Chart.Ticks.generators |
| 8022 | */ |
| 8023 | generators: { |
| 8024 | /** |
| 8025 | * Interface for the options provided to the numeric tick generator |
| 8026 | * @interface INumericTickGenerationOptions |
| 8027 | */ |
| 8028 | /** |
| 8029 | * The maximum number of ticks to display |
| 8030 | * @name INumericTickGenerationOptions#maxTicks |
| 8031 | * @type Number |
| 8032 | */ |
| 8033 | /** |
| 8034 | * The distance between each tick. |
| 8035 | * @name INumericTickGenerationOptions#stepSize |
| 8036 | * @type Number |
| 8037 | * @optional |
| 8038 | */ |
| 8039 | /** |
| 8040 | * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum |
| 8041 | * @name INumericTickGenerationOptions#min |
| 8042 | * @type Number |
| 8043 | * @optional |
| 8044 | */ |
| 8045 | /** |
| 8046 | * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum |
| 8047 | * @name INumericTickGenerationOptions#max |
| 8048 | * @type Number |
| 8049 | * @optional |
| 8050 | */ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8051 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8052 | /** |
| 8053 | * Generate a set of linear ticks |
| 8054 | * @method Chart.Ticks.generators.linear |
| 8055 | * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks |
| 8056 | * @param dataRange {IRange} the range of the data |
| 8057 | * @returns {Array<Number>} array of tick values |
| 8058 | */ |
| 8059 | linear: function(generationOptions, dataRange) { |
| 8060 | var ticks = []; |
| 8061 | // To get a "nice" value for the tick spacing, we will use the appropriately named |
| 8062 | // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks |
| 8063 | // for details. |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8064 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8065 | var spacing; |
| 8066 | if (generationOptions.stepSize && generationOptions.stepSize > 0) { |
| 8067 | spacing = generationOptions.stepSize; |
| 8068 | } else { |
| 8069 | var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); |
| 8070 | spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); |
| 8071 | } |
| 8072 | var niceMin = Math.floor(dataRange.min / spacing) * spacing; |
| 8073 | var niceMax = Math.ceil(dataRange.max / spacing) * spacing; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8074 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8075 | // If min, max and stepSize is set and they make an evenly spaced scale use it. |
| 8076 | if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { |
| 8077 | // If very close to our whole number, use it. |
| 8078 | if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { |
| 8079 | niceMin = generationOptions.min; |
| 8080 | niceMax = generationOptions.max; |
| 8081 | } |
| 8082 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8083 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8084 | var numSpaces = (niceMax - niceMin) / spacing; |
| 8085 | // If very close to our rounded value, use it. |
| 8086 | if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { |
| 8087 | numSpaces = Math.round(numSpaces); |
| 8088 | } else { |
| 8089 | numSpaces = Math.ceil(numSpaces); |
| 8090 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8091 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8092 | // Put the values into the ticks array |
| 8093 | ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); |
| 8094 | for (var j = 1; j < numSpaces; ++j) { |
| 8095 | ticks.push(niceMin + (j * spacing)); |
| 8096 | } |
| 8097 | ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8098 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8099 | return ticks; |
| 8100 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8101 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8102 | /** |
| 8103 | * Generate a set of logarithmic ticks |
| 8104 | * @method Chart.Ticks.generators.logarithmic |
| 8105 | * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks |
| 8106 | * @param dataRange {IRange} the range of the data |
| 8107 | * @returns {Array<Number>} array of tick values |
| 8108 | */ |
| 8109 | logarithmic: function(generationOptions, dataRange) { |
| 8110 | var ticks = []; |
| 8111 | var getValueOrDefault = helpers.getValueOrDefault; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8112 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8113 | // Figure out what the max number of ticks we can support it is based on the size of |
| 8114 | // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 |
| 8115 | // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on |
| 8116 | // the graph |
| 8117 | var tickVal = getValueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8118 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8119 | var endExp = Math.floor(helpers.log10(dataRange.max)); |
| 8120 | var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); |
| 8121 | var exp; |
| 8122 | var significand; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8123 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8124 | if (tickVal === 0) { |
| 8125 | exp = Math.floor(helpers.log10(dataRange.minNotZero)); |
| 8126 | significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8127 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8128 | ticks.push(tickVal); |
| 8129 | tickVal = significand * Math.pow(10, exp); |
| 8130 | } else { |
| 8131 | exp = Math.floor(helpers.log10(tickVal)); |
| 8132 | significand = Math.floor(tickVal / Math.pow(10, exp)); |
| 8133 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8134 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8135 | do { |
| 8136 | ticks.push(tickVal); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8137 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8138 | ++significand; |
| 8139 | if (significand === 10) { |
| 8140 | significand = 1; |
| 8141 | ++exp; |
| 8142 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8143 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8144 | tickVal = significand * Math.pow(10, exp); |
| 8145 | } while (exp < endExp || (exp === endExp && significand < endSignificand)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8146 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8147 | var lastTick = getValueOrDefault(generationOptions.max, tickVal); |
| 8148 | ticks.push(lastTick); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8149 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8150 | return ticks; |
| 8151 | } |
| 8152 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8153 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8154 | /** |
| 8155 | * Namespace to hold formatters for different types of ticks |
| 8156 | * @namespace Chart.Ticks.formatters |
| 8157 | */ |
| 8158 | formatters: { |
| 8159 | /** |
| 8160 | * Formatter for value labels |
| 8161 | * @method Chart.Ticks.formatters.values |
| 8162 | * @param value the value to display |
| 8163 | * @return {String|Array} the label to display |
| 8164 | */ |
| 8165 | values: function(value) { |
| 8166 | return helpers.isArray(value) ? value : '' + value; |
| 8167 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8168 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8169 | /** |
| 8170 | * Formatter for linear numeric ticks |
| 8171 | * @method Chart.Ticks.formatters.linear |
| 8172 | * @param tickValue {Number} the value to be formatted |
| 8173 | * @param index {Number} the position of the tickValue parameter in the ticks array |
| 8174 | * @param ticks {Array<Number>} the list of ticks being converted |
| 8175 | * @return {String} string representation of the tickValue parameter |
| 8176 | */ |
| 8177 | linear: function(tickValue, index, ticks) { |
| 8178 | // If we have lots of ticks, don't use the ones |
| 8179 | var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8180 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8181 | // If we have a number like 2.5 as the delta, figure out how many decimal places we need |
| 8182 | if (Math.abs(delta) > 1) { |
| 8183 | if (tickValue !== Math.floor(tickValue)) { |
| 8184 | // not an integer |
| 8185 | delta = tickValue - Math.floor(tickValue); |
| 8186 | } |
| 8187 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8188 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8189 | var logDelta = helpers.log10(Math.abs(delta)); |
| 8190 | var tickString = ''; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8191 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8192 | if (tickValue !== 0) { |
| 8193 | var numDecimal = -1 * Math.floor(logDelta); |
| 8194 | numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places |
| 8195 | tickString = tickValue.toFixed(numDecimal); |
| 8196 | } else { |
| 8197 | tickString = '0'; // never show decimal places for 0 |
| 8198 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8199 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8200 | return tickString; |
| 8201 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8202 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8203 | logarithmic: function(tickValue, index, ticks) { |
| 8204 | var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8205 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8206 | if (tickValue === 0) { |
| 8207 | return '0'; |
| 8208 | } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { |
| 8209 | return tickValue.toExponential(); |
| 8210 | } |
| 8211 | return ''; |
| 8212 | } |
| 8213 | } |
| 8214 | }; |
| 8215 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8216 | |
| 8217 | },{}],34:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8218 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8219 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8220 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8221 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8222 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8223 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8224 | /** |
| 8225 | * Helper method to merge the opacity into a color |
| 8226 | */ |
| 8227 | function mergeOpacity(colorString, opacity) { |
| 8228 | var color = helpers.color(colorString); |
| 8229 | return color.alpha(opacity * color.alpha()).rgbaString(); |
| 8230 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8231 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8232 | Chart.defaults.global.tooltips = { |
| 8233 | enabled: true, |
| 8234 | custom: null, |
| 8235 | mode: 'nearest', |
| 8236 | position: 'average', |
| 8237 | intersect: true, |
| 8238 | backgroundColor: 'rgba(0,0,0,0.8)', |
| 8239 | titleFontStyle: 'bold', |
| 8240 | titleSpacing: 2, |
| 8241 | titleMarginBottom: 6, |
| 8242 | titleFontColor: '#fff', |
| 8243 | titleAlign: 'left', |
| 8244 | bodySpacing: 2, |
| 8245 | bodyFontColor: '#fff', |
| 8246 | bodyAlign: 'left', |
| 8247 | footerFontStyle: 'bold', |
| 8248 | footerSpacing: 2, |
| 8249 | footerMarginTop: 6, |
| 8250 | footerFontColor: '#fff', |
| 8251 | footerAlign: 'left', |
| 8252 | yPadding: 6, |
| 8253 | xPadding: 6, |
| 8254 | caretPadding: 2, |
| 8255 | caretSize: 5, |
| 8256 | cornerRadius: 6, |
| 8257 | multiKeyBackground: '#fff', |
| 8258 | displayColors: true, |
| 8259 | borderColor: 'rgba(0,0,0,0)', |
| 8260 | borderWidth: 0, |
| 8261 | callbacks: { |
| 8262 | // Args are: (tooltipItems, data) |
| 8263 | beforeTitle: helpers.noop, |
| 8264 | title: function(tooltipItems, data) { |
| 8265 | // Pick first xLabel for now |
| 8266 | var title = ''; |
| 8267 | var labels = data.labels; |
| 8268 | var labelCount = labels ? labels.length : 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8269 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8270 | if (tooltipItems.length > 0) { |
| 8271 | var item = tooltipItems[0]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8272 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8273 | if (item.xLabel) { |
| 8274 | title = item.xLabel; |
| 8275 | } else if (labelCount > 0 && item.index < labelCount) { |
| 8276 | title = labels[item.index]; |
| 8277 | } |
| 8278 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8279 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8280 | return title; |
| 8281 | }, |
| 8282 | afterTitle: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8283 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8284 | // Args are: (tooltipItems, data) |
| 8285 | beforeBody: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8286 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8287 | // Args are: (tooltipItem, data) |
| 8288 | beforeLabel: helpers.noop, |
| 8289 | label: function(tooltipItem, data) { |
| 8290 | var label = data.datasets[tooltipItem.datasetIndex].label || ''; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8291 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8292 | if (label) { |
| 8293 | label += ': '; |
| 8294 | } |
| 8295 | label += tooltipItem.yLabel; |
| 8296 | return label; |
| 8297 | }, |
| 8298 | labelColor: function(tooltipItem, chart) { |
| 8299 | var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); |
| 8300 | var activeElement = meta.data[tooltipItem.index]; |
| 8301 | var view = activeElement._view; |
| 8302 | return { |
| 8303 | borderColor: view.borderColor, |
| 8304 | backgroundColor: view.backgroundColor |
| 8305 | }; |
| 8306 | }, |
| 8307 | afterLabel: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8308 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8309 | // Args are: (tooltipItems, data) |
| 8310 | afterBody: helpers.noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8311 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8312 | // Args are: (tooltipItems, data) |
| 8313 | beforeFooter: helpers.noop, |
| 8314 | footer: helpers.noop, |
| 8315 | afterFooter: helpers.noop |
| 8316 | } |
| 8317 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8318 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8319 | // Helper to push or concat based on if the 2nd parameter is an array or not |
| 8320 | function pushOrConcat(base, toPush) { |
| 8321 | if (toPush) { |
| 8322 | if (helpers.isArray(toPush)) { |
| 8323 | // base = base.concat(toPush); |
| 8324 | Array.prototype.push.apply(base, toPush); |
| 8325 | } else { |
| 8326 | base.push(toPush); |
| 8327 | } |
| 8328 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8329 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8330 | return base; |
| 8331 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8332 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8333 | // Private helper to create a tooltip item model |
| 8334 | // @param element : the chart element (point, arc, bar) to create the tooltip item for |
| 8335 | // @return : new tooltip item |
| 8336 | function createTooltipItem(element) { |
| 8337 | var xScale = element._xScale; |
| 8338 | var yScale = element._yScale || element._scale; // handle radar || polarArea charts |
| 8339 | var index = element._index, |
| 8340 | datasetIndex = element._datasetIndex; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8341 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8342 | return { |
| 8343 | xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', |
| 8344 | yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', |
| 8345 | index: index, |
| 8346 | datasetIndex: datasetIndex, |
| 8347 | x: element._model.x, |
| 8348 | y: element._model.y |
| 8349 | }; |
| 8350 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8351 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8352 | /** |
| 8353 | * Helper to get the reset model for the tooltip |
| 8354 | * @param tooltipOpts {Object} the tooltip options |
| 8355 | */ |
| 8356 | function getBaseModel(tooltipOpts) { |
| 8357 | var globalDefaults = Chart.defaults.global; |
| 8358 | var getValueOrDefault = helpers.getValueOrDefault; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8359 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8360 | return { |
| 8361 | // Positioning |
| 8362 | xPadding: tooltipOpts.xPadding, |
| 8363 | yPadding: tooltipOpts.yPadding, |
| 8364 | xAlign: tooltipOpts.xAlign, |
| 8365 | yAlign: tooltipOpts.yAlign, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8366 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8367 | // Body |
| 8368 | bodyFontColor: tooltipOpts.bodyFontColor, |
| 8369 | _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), |
| 8370 | _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), |
| 8371 | _bodyAlign: tooltipOpts.bodyAlign, |
| 8372 | bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), |
| 8373 | bodySpacing: tooltipOpts.bodySpacing, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8374 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8375 | // Title |
| 8376 | titleFontColor: tooltipOpts.titleFontColor, |
| 8377 | _titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), |
| 8378 | _titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), |
| 8379 | titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), |
| 8380 | _titleAlign: tooltipOpts.titleAlign, |
| 8381 | titleSpacing: tooltipOpts.titleSpacing, |
| 8382 | titleMarginBottom: tooltipOpts.titleMarginBottom, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8383 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8384 | // Footer |
| 8385 | footerFontColor: tooltipOpts.footerFontColor, |
| 8386 | _footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), |
| 8387 | _footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), |
| 8388 | footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), |
| 8389 | _footerAlign: tooltipOpts.footerAlign, |
| 8390 | footerSpacing: tooltipOpts.footerSpacing, |
| 8391 | footerMarginTop: tooltipOpts.footerMarginTop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8392 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8393 | // Appearance |
| 8394 | caretSize: tooltipOpts.caretSize, |
| 8395 | cornerRadius: tooltipOpts.cornerRadius, |
| 8396 | backgroundColor: tooltipOpts.backgroundColor, |
| 8397 | opacity: 0, |
| 8398 | legendColorBackground: tooltipOpts.multiKeyBackground, |
| 8399 | displayColors: tooltipOpts.displayColors, |
| 8400 | borderColor: tooltipOpts.borderColor, |
| 8401 | borderWidth: tooltipOpts.borderWidth |
| 8402 | }; |
| 8403 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8404 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8405 | /** |
| 8406 | * Get the size of the tooltip |
| 8407 | */ |
| 8408 | function getTooltipSize(tooltip, model) { |
| 8409 | var ctx = tooltip._chart.ctx; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8410 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8411 | var height = model.yPadding * 2; // Tooltip Padding |
| 8412 | var width = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8413 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8414 | // Count of all lines in the body |
| 8415 | var body = model.body; |
| 8416 | var combinedBodyLength = body.reduce(function(count, bodyItem) { |
| 8417 | return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; |
| 8418 | }, 0); |
| 8419 | combinedBodyLength += model.beforeBody.length + model.afterBody.length; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8420 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8421 | var titleLineCount = model.title.length; |
| 8422 | var footerLineCount = model.footer.length; |
| 8423 | var titleFontSize = model.titleFontSize, |
| 8424 | bodyFontSize = model.bodyFontSize, |
| 8425 | footerFontSize = model.footerFontSize; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8426 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8427 | height += titleLineCount * titleFontSize; // Title Lines |
| 8428 | height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing |
| 8429 | height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin |
| 8430 | height += combinedBodyLength * bodyFontSize; // Body Lines |
| 8431 | height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing |
| 8432 | height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin |
| 8433 | height += footerLineCount * (footerFontSize); // Footer Lines |
| 8434 | height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8435 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8436 | // Title width |
| 8437 | var widthPadding = 0; |
| 8438 | var maxLineWidth = function(line) { |
| 8439 | width = Math.max(width, ctx.measureText(line).width + widthPadding); |
| 8440 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8441 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8442 | ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); |
| 8443 | helpers.each(model.title, maxLineWidth); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8444 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8445 | // Body width |
| 8446 | ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); |
| 8447 | helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8448 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8449 | // Body lines may include some extra width due to the color box |
| 8450 | widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; |
| 8451 | helpers.each(body, function(bodyItem) { |
| 8452 | helpers.each(bodyItem.before, maxLineWidth); |
| 8453 | helpers.each(bodyItem.lines, maxLineWidth); |
| 8454 | helpers.each(bodyItem.after, maxLineWidth); |
| 8455 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8456 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8457 | // Reset back to 0 |
| 8458 | widthPadding = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8459 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8460 | // Footer width |
| 8461 | ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); |
| 8462 | helpers.each(model.footer, maxLineWidth); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8463 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8464 | // Add padding |
| 8465 | width += 2 * model.xPadding; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8466 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8467 | return { |
| 8468 | width: width, |
| 8469 | height: height |
| 8470 | }; |
| 8471 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8472 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8473 | /** |
| 8474 | * Helper to get the alignment of a tooltip given the size |
| 8475 | */ |
| 8476 | function determineAlignment(tooltip, size) { |
| 8477 | var model = tooltip._model; |
| 8478 | var chart = tooltip._chart; |
| 8479 | var chartArea = tooltip._chart.chartArea; |
| 8480 | var xAlign = 'center'; |
| 8481 | var yAlign = 'center'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8482 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8483 | if (model.y < size.height) { |
| 8484 | yAlign = 'top'; |
| 8485 | } else if (model.y > (chart.height - size.height)) { |
| 8486 | yAlign = 'bottom'; |
| 8487 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8488 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8489 | var lf, rf; // functions to determine left, right alignment |
| 8490 | var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart |
| 8491 | var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges |
| 8492 | var midX = (chartArea.left + chartArea.right) / 2; |
| 8493 | var midY = (chartArea.top + chartArea.bottom) / 2; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8494 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8495 | if (yAlign === 'center') { |
| 8496 | lf = function(x) { |
| 8497 | return x <= midX; |
| 8498 | }; |
| 8499 | rf = function(x) { |
| 8500 | return x > midX; |
| 8501 | }; |
| 8502 | } else { |
| 8503 | lf = function(x) { |
| 8504 | return x <= (size.width / 2); |
| 8505 | }; |
| 8506 | rf = function(x) { |
| 8507 | return x >= (chart.width - (size.width / 2)); |
| 8508 | }; |
| 8509 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8510 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8511 | olf = function(x) { |
| 8512 | return x + size.width > chart.width; |
| 8513 | }; |
| 8514 | orf = function(x) { |
| 8515 | return x - size.width < 0; |
| 8516 | }; |
| 8517 | yf = function(y) { |
| 8518 | return y <= midY ? 'top' : 'bottom'; |
| 8519 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8520 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8521 | if (lf(model.x)) { |
| 8522 | xAlign = 'left'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8523 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8524 | // Is tooltip too wide and goes over the right side of the chart.? |
| 8525 | if (olf(model.x)) { |
| 8526 | xAlign = 'center'; |
| 8527 | yAlign = yf(model.y); |
| 8528 | } |
| 8529 | } else if (rf(model.x)) { |
| 8530 | xAlign = 'right'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8531 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8532 | // Is tooltip too wide and goes outside left edge of canvas? |
| 8533 | if (orf(model.x)) { |
| 8534 | xAlign = 'center'; |
| 8535 | yAlign = yf(model.y); |
| 8536 | } |
| 8537 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8538 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8539 | var opts = tooltip._options; |
| 8540 | return { |
| 8541 | xAlign: opts.xAlign ? opts.xAlign : xAlign, |
| 8542 | yAlign: opts.yAlign ? opts.yAlign : yAlign |
| 8543 | }; |
| 8544 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8545 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8546 | /** |
| 8547 | * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment |
| 8548 | */ |
| 8549 | function getBackgroundPoint(vm, size, alignment) { |
| 8550 | // Background Position |
| 8551 | var x = vm.x; |
| 8552 | var y = vm.y; |
| 8553 | |
| 8554 | var caretSize = vm.caretSize, |
| 8555 | caretPadding = vm.caretPadding, |
| 8556 | cornerRadius = vm.cornerRadius, |
| 8557 | xAlign = alignment.xAlign, |
| 8558 | yAlign = alignment.yAlign, |
| 8559 | paddingAndSize = caretSize + caretPadding, |
| 8560 | radiusAndPadding = cornerRadius + caretPadding; |
| 8561 | |
| 8562 | if (xAlign === 'right') { |
| 8563 | x -= size.width; |
| 8564 | } else if (xAlign === 'center') { |
| 8565 | x -= (size.width / 2); |
| 8566 | } |
| 8567 | |
| 8568 | if (yAlign === 'top') { |
| 8569 | y += paddingAndSize; |
| 8570 | } else if (yAlign === 'bottom') { |
| 8571 | y -= size.height + paddingAndSize; |
| 8572 | } else { |
| 8573 | y -= (size.height / 2); |
| 8574 | } |
| 8575 | |
| 8576 | if (yAlign === 'center') { |
| 8577 | if (xAlign === 'left') { |
| 8578 | x += paddingAndSize; |
| 8579 | } else if (xAlign === 'right') { |
| 8580 | x -= paddingAndSize; |
| 8581 | } |
| 8582 | } else if (xAlign === 'left') { |
| 8583 | x -= radiusAndPadding; |
| 8584 | } else if (xAlign === 'right') { |
| 8585 | x += radiusAndPadding; |
| 8586 | } |
| 8587 | |
| 8588 | return { |
| 8589 | x: x, |
| 8590 | y: y |
| 8591 | }; |
| 8592 | } |
| 8593 | |
| 8594 | Chart.Tooltip = Chart.Element.extend({ |
| 8595 | initialize: function() { |
| 8596 | this._model = getBaseModel(this._options); |
| 8597 | }, |
| 8598 | |
| 8599 | // Get the title |
| 8600 | // Args are: (tooltipItem, data) |
| 8601 | getTitle: function() { |
| 8602 | var me = this; |
| 8603 | var opts = me._options; |
| 8604 | var callbacks = opts.callbacks; |
| 8605 | |
| 8606 | var beforeTitle = callbacks.beforeTitle.apply(me, arguments), |
| 8607 | title = callbacks.title.apply(me, arguments), |
| 8608 | afterTitle = callbacks.afterTitle.apply(me, arguments); |
| 8609 | |
| 8610 | var lines = []; |
| 8611 | lines = pushOrConcat(lines, beforeTitle); |
| 8612 | lines = pushOrConcat(lines, title); |
| 8613 | lines = pushOrConcat(lines, afterTitle); |
| 8614 | |
| 8615 | return lines; |
| 8616 | }, |
| 8617 | |
| 8618 | // Args are: (tooltipItem, data) |
| 8619 | getBeforeBody: function() { |
| 8620 | var lines = this._options.callbacks.beforeBody.apply(this, arguments); |
| 8621 | return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; |
| 8622 | }, |
| 8623 | |
| 8624 | // Args are: (tooltipItem, data) |
| 8625 | getBody: function(tooltipItems, data) { |
| 8626 | var me = this; |
| 8627 | var callbacks = me._options.callbacks; |
| 8628 | var bodyItems = []; |
| 8629 | |
| 8630 | helpers.each(tooltipItems, function(tooltipItem) { |
| 8631 | var bodyItem = { |
| 8632 | before: [], |
| 8633 | lines: [], |
| 8634 | after: [] |
| 8635 | }; |
| 8636 | pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); |
| 8637 | pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); |
| 8638 | pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); |
| 8639 | |
| 8640 | bodyItems.push(bodyItem); |
| 8641 | }); |
| 8642 | |
| 8643 | return bodyItems; |
| 8644 | }, |
| 8645 | |
| 8646 | // Args are: (tooltipItem, data) |
| 8647 | getAfterBody: function() { |
| 8648 | var lines = this._options.callbacks.afterBody.apply(this, arguments); |
| 8649 | return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; |
| 8650 | }, |
| 8651 | |
| 8652 | // Get the footer and beforeFooter and afterFooter lines |
| 8653 | // Args are: (tooltipItem, data) |
| 8654 | getFooter: function() { |
| 8655 | var me = this; |
| 8656 | var callbacks = me._options.callbacks; |
| 8657 | |
| 8658 | var beforeFooter = callbacks.beforeFooter.apply(me, arguments); |
| 8659 | var footer = callbacks.footer.apply(me, arguments); |
| 8660 | var afterFooter = callbacks.afterFooter.apply(me, arguments); |
| 8661 | |
| 8662 | var lines = []; |
| 8663 | lines = pushOrConcat(lines, beforeFooter); |
| 8664 | lines = pushOrConcat(lines, footer); |
| 8665 | lines = pushOrConcat(lines, afterFooter); |
| 8666 | |
| 8667 | return lines; |
| 8668 | }, |
| 8669 | |
| 8670 | update: function(changed) { |
| 8671 | var me = this; |
| 8672 | var opts = me._options; |
| 8673 | |
| 8674 | // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition |
| 8675 | // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time |
| 8676 | // which breaks any animations. |
| 8677 | var existingModel = me._model; |
| 8678 | var model = me._model = getBaseModel(opts); |
| 8679 | var active = me._active; |
| 8680 | |
| 8681 | var data = me._data; |
| 8682 | |
| 8683 | // In the case where active.length === 0 we need to keep these at existing values for good animations |
| 8684 | var alignment = { |
| 8685 | xAlign: existingModel.xAlign, |
| 8686 | yAlign: existingModel.yAlign |
| 8687 | }; |
| 8688 | var backgroundPoint = { |
| 8689 | x: existingModel.x, |
| 8690 | y: existingModel.y |
| 8691 | }; |
| 8692 | var tooltipSize = { |
| 8693 | width: existingModel.width, |
| 8694 | height: existingModel.height |
| 8695 | }; |
| 8696 | var tooltipPosition = { |
| 8697 | x: existingModel.caretX, |
| 8698 | y: existingModel.caretY |
| 8699 | }; |
| 8700 | |
| 8701 | var i, len; |
| 8702 | |
| 8703 | if (active.length) { |
| 8704 | model.opacity = 1; |
| 8705 | |
| 8706 | var labelColors = []; |
| 8707 | tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition); |
| 8708 | |
| 8709 | var tooltipItems = []; |
| 8710 | for (i = 0, len = active.length; i < len; ++i) { |
| 8711 | tooltipItems.push(createTooltipItem(active[i])); |
| 8712 | } |
| 8713 | |
| 8714 | // If the user provided a filter function, use it to modify the tooltip items |
| 8715 | if (opts.filter) { |
| 8716 | tooltipItems = tooltipItems.filter(function(a) { |
| 8717 | return opts.filter(a, data); |
| 8718 | }); |
| 8719 | } |
| 8720 | |
| 8721 | // If the user provided a sorting function, use it to modify the tooltip items |
| 8722 | if (opts.itemSort) { |
| 8723 | tooltipItems = tooltipItems.sort(function(a, b) { |
| 8724 | return opts.itemSort(a, b, data); |
| 8725 | }); |
| 8726 | } |
| 8727 | |
| 8728 | // Determine colors for boxes |
| 8729 | helpers.each(tooltipItems, function(tooltipItem) { |
| 8730 | labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); |
| 8731 | }); |
| 8732 | |
| 8733 | // Build the Text Lines |
| 8734 | model.title = me.getTitle(tooltipItems, data); |
| 8735 | model.beforeBody = me.getBeforeBody(tooltipItems, data); |
| 8736 | model.body = me.getBody(tooltipItems, data); |
| 8737 | model.afterBody = me.getAfterBody(tooltipItems, data); |
| 8738 | model.footer = me.getFooter(tooltipItems, data); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8739 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8740 | // Initial positioning and colors |
| 8741 | model.x = Math.round(tooltipPosition.x); |
| 8742 | model.y = Math.round(tooltipPosition.y); |
| 8743 | model.caretPadding = opts.caretPadding; |
| 8744 | model.labelColors = labelColors; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8745 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8746 | // data points |
| 8747 | model.dataPoints = tooltipItems; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8748 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8749 | // We need to determine alignment of the tooltip |
| 8750 | tooltipSize = getTooltipSize(this, model); |
| 8751 | alignment = determineAlignment(this, tooltipSize); |
| 8752 | // Final Size and Position |
| 8753 | backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment); |
| 8754 | } else { |
| 8755 | model.opacity = 0; |
| 8756 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8757 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8758 | model.xAlign = alignment.xAlign; |
| 8759 | model.yAlign = alignment.yAlign; |
| 8760 | model.x = backgroundPoint.x; |
| 8761 | model.y = backgroundPoint.y; |
| 8762 | model.width = tooltipSize.width; |
| 8763 | model.height = tooltipSize.height; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8764 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8765 | // Point where the caret on the tooltip points to |
| 8766 | model.caretX = tooltipPosition.x; |
| 8767 | model.caretY = tooltipPosition.y; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8768 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8769 | me._model = model; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8770 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8771 | if (changed && opts.custom) { |
| 8772 | opts.custom.call(me, model); |
| 8773 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8774 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8775 | return me; |
| 8776 | }, |
| 8777 | drawCaret: function(tooltipPoint, size) { |
| 8778 | var ctx = this._chart.ctx; |
| 8779 | var vm = this._view; |
| 8780 | var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8781 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8782 | ctx.lineTo(caretPosition.x1, caretPosition.y1); |
| 8783 | ctx.lineTo(caretPosition.x2, caretPosition.y2); |
| 8784 | ctx.lineTo(caretPosition.x3, caretPosition.y3); |
| 8785 | }, |
| 8786 | getCaretPosition: function(tooltipPoint, size, vm) { |
| 8787 | var x1, x2, x3; |
| 8788 | var y1, y2, y3; |
| 8789 | var caretSize = vm.caretSize; |
| 8790 | var cornerRadius = vm.cornerRadius; |
| 8791 | var xAlign = vm.xAlign, |
| 8792 | yAlign = vm.yAlign; |
| 8793 | var ptX = tooltipPoint.x, |
| 8794 | ptY = tooltipPoint.y; |
| 8795 | var width = size.width, |
| 8796 | height = size.height; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8797 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8798 | if (yAlign === 'center') { |
| 8799 | y2 = ptY + (height / 2); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8800 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8801 | if (xAlign === 'left') { |
| 8802 | x1 = ptX; |
| 8803 | x2 = x1 - caretSize; |
| 8804 | x3 = x1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8805 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8806 | y1 = y2 + caretSize; |
| 8807 | y3 = y2 - caretSize; |
| 8808 | } else { |
| 8809 | x1 = ptX + width; |
| 8810 | x2 = x1 + caretSize; |
| 8811 | x3 = x1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8812 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8813 | y1 = y2 - caretSize; |
| 8814 | y3 = y2 + caretSize; |
| 8815 | } |
| 8816 | } else { |
| 8817 | if (xAlign === 'left') { |
| 8818 | x2 = ptX + cornerRadius + (caretSize); |
| 8819 | x1 = x2 - caretSize; |
| 8820 | x3 = x2 + caretSize; |
| 8821 | } else if (xAlign === 'right') { |
| 8822 | x2 = ptX + width - cornerRadius - caretSize; |
| 8823 | x1 = x2 - caretSize; |
| 8824 | x3 = x2 + caretSize; |
| 8825 | } else { |
| 8826 | x2 = ptX + (width / 2); |
| 8827 | x1 = x2 - caretSize; |
| 8828 | x3 = x2 + caretSize; |
| 8829 | } |
| 8830 | if (yAlign === 'top') { |
| 8831 | y1 = ptY; |
| 8832 | y2 = y1 - caretSize; |
| 8833 | y3 = y1; |
| 8834 | } else { |
| 8835 | y1 = ptY + height; |
| 8836 | y2 = y1 + caretSize; |
| 8837 | y3 = y1; |
| 8838 | // invert drawing order |
| 8839 | var tmp = x3; |
| 8840 | x3 = x1; |
| 8841 | x1 = tmp; |
| 8842 | } |
| 8843 | } |
| 8844 | return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; |
| 8845 | }, |
| 8846 | drawTitle: function(pt, vm, ctx, opacity) { |
| 8847 | var title = vm.title; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8848 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8849 | if (title.length) { |
| 8850 | ctx.textAlign = vm._titleAlign; |
| 8851 | ctx.textBaseline = 'top'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8852 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8853 | var titleFontSize = vm.titleFontSize, |
| 8854 | titleSpacing = vm.titleSpacing; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8855 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8856 | ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); |
| 8857 | ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8858 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8859 | var i, len; |
| 8860 | for (i = 0, len = title.length; i < len; ++i) { |
| 8861 | ctx.fillText(title[i], pt.x, pt.y); |
| 8862 | pt.y += titleFontSize + titleSpacing; // Line Height and spacing |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8863 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8864 | if (i + 1 === title.length) { |
| 8865 | pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing |
| 8866 | } |
| 8867 | } |
| 8868 | } |
| 8869 | }, |
| 8870 | drawBody: function(pt, vm, ctx, opacity) { |
| 8871 | var bodyFontSize = vm.bodyFontSize; |
| 8872 | var bodySpacing = vm.bodySpacing; |
| 8873 | var body = vm.body; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8874 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8875 | ctx.textAlign = vm._bodyAlign; |
| 8876 | ctx.textBaseline = 'top'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8877 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8878 | var textColor = mergeOpacity(vm.bodyFontColor, opacity); |
| 8879 | ctx.fillStyle = textColor; |
| 8880 | ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8881 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8882 | // Before Body |
| 8883 | var xLinePadding = 0; |
| 8884 | var fillLineOfText = function(line) { |
| 8885 | ctx.fillText(line, pt.x + xLinePadding, pt.y); |
| 8886 | pt.y += bodyFontSize + bodySpacing; |
| 8887 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8888 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8889 | // Before body lines |
| 8890 | helpers.each(vm.beforeBody, fillLineOfText); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8891 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8892 | var drawColorBoxes = vm.displayColors; |
| 8893 | xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8894 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8895 | // Draw body lines now |
| 8896 | helpers.each(body, function(bodyItem, i) { |
| 8897 | helpers.each(bodyItem.before, fillLineOfText); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8898 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8899 | helpers.each(bodyItem.lines, function(line) { |
| 8900 | // Draw Legend-like boxes if needed |
| 8901 | if (drawColorBoxes) { |
| 8902 | // Fill a white rect so that colours merge nicely if the opacity is < 1 |
| 8903 | ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); |
| 8904 | ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8905 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8906 | // Border |
| 8907 | ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); |
| 8908 | ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8909 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8910 | // Inner square |
| 8911 | ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); |
| 8912 | ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8913 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8914 | ctx.fillStyle = textColor; |
| 8915 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8916 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8917 | fillLineOfText(line); |
| 8918 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8919 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8920 | helpers.each(bodyItem.after, fillLineOfText); |
| 8921 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8922 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8923 | // Reset back to 0 for after body |
| 8924 | xLinePadding = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8925 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8926 | // After body lines |
| 8927 | helpers.each(vm.afterBody, fillLineOfText); |
| 8928 | pt.y -= bodySpacing; // Remove last body spacing |
| 8929 | }, |
| 8930 | drawFooter: function(pt, vm, ctx, opacity) { |
| 8931 | var footer = vm.footer; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8932 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8933 | if (footer.length) { |
| 8934 | pt.y += vm.footerMarginTop; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8935 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8936 | ctx.textAlign = vm._footerAlign; |
| 8937 | ctx.textBaseline = 'top'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8938 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8939 | ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); |
| 8940 | ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8941 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8942 | helpers.each(footer, function(line) { |
| 8943 | ctx.fillText(line, pt.x, pt.y); |
| 8944 | pt.y += vm.footerFontSize + vm.footerSpacing; |
| 8945 | }); |
| 8946 | } |
| 8947 | }, |
| 8948 | drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { |
| 8949 | ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); |
| 8950 | ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); |
| 8951 | ctx.lineWidth = vm.borderWidth; |
| 8952 | var xAlign = vm.xAlign; |
| 8953 | var yAlign = vm.yAlign; |
| 8954 | var x = pt.x; |
| 8955 | var y = pt.y; |
| 8956 | var width = tooltipSize.width; |
| 8957 | var height = tooltipSize.height; |
| 8958 | var radius = vm.cornerRadius; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8959 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8960 | ctx.beginPath(); |
| 8961 | ctx.moveTo(x + radius, y); |
| 8962 | if (yAlign === 'top') { |
| 8963 | this.drawCaret(pt, tooltipSize); |
| 8964 | } |
| 8965 | ctx.lineTo(x + width - radius, y); |
| 8966 | ctx.quadraticCurveTo(x + width, y, x + width, y + radius); |
| 8967 | if (yAlign === 'center' && xAlign === 'right') { |
| 8968 | this.drawCaret(pt, tooltipSize); |
| 8969 | } |
| 8970 | ctx.lineTo(x + width, y + height - radius); |
| 8971 | ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); |
| 8972 | if (yAlign === 'bottom') { |
| 8973 | this.drawCaret(pt, tooltipSize); |
| 8974 | } |
| 8975 | ctx.lineTo(x + radius, y + height); |
| 8976 | ctx.quadraticCurveTo(x, y + height, x, y + height - radius); |
| 8977 | if (yAlign === 'center' && xAlign === 'left') { |
| 8978 | this.drawCaret(pt, tooltipSize); |
| 8979 | } |
| 8980 | ctx.lineTo(x, y + radius); |
| 8981 | ctx.quadraticCurveTo(x, y, x + radius, y); |
| 8982 | ctx.closePath(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8983 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8984 | ctx.fill(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8985 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8986 | if (vm.borderWidth > 0) { |
| 8987 | ctx.stroke(); |
| 8988 | } |
| 8989 | }, |
| 8990 | draw: function() { |
| 8991 | var ctx = this._chart.ctx; |
| 8992 | var vm = this._view; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8993 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8994 | if (vm.opacity === 0) { |
| 8995 | return; |
| 8996 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 8997 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 8998 | var tooltipSize = { |
| 8999 | width: vm.width, |
| 9000 | height: vm.height |
| 9001 | }; |
| 9002 | var pt = { |
| 9003 | x: vm.x, |
| 9004 | y: vm.y |
| 9005 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9006 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9007 | // IE11/Edge does not like very small opacities, so snap to 0 |
| 9008 | var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9009 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9010 | // Truthy/falsey value for empty tooltip |
| 9011 | var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9012 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9013 | if (this._options.enabled && hasTooltipContent) { |
| 9014 | // Draw Background |
| 9015 | this.drawBackground(pt, vm, ctx, tooltipSize, opacity); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9016 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9017 | // Draw Title, Body, and Footer |
| 9018 | pt.x += vm.xPadding; |
| 9019 | pt.y += vm.yPadding; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9020 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9021 | // Titles |
| 9022 | this.drawTitle(pt, vm, ctx, opacity); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9023 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9024 | // Body |
| 9025 | this.drawBody(pt, vm, ctx, opacity); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9026 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9027 | // Footer |
| 9028 | this.drawFooter(pt, vm, ctx, opacity); |
| 9029 | } |
| 9030 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9031 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9032 | /** |
| 9033 | * Handle an event |
| 9034 | * @private |
| 9035 | * @param {IEvent} event - The event to handle |
| 9036 | * @returns {Boolean} true if the tooltip changed |
| 9037 | */ |
| 9038 | handleEvent: function(e) { |
| 9039 | var me = this; |
| 9040 | var options = me._options; |
| 9041 | var changed = false; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9042 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9043 | me._lastActive = me._lastActive || []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9044 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9045 | // Find Active Elements for tooltips |
| 9046 | if (e.type === 'mouseout') { |
| 9047 | me._active = []; |
| 9048 | } else { |
| 9049 | me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); |
| 9050 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9051 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9052 | // Remember Last Actives |
| 9053 | changed = !helpers.arrayEquals(me._active, me._lastActive); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9054 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9055 | // If tooltip didn't change, do not handle the target event |
| 9056 | if (!changed) { |
| 9057 | return false; |
| 9058 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9059 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9060 | me._lastActive = me._active; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9061 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9062 | if (options.enabled || options.custom) { |
| 9063 | me._eventPosition = { |
| 9064 | x: e.x, |
| 9065 | y: e.y |
| 9066 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9067 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9068 | var model = me._model; |
| 9069 | me.update(true); |
| 9070 | me.pivot(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9071 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9072 | // See if our tooltip position changed |
| 9073 | changed |= (model.x !== me._model.x) || (model.y !== me._model.y); |
| 9074 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9075 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9076 | return changed; |
| 9077 | } |
| 9078 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9079 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9080 | /** |
| 9081 | * @namespace Chart.Tooltip.positioners |
| 9082 | */ |
| 9083 | Chart.Tooltip.positioners = { |
| 9084 | /** |
| 9085 | * Average mode places the tooltip at the average position of the elements shown |
| 9086 | * @function Chart.Tooltip.positioners.average |
| 9087 | * @param elements {ChartElement[]} the elements being displayed in the tooltip |
| 9088 | * @returns {Point} tooltip position |
| 9089 | */ |
| 9090 | average: function(elements) { |
| 9091 | if (!elements.length) { |
| 9092 | return false; |
| 9093 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9094 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9095 | var i, len; |
| 9096 | var x = 0; |
| 9097 | var y = 0; |
| 9098 | var count = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9099 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9100 | for (i = 0, len = elements.length; i < len; ++i) { |
| 9101 | var el = elements[i]; |
| 9102 | if (el && el.hasValue()) { |
| 9103 | var pos = el.tooltipPosition(); |
| 9104 | x += pos.x; |
| 9105 | y += pos.y; |
| 9106 | ++count; |
| 9107 | } |
| 9108 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9109 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9110 | return { |
| 9111 | x: Math.round(x / count), |
| 9112 | y: Math.round(y / count) |
| 9113 | }; |
| 9114 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9115 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9116 | /** |
| 9117 | * Gets the tooltip position nearest of the item nearest to the event position |
| 9118 | * @function Chart.Tooltip.positioners.nearest |
| 9119 | * @param elements {Chart.Element[]} the tooltip elements |
| 9120 | * @param eventPosition {Point} the position of the event in canvas coordinates |
| 9121 | * @returns {Point} the tooltip position |
| 9122 | */ |
| 9123 | nearest: function(elements, eventPosition) { |
| 9124 | var x = eventPosition.x; |
| 9125 | var y = eventPosition.y; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9126 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9127 | var nearestElement; |
| 9128 | var minDistance = Number.POSITIVE_INFINITY; |
| 9129 | var i, len; |
| 9130 | for (i = 0, len = elements.length; i < len; ++i) { |
| 9131 | var el = elements[i]; |
| 9132 | if (el && el.hasValue()) { |
| 9133 | var center = el.getCenterPoint(); |
| 9134 | var d = helpers.distanceBetweenPoints(eventPosition, center); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9135 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9136 | if (d < minDistance) { |
| 9137 | minDistance = d; |
| 9138 | nearestElement = el; |
| 9139 | } |
| 9140 | } |
| 9141 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9142 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9143 | if (nearestElement) { |
| 9144 | var tp = nearestElement.tooltipPosition(); |
| 9145 | x = tp.x; |
| 9146 | y = tp.y; |
| 9147 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9148 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9149 | return { |
| 9150 | x: x, |
| 9151 | y: y |
| 9152 | }; |
| 9153 | } |
| 9154 | }; |
| 9155 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9156 | |
| 9157 | },{}],35:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9158 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9159 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9160 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9161 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9162 | var helpers = Chart.helpers, |
| 9163 | globalOpts = Chart.defaults.global; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9164 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9165 | globalOpts.elements.arc = { |
| 9166 | backgroundColor: globalOpts.defaultColor, |
| 9167 | borderColor: '#fff', |
| 9168 | borderWidth: 2 |
| 9169 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9170 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9171 | Chart.elements.Arc = Chart.Element.extend({ |
| 9172 | inLabelRange: function(mouseX) { |
| 9173 | var vm = this._view; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9174 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9175 | if (vm) { |
| 9176 | return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); |
| 9177 | } |
| 9178 | return false; |
| 9179 | }, |
| 9180 | inRange: function(chartX, chartY) { |
| 9181 | var vm = this._view; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9182 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9183 | if (vm) { |
| 9184 | var pointRelativePosition = helpers.getAngleFromPoint(vm, { |
| 9185 | x: chartX, |
| 9186 | y: chartY |
| 9187 | }), |
| 9188 | angle = pointRelativePosition.angle, |
| 9189 | distance = pointRelativePosition.distance; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9190 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9191 | // Sanitise angle range |
| 9192 | var startAngle = vm.startAngle; |
| 9193 | var endAngle = vm.endAngle; |
| 9194 | while (endAngle < startAngle) { |
| 9195 | endAngle += 2.0 * Math.PI; |
| 9196 | } |
| 9197 | while (angle > endAngle) { |
| 9198 | angle -= 2.0 * Math.PI; |
| 9199 | } |
| 9200 | while (angle < startAngle) { |
| 9201 | angle += 2.0 * Math.PI; |
| 9202 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9203 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9204 | // Check if within the range of the open/close angle |
| 9205 | var betweenAngles = (angle >= startAngle && angle <= endAngle), |
| 9206 | withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9207 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9208 | return (betweenAngles && withinRadius); |
| 9209 | } |
| 9210 | return false; |
| 9211 | }, |
| 9212 | getCenterPoint: function() { |
| 9213 | var vm = this._view; |
| 9214 | var halfAngle = (vm.startAngle + vm.endAngle) / 2; |
| 9215 | var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; |
| 9216 | return { |
| 9217 | x: vm.x + Math.cos(halfAngle) * halfRadius, |
| 9218 | y: vm.y + Math.sin(halfAngle) * halfRadius |
| 9219 | }; |
| 9220 | }, |
| 9221 | getArea: function() { |
| 9222 | var vm = this._view; |
| 9223 | return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); |
| 9224 | }, |
| 9225 | tooltipPosition: function() { |
| 9226 | var vm = this._view; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9227 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9228 | var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2), |
| 9229 | rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; |
| 9230 | return { |
| 9231 | x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), |
| 9232 | y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) |
| 9233 | }; |
| 9234 | }, |
| 9235 | draw: function() { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9236 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9237 | var ctx = this._chart.ctx, |
| 9238 | vm = this._view, |
| 9239 | sA = vm.startAngle, |
| 9240 | eA = vm.endAngle; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9241 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9242 | ctx.beginPath(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9243 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9244 | ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); |
| 9245 | ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9246 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9247 | ctx.closePath(); |
| 9248 | ctx.strokeStyle = vm.borderColor; |
| 9249 | ctx.lineWidth = vm.borderWidth; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9250 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9251 | ctx.fillStyle = vm.backgroundColor; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9252 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9253 | ctx.fill(); |
| 9254 | ctx.lineJoin = 'bevel'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9255 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9256 | if (vm.borderWidth) { |
| 9257 | ctx.stroke(); |
| 9258 | } |
| 9259 | } |
| 9260 | }); |
| 9261 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9262 | |
| 9263 | },{}],36:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9264 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9265 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9266 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9267 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9268 | var helpers = Chart.helpers; |
| 9269 | var globalDefaults = Chart.defaults.global; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9270 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9271 | Chart.defaults.global.elements.line = { |
| 9272 | tension: 0.4, |
| 9273 | backgroundColor: globalDefaults.defaultColor, |
| 9274 | borderWidth: 3, |
| 9275 | borderColor: globalDefaults.defaultColor, |
| 9276 | borderCapStyle: 'butt', |
| 9277 | borderDash: [], |
| 9278 | borderDashOffset: 0.0, |
| 9279 | borderJoinStyle: 'miter', |
| 9280 | capBezierPoints: true, |
| 9281 | fill: true, // do we fill in the area between the line and its base axis |
| 9282 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9283 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9284 | Chart.elements.Line = Chart.Element.extend({ |
| 9285 | draw: function() { |
| 9286 | var me = this; |
| 9287 | var vm = me._view; |
| 9288 | var ctx = me._chart.ctx; |
| 9289 | var spanGaps = vm.spanGaps; |
| 9290 | var points = me._children.slice(); // clone array |
| 9291 | var globalOptionLineElements = globalDefaults.elements.line; |
| 9292 | var lastDrawnIndex = -1; |
| 9293 | var index, current, previous, currentVM; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9294 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9295 | // If we are looping, adding the first point again |
| 9296 | if (me._loop && points.length) { |
| 9297 | points.push(points[0]); |
| 9298 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9299 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9300 | ctx.save(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9301 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9302 | // Stroke Line Options |
| 9303 | ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9304 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9305 | // IE 9 and 10 do not support line dash |
| 9306 | if (ctx.setLineDash) { |
| 9307 | ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); |
| 9308 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9309 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9310 | ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; |
| 9311 | ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; |
| 9312 | ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; |
| 9313 | ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9314 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9315 | // Stroke Line |
| 9316 | ctx.beginPath(); |
| 9317 | lastDrawnIndex = -1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9318 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9319 | for (index = 0; index < points.length; ++index) { |
| 9320 | current = points[index]; |
| 9321 | previous = helpers.previousItem(points, index); |
| 9322 | currentVM = current._view; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9323 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9324 | // First point moves to it's starting position no matter what |
| 9325 | if (index === 0) { |
| 9326 | if (!currentVM.skip) { |
| 9327 | ctx.moveTo(currentVM.x, currentVM.y); |
| 9328 | lastDrawnIndex = index; |
| 9329 | } |
| 9330 | } else { |
| 9331 | previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9332 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9333 | if (!currentVM.skip) { |
| 9334 | if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { |
| 9335 | // There was a gap and this is the first point after the gap |
| 9336 | ctx.moveTo(currentVM.x, currentVM.y); |
| 9337 | } else { |
| 9338 | // Line to next point |
| 9339 | helpers.canvas.lineTo(ctx, previous._view, current._view); |
| 9340 | } |
| 9341 | lastDrawnIndex = index; |
| 9342 | } |
| 9343 | } |
| 9344 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9345 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9346 | ctx.stroke(); |
| 9347 | ctx.restore(); |
| 9348 | } |
| 9349 | }); |
| 9350 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9351 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9352 | },{}],37:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9353 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9354 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9355 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9356 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9357 | var helpers = Chart.helpers, |
| 9358 | globalOpts = Chart.defaults.global, |
| 9359 | defaultColor = globalOpts.defaultColor; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9360 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9361 | globalOpts.elements.point = { |
| 9362 | radius: 3, |
| 9363 | pointStyle: 'circle', |
| 9364 | backgroundColor: defaultColor, |
| 9365 | borderWidth: 1, |
| 9366 | borderColor: defaultColor, |
| 9367 | // Hover |
| 9368 | hitRadius: 1, |
| 9369 | hoverRadius: 4, |
| 9370 | hoverBorderWidth: 1 |
| 9371 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9372 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9373 | function xRange(mouseX) { |
| 9374 | var vm = this._view; |
| 9375 | return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; |
| 9376 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9377 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9378 | function yRange(mouseY) { |
| 9379 | var vm = this._view; |
| 9380 | return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; |
| 9381 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9382 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9383 | Chart.elements.Point = Chart.Element.extend({ |
| 9384 | inRange: function(mouseX, mouseY) { |
| 9385 | var vm = this._view; |
| 9386 | return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; |
| 9387 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9388 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9389 | inLabelRange: xRange, |
| 9390 | inXRange: xRange, |
| 9391 | inYRange: yRange, |
| 9392 | |
| 9393 | getCenterPoint: function() { |
| 9394 | var vm = this._view; |
| 9395 | return { |
| 9396 | x: vm.x, |
| 9397 | y: vm.y |
| 9398 | }; |
| 9399 | }, |
| 9400 | getArea: function() { |
| 9401 | return Math.PI * Math.pow(this._view.radius, 2); |
| 9402 | }, |
| 9403 | tooltipPosition: function() { |
| 9404 | var vm = this._view; |
| 9405 | return { |
| 9406 | x: vm.x, |
| 9407 | y: vm.y, |
| 9408 | padding: vm.radius + vm.borderWidth |
| 9409 | }; |
| 9410 | }, |
| 9411 | draw: function(chartArea) { |
| 9412 | var vm = this._view; |
| 9413 | var model = this._model; |
| 9414 | var ctx = this._chart.ctx; |
| 9415 | var pointStyle = vm.pointStyle; |
| 9416 | var radius = vm.radius; |
| 9417 | var x = vm.x; |
| 9418 | var y = vm.y; |
| 9419 | var color = Chart.helpers.color; |
| 9420 | var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) |
| 9421 | var ratio = 0; |
| 9422 | |
| 9423 | if (vm.skip) { |
| 9424 | return; |
| 9425 | } |
| 9426 | |
| 9427 | ctx.strokeStyle = vm.borderColor || defaultColor; |
| 9428 | ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth); |
| 9429 | ctx.fillStyle = vm.backgroundColor || defaultColor; |
| 9430 | |
| 9431 | // Cliping for Points. |
| 9432 | // going out from inner charArea? |
| 9433 | if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right*errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom*errMargin < model.y))) { |
| 9434 | // Point fade out |
| 9435 | if (model.x < chartArea.left) { |
| 9436 | ratio = (x - model.x) / (chartArea.left - model.x); |
| 9437 | } else if (chartArea.right*errMargin < model.x) { |
| 9438 | ratio = (model.x - x) / (model.x - chartArea.right); |
| 9439 | } else if (model.y < chartArea.top) { |
| 9440 | ratio = (y - model.y) / (chartArea.top - model.y); |
| 9441 | } else if (chartArea.bottom*errMargin < model.y) { |
| 9442 | ratio = (model.y - y) / (model.y - chartArea.bottom); |
| 9443 | } |
| 9444 | ratio = Math.round(ratio*100) / 100; |
| 9445 | ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); |
| 9446 | ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); |
| 9447 | } |
| 9448 | |
| 9449 | Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y); |
| 9450 | } |
| 9451 | }); |
| 9452 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9453 | |
| 9454 | },{}],38:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9455 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9456 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9457 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9458 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9459 | var globalOpts = Chart.defaults.global; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9460 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9461 | globalOpts.elements.rectangle = { |
| 9462 | backgroundColor: globalOpts.defaultColor, |
| 9463 | borderWidth: 0, |
| 9464 | borderColor: globalOpts.defaultColor, |
| 9465 | borderSkipped: 'bottom' |
| 9466 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9467 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9468 | function isVertical(bar) { |
| 9469 | return bar._view.width !== undefined; |
| 9470 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9471 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9472 | /** |
| 9473 | * Helper function to get the bounds of the bar regardless of the orientation |
| 9474 | * @private |
| 9475 | * @param bar {Chart.Element.Rectangle} the bar |
| 9476 | * @return {Bounds} bounds of the bar |
| 9477 | */ |
| 9478 | function getBarBounds(bar) { |
| 9479 | var vm = bar._view; |
| 9480 | var x1, x2, y1, y2; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9481 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9482 | if (isVertical(bar)) { |
| 9483 | // vertical |
| 9484 | var halfWidth = vm.width / 2; |
| 9485 | x1 = vm.x - halfWidth; |
| 9486 | x2 = vm.x + halfWidth; |
| 9487 | y1 = Math.min(vm.y, vm.base); |
| 9488 | y2 = Math.max(vm.y, vm.base); |
| 9489 | } else { |
| 9490 | // horizontal bar |
| 9491 | var halfHeight = vm.height / 2; |
| 9492 | x1 = Math.min(vm.x, vm.base); |
| 9493 | x2 = Math.max(vm.x, vm.base); |
| 9494 | y1 = vm.y - halfHeight; |
| 9495 | y2 = vm.y + halfHeight; |
| 9496 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9497 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9498 | return { |
| 9499 | left: x1, |
| 9500 | top: y1, |
| 9501 | right: x2, |
| 9502 | bottom: y2 |
| 9503 | }; |
| 9504 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9505 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9506 | Chart.elements.Rectangle = Chart.Element.extend({ |
| 9507 | draw: function() { |
| 9508 | var ctx = this._chart.ctx; |
| 9509 | var vm = this._view; |
| 9510 | var left, right, top, bottom, signX, signY, borderSkipped; |
| 9511 | var borderWidth = vm.borderWidth; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9512 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9513 | if (!vm.horizontal) { |
| 9514 | // bar |
| 9515 | left = vm.x - vm.width / 2; |
| 9516 | right = vm.x + vm.width / 2; |
| 9517 | top = vm.y; |
| 9518 | bottom = vm.base; |
| 9519 | signX = 1; |
| 9520 | signY = bottom > top? 1: -1; |
| 9521 | borderSkipped = vm.borderSkipped || 'bottom'; |
| 9522 | } else { |
| 9523 | // horizontal bar |
| 9524 | left = vm.base; |
| 9525 | right = vm.x; |
| 9526 | top = vm.y - vm.height / 2; |
| 9527 | bottom = vm.y + vm.height / 2; |
| 9528 | signX = right > left? 1: -1; |
| 9529 | signY = 1; |
| 9530 | borderSkipped = vm.borderSkipped || 'left'; |
| 9531 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9532 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9533 | // Canvas doesn't allow us to stroke inside the width so we can |
| 9534 | // adjust the sizes to fit if we're setting a stroke on the line |
| 9535 | if (borderWidth) { |
| 9536 | // borderWidth shold be less than bar width and bar height. |
| 9537 | var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); |
| 9538 | borderWidth = borderWidth > barSize? barSize: borderWidth; |
| 9539 | var halfStroke = borderWidth / 2; |
| 9540 | // Adjust borderWidth when bar top position is near vm.base(zero). |
| 9541 | var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0); |
| 9542 | var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0); |
| 9543 | var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0); |
| 9544 | var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0); |
| 9545 | // not become a vertical line? |
| 9546 | if (borderLeft !== borderRight) { |
| 9547 | top = borderTop; |
| 9548 | bottom = borderBottom; |
| 9549 | } |
| 9550 | // not become a horizontal line? |
| 9551 | if (borderTop !== borderBottom) { |
| 9552 | left = borderLeft; |
| 9553 | right = borderRight; |
| 9554 | } |
| 9555 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9556 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9557 | ctx.beginPath(); |
| 9558 | ctx.fillStyle = vm.backgroundColor; |
| 9559 | ctx.strokeStyle = vm.borderColor; |
| 9560 | ctx.lineWidth = borderWidth; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9561 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9562 | // Corner points, from bottom-left to bottom-right clockwise |
| 9563 | // | 1 2 | |
| 9564 | // | 0 3 | |
| 9565 | var corners = [ |
| 9566 | [left, bottom], |
| 9567 | [left, top], |
| 9568 | [right, top], |
| 9569 | [right, bottom] |
| 9570 | ]; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9571 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9572 | // Find first (starting) corner with fallback to 'bottom' |
| 9573 | var borders = ['bottom', 'left', 'top', 'right']; |
| 9574 | var startCorner = borders.indexOf(borderSkipped, 0); |
| 9575 | if (startCorner === -1) { |
| 9576 | startCorner = 0; |
| 9577 | } |
| 9578 | |
| 9579 | function cornerAt(index) { |
| 9580 | return corners[(startCorner + index) % 4]; |
| 9581 | } |
| 9582 | |
| 9583 | // Draw rectangle from 'startCorner' |
| 9584 | var corner = cornerAt(0); |
| 9585 | ctx.moveTo(corner[0], corner[1]); |
| 9586 | |
| 9587 | for (var i = 1; i < 4; i++) { |
| 9588 | corner = cornerAt(i); |
| 9589 | ctx.lineTo(corner[0], corner[1]); |
| 9590 | } |
| 9591 | |
| 9592 | ctx.fill(); |
| 9593 | if (borderWidth) { |
| 9594 | ctx.stroke(); |
| 9595 | } |
| 9596 | }, |
| 9597 | height: function() { |
| 9598 | var vm = this._view; |
| 9599 | return vm.base - vm.y; |
| 9600 | }, |
| 9601 | inRange: function(mouseX, mouseY) { |
| 9602 | var inRange = false; |
| 9603 | |
| 9604 | if (this._view) { |
| 9605 | var bounds = getBarBounds(this); |
| 9606 | inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; |
| 9607 | } |
| 9608 | |
| 9609 | return inRange; |
| 9610 | }, |
| 9611 | inLabelRange: function(mouseX, mouseY) { |
| 9612 | var me = this; |
| 9613 | if (!me._view) { |
| 9614 | return false; |
| 9615 | } |
| 9616 | |
| 9617 | var inRange = false; |
| 9618 | var bounds = getBarBounds(me); |
| 9619 | |
| 9620 | if (isVertical(me)) { |
| 9621 | inRange = mouseX >= bounds.left && mouseX <= bounds.right; |
| 9622 | } else { |
| 9623 | inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; |
| 9624 | } |
| 9625 | |
| 9626 | return inRange; |
| 9627 | }, |
| 9628 | inXRange: function(mouseX) { |
| 9629 | var bounds = getBarBounds(this); |
| 9630 | return mouseX >= bounds.left && mouseX <= bounds.right; |
| 9631 | }, |
| 9632 | inYRange: function(mouseY) { |
| 9633 | var bounds = getBarBounds(this); |
| 9634 | return mouseY >= bounds.top && mouseY <= bounds.bottom; |
| 9635 | }, |
| 9636 | getCenterPoint: function() { |
| 9637 | var vm = this._view; |
| 9638 | var x, y; |
| 9639 | if (isVertical(this)) { |
| 9640 | x = vm.x; |
| 9641 | y = (vm.y + vm.base) / 2; |
| 9642 | } else { |
| 9643 | x = (vm.x + vm.base) / 2; |
| 9644 | y = vm.y; |
| 9645 | } |
| 9646 | |
| 9647 | return {x: x, y: y}; |
| 9648 | }, |
| 9649 | getArea: function() { |
| 9650 | var vm = this._view; |
| 9651 | return vm.width * Math.abs(vm.y - vm.base); |
| 9652 | }, |
| 9653 | tooltipPosition: function() { |
| 9654 | var vm = this._view; |
| 9655 | return { |
| 9656 | x: vm.x, |
| 9657 | y: vm.y |
| 9658 | }; |
| 9659 | } |
| 9660 | }); |
| 9661 | |
| 9662 | }; |
| 9663 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9664 | },{}],39:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9665 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9666 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9667 | // Chart.Platform implementation for targeting a web browser |
| 9668 | module.exports = function(Chart) { |
| 9669 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9670 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9671 | // DOM event types -> Chart.js event types. |
| 9672 | // Note: only events with different types are mapped. |
| 9673 | // https://developer.mozilla.org/en-US/docs/Web/Events |
| 9674 | var eventTypeMap = { |
| 9675 | // Touch events |
| 9676 | touchstart: 'mousedown', |
| 9677 | touchmove: 'mousemove', |
| 9678 | touchend: 'mouseup', |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9679 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9680 | // Pointer events |
| 9681 | pointerenter: 'mouseenter', |
| 9682 | pointerdown: 'mousedown', |
| 9683 | pointermove: 'mousemove', |
| 9684 | pointerup: 'mouseup', |
| 9685 | pointerleave: 'mouseout', |
| 9686 | pointerout: 'mouseout' |
| 9687 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9688 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9689 | /** |
| 9690 | * The "used" size is the final value of a dimension property after all calculations have |
| 9691 | * been performed. This method uses the computed style of `element` but returns undefined |
| 9692 | * if the computed style is not expressed in pixels. That can happen in some cases where |
| 9693 | * `element` has a size relative to its parent and this last one is not yet displayed, |
| 9694 | * for example because of `display: none` on a parent node. |
| 9695 | * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value |
| 9696 | * @returns {Number} Size in pixels or undefined if unknown. |
| 9697 | */ |
| 9698 | function readUsedSize(element, property) { |
| 9699 | var value = helpers.getStyle(element, property); |
| 9700 | var matches = value && value.match(/^(\d+)(\.\d+)?px$/); |
| 9701 | return matches? Number(matches[1]) : undefined; |
| 9702 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9703 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9704 | /** |
| 9705 | * Initializes the canvas style and render size without modifying the canvas display size, |
| 9706 | * since responsiveness is handled by the controller.resize() method. The config is used |
| 9707 | * to determine the aspect ratio to apply in case no explicit height has been specified. |
| 9708 | */ |
| 9709 | function initCanvas(canvas, config) { |
| 9710 | var style = canvas.style; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9711 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9712 | // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it |
| 9713 | // returns null or '' if no explicit value has been set to the canvas attribute. |
| 9714 | var renderHeight = canvas.getAttribute('height'); |
| 9715 | var renderWidth = canvas.getAttribute('width'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9716 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9717 | // Chart.js modifies some canvas values that we want to restore on destroy |
| 9718 | canvas._chartjs = { |
| 9719 | initial: { |
| 9720 | height: renderHeight, |
| 9721 | width: renderWidth, |
| 9722 | style: { |
| 9723 | display: style.display, |
| 9724 | height: style.height, |
| 9725 | width: style.width |
| 9726 | } |
| 9727 | } |
| 9728 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9729 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9730 | // Force canvas to display as block to avoid extra space caused by inline |
| 9731 | // elements, which would interfere with the responsive resize process. |
| 9732 | // https://github.com/chartjs/Chart.js/issues/2538 |
| 9733 | style.display = style.display || 'block'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9734 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9735 | if (renderWidth === null || renderWidth === '') { |
| 9736 | var displayWidth = readUsedSize(canvas, 'width'); |
| 9737 | if (displayWidth !== undefined) { |
| 9738 | canvas.width = displayWidth; |
| 9739 | } |
| 9740 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9741 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9742 | if (renderHeight === null || renderHeight === '') { |
| 9743 | if (canvas.style.height === '') { |
| 9744 | // If no explicit render height and style height, let's apply the aspect ratio, |
| 9745 | // which one can be specified by the user but also by charts as default option |
| 9746 | // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. |
| 9747 | canvas.height = canvas.width / (config.options.aspectRatio || 2); |
| 9748 | } else { |
| 9749 | var displayHeight = readUsedSize(canvas, 'height'); |
| 9750 | if (displayWidth !== undefined) { |
| 9751 | canvas.height = displayHeight; |
| 9752 | } |
| 9753 | } |
| 9754 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9755 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9756 | return canvas; |
| 9757 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9758 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9759 | function createEvent(type, chart, x, y, nativeEvent) { |
| 9760 | return { |
| 9761 | type: type, |
| 9762 | chart: chart, |
| 9763 | native: nativeEvent || null, |
| 9764 | x: x !== undefined? x : null, |
| 9765 | y: y !== undefined? y : null, |
| 9766 | }; |
| 9767 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9768 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9769 | function fromNativeEvent(event, chart) { |
| 9770 | var type = eventTypeMap[event.type] || event.type; |
| 9771 | var pos = helpers.getRelativePosition(event, chart); |
| 9772 | return createEvent(type, chart, pos.x, pos.y, event); |
| 9773 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9774 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9775 | function createResizer(handler) { |
| 9776 | var iframe = document.createElement('iframe'); |
| 9777 | iframe.className = 'chartjs-hidden-iframe'; |
| 9778 | iframe.style.cssText = |
| 9779 | 'display:block;'+ |
| 9780 | 'overflow:hidden;'+ |
| 9781 | 'border:0;'+ |
| 9782 | 'margin:0;'+ |
| 9783 | 'top:0;'+ |
| 9784 | 'left:0;'+ |
| 9785 | 'bottom:0;'+ |
| 9786 | 'right:0;'+ |
| 9787 | 'height:100%;'+ |
| 9788 | 'width:100%;'+ |
| 9789 | 'position:absolute;'+ |
| 9790 | 'pointer-events:none;'+ |
| 9791 | 'z-index:-1;'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9792 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9793 | // Prevent the iframe to gain focus on tab. |
| 9794 | // https://github.com/chartjs/Chart.js/issues/3090 |
| 9795 | iframe.tabIndex = -1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9796 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9797 | // If the iframe is re-attached to the DOM, the resize listener is removed because the |
| 9798 | // content is reloaded, so make sure to install the handler after the iframe is loaded. |
| 9799 | // https://github.com/chartjs/Chart.js/issues/3521 |
| 9800 | helpers.addEvent(iframe, 'load', function() { |
| 9801 | helpers.addEvent(iframe.contentWindow || iframe, 'resize', handler); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9802 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9803 | // The iframe size might have changed while loading, which can also |
| 9804 | // happen if the size has been changed while detached from the DOM. |
| 9805 | handler(); |
| 9806 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9807 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9808 | return iframe; |
| 9809 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9810 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9811 | function addResizeListener(node, listener, chart) { |
| 9812 | var stub = node._chartjs = { |
| 9813 | ticking: false |
| 9814 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9815 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9816 | // Throttle the callback notification until the next animation frame. |
| 9817 | var notify = function() { |
| 9818 | if (!stub.ticking) { |
| 9819 | stub.ticking = true; |
| 9820 | helpers.requestAnimFrame.call(window, function() { |
| 9821 | if (stub.resizer) { |
| 9822 | stub.ticking = false; |
| 9823 | return listener(createEvent('resize', chart)); |
| 9824 | } |
| 9825 | }); |
| 9826 | } |
| 9827 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9828 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9829 | // Let's keep track of this added iframe and thus avoid DOM query when removing it. |
| 9830 | stub.resizer = createResizer(notify); |
| 9831 | |
| 9832 | node.insertBefore(stub.resizer, node.firstChild); |
| 9833 | } |
| 9834 | |
| 9835 | function removeResizeListener(node) { |
| 9836 | if (!node || !node._chartjs) { |
| 9837 | return; |
| 9838 | } |
| 9839 | |
| 9840 | var resizer = node._chartjs.resizer; |
| 9841 | if (resizer) { |
| 9842 | resizer.parentNode.removeChild(resizer); |
| 9843 | node._chartjs.resizer = null; |
| 9844 | } |
| 9845 | |
| 9846 | delete node._chartjs; |
| 9847 | } |
| 9848 | |
| 9849 | return { |
| 9850 | acquireContext: function(item, config) { |
| 9851 | if (typeof item === 'string') { |
| 9852 | item = document.getElementById(item); |
| 9853 | } else if (item.length) { |
| 9854 | // Support for array based queries (such as jQuery) |
| 9855 | item = item[0]; |
| 9856 | } |
| 9857 | |
| 9858 | if (item && item.canvas) { |
| 9859 | // Support for any object associated to a canvas (including a context2d) |
| 9860 | item = item.canvas; |
| 9861 | } |
| 9862 | |
| 9863 | // To prevent canvas fingerprinting, some add-ons undefine the getContext |
| 9864 | // method, for example: https://github.com/kkapsner/CanvasBlocker |
| 9865 | // https://github.com/chartjs/Chart.js/issues/2807 |
| 9866 | var context = item && item.getContext && item.getContext('2d'); |
| 9867 | |
| 9868 | // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is |
| 9869 | // inside an iframe or when running in a protected environment. We could guess the |
| 9870 | // types from their toString() value but let's keep things flexible and assume it's |
| 9871 | // a sufficient condition if the item has a context2D which has item as `canvas`. |
| 9872 | // https://github.com/chartjs/Chart.js/issues/3887 |
| 9873 | // https://github.com/chartjs/Chart.js/issues/4102 |
| 9874 | // https://github.com/chartjs/Chart.js/issues/4152 |
| 9875 | if (context && context.canvas === item) { |
| 9876 | initCanvas(item, config); |
| 9877 | return context; |
| 9878 | } |
| 9879 | |
| 9880 | return null; |
| 9881 | }, |
| 9882 | |
| 9883 | releaseContext: function(context) { |
| 9884 | var canvas = context.canvas; |
| 9885 | if (!canvas._chartjs) { |
| 9886 | return; |
| 9887 | } |
| 9888 | |
| 9889 | var initial = canvas._chartjs.initial; |
| 9890 | ['height', 'width'].forEach(function(prop) { |
| 9891 | var value = initial[prop]; |
| 9892 | if (value === undefined || value === null) { |
| 9893 | canvas.removeAttribute(prop); |
| 9894 | } else { |
| 9895 | canvas.setAttribute(prop, value); |
| 9896 | } |
| 9897 | }); |
| 9898 | |
| 9899 | helpers.each(initial.style || {}, function(value, key) { |
| 9900 | canvas.style[key] = value; |
| 9901 | }); |
| 9902 | |
| 9903 | // The canvas render size might have been changed (and thus the state stack discarded), |
| 9904 | // we can't use save() and restore() to restore the initial state. So make sure that at |
| 9905 | // least the canvas context is reset to the default state by setting the canvas width. |
| 9906 | // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html |
| 9907 | canvas.width = canvas.width; |
| 9908 | |
| 9909 | delete canvas._chartjs; |
| 9910 | }, |
| 9911 | |
| 9912 | addEventListener: function(chart, type, listener) { |
| 9913 | var canvas = chart.canvas; |
| 9914 | if (type === 'resize') { |
| 9915 | // Note: the resize event is not supported on all browsers. |
| 9916 | addResizeListener(canvas.parentNode, listener, chart); |
| 9917 | return; |
| 9918 | } |
| 9919 | |
| 9920 | var stub = listener._chartjs || (listener._chartjs = {}); |
| 9921 | var proxies = stub.proxies || (stub.proxies = {}); |
| 9922 | var proxy = proxies[chart.id + '_' + type] = function(event) { |
| 9923 | listener(fromNativeEvent(event, chart)); |
| 9924 | }; |
| 9925 | |
| 9926 | helpers.addEvent(canvas, type, proxy); |
| 9927 | }, |
| 9928 | |
| 9929 | removeEventListener: function(chart, type, listener) { |
| 9930 | var canvas = chart.canvas; |
| 9931 | if (type === 'resize') { |
| 9932 | // Note: the resize event is not supported on all browsers. |
| 9933 | removeResizeListener(canvas.parentNode, listener); |
| 9934 | return; |
| 9935 | } |
| 9936 | |
| 9937 | var stub = listener._chartjs || {}; |
| 9938 | var proxies = stub.proxies || {}; |
| 9939 | var proxy = proxies[chart.id + '_' + type]; |
| 9940 | if (!proxy) { |
| 9941 | return; |
| 9942 | } |
| 9943 | |
| 9944 | helpers.removeEvent(canvas, type, proxy); |
| 9945 | } |
| 9946 | }; |
| 9947 | }; |
| 9948 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9949 | },{}],40:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9950 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9951 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9952 | // By default, select the browser (DOM) platform. |
| 9953 | // @TODO Make possible to select another platform at build time. |
| 9954 | var implementation = require(39); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9955 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9956 | module.exports = function(Chart) { |
| 9957 | /** |
| 9958 | * @namespace Chart.platform |
| 9959 | * @see https://chartjs.gitbooks.io/proposals/content/Platform.html |
| 9960 | * @since 2.4.0 |
| 9961 | */ |
| 9962 | Chart.platform = { |
| 9963 | /** |
| 9964 | * Called at chart construction time, returns a context2d instance implementing |
| 9965 | * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. |
| 9966 | * @param {*} item - The native item from which to acquire context (platform specific) |
| 9967 | * @param {Object} options - The chart options |
| 9968 | * @returns {CanvasRenderingContext2D} context2d instance |
| 9969 | */ |
| 9970 | acquireContext: function() {}, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9971 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9972 | /** |
| 9973 | * Called at chart destruction time, releases any resources associated to the context |
| 9974 | * previously returned by the acquireContext() method. |
| 9975 | * @param {CanvasRenderingContext2D} context - The context2d instance |
| 9976 | * @returns {Boolean} true if the method succeeded, else false |
| 9977 | */ |
| 9978 | releaseContext: function() {}, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9979 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9980 | /** |
| 9981 | * Registers the specified listener on the given chart. |
| 9982 | * @param {Chart} chart - Chart from which to listen for event |
| 9983 | * @param {String} type - The ({@link IEvent}) type to listen for |
| 9984 | * @param {Function} listener - Receives a notification (an object that implements |
| 9985 | * the {@link IEvent} interface) when an event of the specified type occurs. |
| 9986 | */ |
| 9987 | addEventListener: function() {}, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9988 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9989 | /** |
| 9990 | * Removes the specified listener previously registered with addEventListener. |
| 9991 | * @param {Chart} chart -Chart from which to remove the listener |
| 9992 | * @param {String} type - The ({@link IEvent}) type to remove |
| 9993 | * @param {Function} listener - The listener function to remove from the event target. |
| 9994 | */ |
| 9995 | removeEventListener: function() {} |
| 9996 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 9997 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 9998 | /** |
| 9999 | * @interface IPlatform |
| 10000 | * Allows abstracting platform dependencies away from the chart |
| 10001 | * @borrows Chart.platform.acquireContext as acquireContext |
| 10002 | * @borrows Chart.platform.releaseContext as releaseContext |
| 10003 | * @borrows Chart.platform.addEventListener as addEventListener |
| 10004 | * @borrows Chart.platform.removeEventListener as removeEventListener |
| 10005 | */ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10006 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10007 | /** |
| 10008 | * @interface IEvent |
| 10009 | * @prop {String} type - The event type name, possible values are: |
| 10010 | * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout', |
| 10011 | * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' |
| 10012 | * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') |
| 10013 | * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) |
| 10014 | * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) |
| 10015 | */ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10016 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10017 | Chart.helpers.extend(Chart.platform, implementation(Chart)); |
| 10018 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10019 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10020 | },{"39":39}],41:[function(require,module,exports){ |
| 10021 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10022 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10023 | module.exports = function(Chart) { |
| 10024 | /** |
| 10025 | * Plugin based on discussion from the following Chart.js issues: |
| 10026 | * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 |
| 10027 | * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 |
| 10028 | */ |
| 10029 | Chart.defaults.global.plugins.filler = { |
| 10030 | propagate: true |
| 10031 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10032 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10033 | var defaults = Chart.defaults; |
| 10034 | var helpers = Chart.helpers; |
| 10035 | var mappers = { |
| 10036 | dataset: function(source) { |
| 10037 | var index = source.fill; |
| 10038 | var chart = source.chart; |
| 10039 | var meta = chart.getDatasetMeta(index); |
| 10040 | var visible = meta && chart.isDatasetVisible(index); |
| 10041 | var points = (visible && meta.dataset._children) || []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10042 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10043 | return !points.length? null : function(point, i) { |
| 10044 | return points[i]._view || null; |
| 10045 | }; |
| 10046 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10047 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10048 | boundary: function(source) { |
| 10049 | var boundary = source.boundary; |
| 10050 | var x = boundary? boundary.x : null; |
| 10051 | var y = boundary? boundary.y : null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10052 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10053 | return function(point) { |
| 10054 | return { |
| 10055 | x: x === null? point.x : x, |
| 10056 | y: y === null? point.y : y, |
| 10057 | }; |
| 10058 | }; |
| 10059 | } |
| 10060 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10061 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10062 | // @todo if (fill[0] === '#') |
| 10063 | function decodeFill(el, index, count) { |
| 10064 | var model = el._model || {}; |
| 10065 | var fill = model.fill; |
| 10066 | var target; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10067 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10068 | if (fill === undefined) { |
| 10069 | fill = !!model.backgroundColor; |
| 10070 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10071 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10072 | if (fill === false || fill === null) { |
| 10073 | return false; |
| 10074 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10075 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10076 | if (fill === true) { |
| 10077 | return 'origin'; |
| 10078 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10079 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10080 | target = parseFloat(fill, 10); |
| 10081 | if (isFinite(target) && Math.floor(target) === target) { |
| 10082 | if (fill[0] === '-' || fill[0] === '+') { |
| 10083 | target = index + target; |
| 10084 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10085 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10086 | if (target === index || target < 0 || target >= count) { |
| 10087 | return false; |
| 10088 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10089 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10090 | return target; |
| 10091 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10092 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10093 | switch (fill) { |
| 10094 | // compatibility |
| 10095 | case 'bottom': |
| 10096 | return 'start'; |
| 10097 | case 'top': |
| 10098 | return 'end'; |
| 10099 | case 'zero': |
| 10100 | return 'origin'; |
| 10101 | // supported boundaries |
| 10102 | case 'origin': |
| 10103 | case 'start': |
| 10104 | case 'end': |
| 10105 | return fill; |
| 10106 | // invalid fill values |
| 10107 | default: |
| 10108 | return false; |
| 10109 | } |
| 10110 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10111 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10112 | function computeBoundary(source) { |
| 10113 | var model = source.el._model || {}; |
| 10114 | var scale = source.el._scale || {}; |
| 10115 | var fill = source.fill; |
| 10116 | var target = null; |
| 10117 | var horizontal; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10118 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10119 | if (isFinite(fill)) { |
| 10120 | return null; |
| 10121 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10122 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10123 | // Backward compatibility: until v3, we still need to support boundary values set on |
| 10124 | // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and |
| 10125 | // controllers might still use it (e.g. the Smith chart). |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10126 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10127 | if (fill === 'start') { |
| 10128 | target = model.scaleBottom === undefined? scale.bottom : model.scaleBottom; |
| 10129 | } else if (fill === 'end') { |
| 10130 | target = model.scaleTop === undefined? scale.top : model.scaleTop; |
| 10131 | } else if (model.scaleZero !== undefined) { |
| 10132 | target = model.scaleZero; |
| 10133 | } else if (scale.getBasePosition) { |
| 10134 | target = scale.getBasePosition(); |
| 10135 | } else if (scale.getBasePixel) { |
| 10136 | target = scale.getBasePixel(); |
| 10137 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10138 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10139 | if (target !== undefined && target !== null) { |
| 10140 | if (target.x !== undefined && target.y !== undefined) { |
| 10141 | return target; |
| 10142 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10143 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10144 | if (typeof target === 'number' && isFinite(target)) { |
| 10145 | horizontal = scale.isHorizontal(); |
| 10146 | return { |
| 10147 | x: horizontal? target : null, |
| 10148 | y: horizontal? null : target |
| 10149 | }; |
| 10150 | } |
| 10151 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10152 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10153 | return null; |
| 10154 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10155 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10156 | function resolveTarget(sources, index, propagate) { |
| 10157 | var source = sources[index]; |
| 10158 | var fill = source.fill; |
| 10159 | var visited = [index]; |
| 10160 | var target; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10161 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10162 | if (!propagate) { |
| 10163 | return fill; |
| 10164 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10165 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10166 | while (fill !== false && visited.indexOf(fill) === -1) { |
| 10167 | if (!isFinite(fill)) { |
| 10168 | return fill; |
| 10169 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10170 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10171 | target = sources[fill]; |
| 10172 | if (!target) { |
| 10173 | return false; |
| 10174 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10175 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10176 | if (target.visible) { |
| 10177 | return fill; |
| 10178 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10179 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10180 | visited.push(fill); |
| 10181 | fill = target.fill; |
| 10182 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10183 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10184 | return false; |
| 10185 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10186 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10187 | function createMapper(source) { |
| 10188 | var fill = source.fill; |
| 10189 | var type = 'dataset'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10190 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10191 | if (fill === false) { |
| 10192 | return null; |
| 10193 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10194 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10195 | if (!isFinite(fill)) { |
| 10196 | type = 'boundary'; |
| 10197 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10198 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10199 | return mappers[type](source); |
| 10200 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10201 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10202 | function isDrawable(point) { |
| 10203 | return point && !point.skip; |
| 10204 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10205 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10206 | function drawArea(ctx, curve0, curve1, len0, len1) { |
| 10207 | var i; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10208 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10209 | if (!len0 || !len1) { |
| 10210 | return; |
| 10211 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10212 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10213 | // building first area curve (normal) |
| 10214 | ctx.moveTo(curve0[0].x, curve0[0].y); |
| 10215 | for (i=1; i<len0; ++i) { |
| 10216 | helpers.canvas.lineTo(ctx, curve0[i-1], curve0[i]); |
| 10217 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10218 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10219 | // joining the two area curves |
| 10220 | ctx.lineTo(curve1[len1-1].x, curve1[len1-1].y); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10221 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10222 | // building opposite area curve (reverse) |
| 10223 | for (i=len1-1; i>0; --i) { |
| 10224 | helpers.canvas.lineTo(ctx, curve1[i], curve1[i-1], true); |
| 10225 | } |
| 10226 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10227 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10228 | function doFill(ctx, points, mapper, view, color, loop) { |
| 10229 | var count = points.length; |
| 10230 | var span = view.spanGaps; |
| 10231 | var curve0 = []; |
| 10232 | var curve1 = []; |
| 10233 | var len0 = 0; |
| 10234 | var len1 = 0; |
| 10235 | var i, ilen, index, p0, p1, d0, d1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10236 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10237 | ctx.beginPath(); |
| 10238 | |
| 10239 | for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { |
| 10240 | index = i%count; |
| 10241 | p0 = points[index]._view; |
| 10242 | p1 = mapper(p0, index, view); |
| 10243 | d0 = isDrawable(p0); |
| 10244 | d1 = isDrawable(p1); |
| 10245 | |
| 10246 | if (d0 && d1) { |
| 10247 | len0 = curve0.push(p0); |
| 10248 | len1 = curve1.push(p1); |
| 10249 | } else if (len0 && len1) { |
| 10250 | if (!span) { |
| 10251 | drawArea(ctx, curve0, curve1, len0, len1); |
| 10252 | len0 = len1 = 0; |
| 10253 | curve0 = []; |
| 10254 | curve1 = []; |
| 10255 | } else { |
| 10256 | if (d0) { |
| 10257 | curve0.push(p0); |
| 10258 | } |
| 10259 | if (d1) { |
| 10260 | curve1.push(p1); |
| 10261 | } |
| 10262 | } |
| 10263 | } |
| 10264 | } |
| 10265 | |
| 10266 | drawArea(ctx, curve0, curve1, len0, len1); |
| 10267 | |
| 10268 | ctx.closePath(); |
| 10269 | ctx.fillStyle = color; |
| 10270 | ctx.fill(); |
| 10271 | } |
| 10272 | |
| 10273 | return { |
| 10274 | id: 'filler', |
| 10275 | |
| 10276 | afterDatasetsUpdate: function(chart, options) { |
| 10277 | var count = (chart.data.datasets || []).length; |
| 10278 | var propagate = options.propagate; |
| 10279 | var sources = []; |
| 10280 | var meta, i, el, source; |
| 10281 | |
| 10282 | for (i = 0; i < count; ++i) { |
| 10283 | meta = chart.getDatasetMeta(i); |
| 10284 | el = meta.dataset; |
| 10285 | source = null; |
| 10286 | |
| 10287 | if (el && el._model && el instanceof Chart.elements.Line) { |
| 10288 | source = { |
| 10289 | visible: chart.isDatasetVisible(i), |
| 10290 | fill: decodeFill(el, i, count), |
| 10291 | chart: chart, |
| 10292 | el: el |
| 10293 | }; |
| 10294 | } |
| 10295 | |
| 10296 | meta.$filler = source; |
| 10297 | sources.push(source); |
| 10298 | } |
| 10299 | |
| 10300 | for (i=0; i<count; ++i) { |
| 10301 | source = sources[i]; |
| 10302 | if (!source) { |
| 10303 | continue; |
| 10304 | } |
| 10305 | |
| 10306 | source.fill = resolveTarget(sources, i, propagate); |
| 10307 | source.boundary = computeBoundary(source); |
| 10308 | source.mapper = createMapper(source); |
| 10309 | } |
| 10310 | }, |
| 10311 | |
| 10312 | beforeDatasetDraw: function(chart, args) { |
| 10313 | var meta = args.meta.$filler; |
| 10314 | if (!meta) { |
| 10315 | return; |
| 10316 | } |
| 10317 | |
| 10318 | var el = meta.el; |
| 10319 | var view = el._view; |
| 10320 | var points = el._children || []; |
| 10321 | var mapper = meta.mapper; |
| 10322 | var color = view.backgroundColor || defaults.global.defaultColor; |
| 10323 | |
| 10324 | if (mapper && color && points.length) { |
| 10325 | doFill(chart.ctx, points, mapper, view, color, el._loop); |
| 10326 | } |
| 10327 | } |
| 10328 | }; |
| 10329 | }; |
| 10330 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10331 | },{}],42:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10332 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10333 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10334 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10335 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10336 | var helpers = Chart.helpers; |
| 10337 | var layout = Chart.layoutService; |
| 10338 | var noop = helpers.noop; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10339 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10340 | Chart.defaults.global.legend = { |
| 10341 | display: true, |
| 10342 | position: 'top', |
| 10343 | fullWidth: true, |
| 10344 | reverse: false, |
| 10345 | weight: 1000, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10346 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10347 | // a callback that will handle |
| 10348 | onClick: function(e, legendItem) { |
| 10349 | var index = legendItem.datasetIndex; |
| 10350 | var ci = this.chart; |
| 10351 | var meta = ci.getDatasetMeta(index); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10352 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10353 | // See controller.isDatasetVisible comment |
| 10354 | meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10355 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10356 | // We hid a dataset ... rerender the chart |
| 10357 | ci.update(); |
| 10358 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10359 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10360 | onHover: null, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10361 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10362 | labels: { |
| 10363 | boxWidth: 40, |
| 10364 | padding: 10, |
| 10365 | // Generates labels shown in the legend |
| 10366 | // Valid properties to return: |
| 10367 | // text : text to display |
| 10368 | // fillStyle : fill of coloured box |
| 10369 | // strokeStyle: stroke of coloured box |
| 10370 | // hidden : if this legend item refers to a hidden item |
| 10371 | // lineCap : cap style for line |
| 10372 | // lineDash |
| 10373 | // lineDashOffset : |
| 10374 | // lineJoin : |
| 10375 | // lineWidth : |
| 10376 | generateLabels: function(chart) { |
| 10377 | var data = chart.data; |
| 10378 | return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { |
| 10379 | return { |
| 10380 | text: dataset.label, |
| 10381 | fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), |
| 10382 | hidden: !chart.isDatasetVisible(i), |
| 10383 | lineCap: dataset.borderCapStyle, |
| 10384 | lineDash: dataset.borderDash, |
| 10385 | lineDashOffset: dataset.borderDashOffset, |
| 10386 | lineJoin: dataset.borderJoinStyle, |
| 10387 | lineWidth: dataset.borderWidth, |
| 10388 | strokeStyle: dataset.borderColor, |
| 10389 | pointStyle: dataset.pointStyle, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10390 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10391 | // Below is extra data used for toggling the datasets |
| 10392 | datasetIndex: i |
| 10393 | }; |
| 10394 | }, this) : []; |
| 10395 | } |
| 10396 | } |
| 10397 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10398 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10399 | /** |
| 10400 | * Helper function to get the box width based on the usePointStyle option |
| 10401 | * @param labelopts {Object} the label options on the legend |
| 10402 | * @param fontSize {Number} the label font size |
| 10403 | * @return {Number} width of the color box area |
| 10404 | */ |
| 10405 | function getBoxWidth(labelOpts, fontSize) { |
| 10406 | return labelOpts.usePointStyle ? |
| 10407 | fontSize * Math.SQRT2 : |
| 10408 | labelOpts.boxWidth; |
| 10409 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10410 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10411 | Chart.Legend = Chart.Element.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10412 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10413 | initialize: function(config) { |
| 10414 | helpers.extend(this, config); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10415 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10416 | // Contains hit boxes for each dataset (in dataset order) |
| 10417 | this.legendHitBoxes = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10418 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10419 | // Are we in doughnut mode which has a different data type |
| 10420 | this.doughnutMode = false; |
| 10421 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10422 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10423 | // These methods are ordered by lifecycle. Utilities then follow. |
| 10424 | // Any function defined here is inherited by all legend types. |
| 10425 | // Any function can be extended by the legend type |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10426 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10427 | beforeUpdate: noop, |
| 10428 | update: function(maxWidth, maxHeight, margins) { |
| 10429 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10430 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10431 | // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
| 10432 | me.beforeUpdate(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10433 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10434 | // Absorb the master measurements |
| 10435 | me.maxWidth = maxWidth; |
| 10436 | me.maxHeight = maxHeight; |
| 10437 | me.margins = margins; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10438 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10439 | // Dimensions |
| 10440 | me.beforeSetDimensions(); |
| 10441 | me.setDimensions(); |
| 10442 | me.afterSetDimensions(); |
| 10443 | // Labels |
| 10444 | me.beforeBuildLabels(); |
| 10445 | me.buildLabels(); |
| 10446 | me.afterBuildLabels(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10447 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10448 | // Fit |
| 10449 | me.beforeFit(); |
| 10450 | me.fit(); |
| 10451 | me.afterFit(); |
| 10452 | // |
| 10453 | me.afterUpdate(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10454 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10455 | return me.minSize; |
| 10456 | }, |
| 10457 | afterUpdate: noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10458 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10459 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10460 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10461 | beforeSetDimensions: noop, |
| 10462 | setDimensions: function() { |
| 10463 | var me = this; |
| 10464 | // Set the unconstrained dimension before label rotation |
| 10465 | if (me.isHorizontal()) { |
| 10466 | // Reset position before calculating rotation |
| 10467 | me.width = me.maxWidth; |
| 10468 | me.left = 0; |
| 10469 | me.right = me.width; |
| 10470 | } else { |
| 10471 | me.height = me.maxHeight; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10472 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10473 | // Reset position before calculating rotation |
| 10474 | me.top = 0; |
| 10475 | me.bottom = me.height; |
| 10476 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10477 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10478 | // Reset padding |
| 10479 | me.paddingLeft = 0; |
| 10480 | me.paddingTop = 0; |
| 10481 | me.paddingRight = 0; |
| 10482 | me.paddingBottom = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10483 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10484 | // Reset minSize |
| 10485 | me.minSize = { |
| 10486 | width: 0, |
| 10487 | height: 0 |
| 10488 | }; |
| 10489 | }, |
| 10490 | afterSetDimensions: noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10491 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10492 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10493 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10494 | beforeBuildLabels: noop, |
| 10495 | buildLabels: function() { |
| 10496 | var me = this; |
| 10497 | var labelOpts = me.options.labels; |
| 10498 | var legendItems = labelOpts.generateLabels.call(me, me.chart); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10499 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10500 | if (labelOpts.filter) { |
| 10501 | legendItems = legendItems.filter(function(item) { |
| 10502 | return labelOpts.filter(item, me.chart.data); |
| 10503 | }); |
| 10504 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10505 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10506 | if (me.options.reverse) { |
| 10507 | legendItems.reverse(); |
| 10508 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10509 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10510 | me.legendItems = legendItems; |
| 10511 | }, |
| 10512 | afterBuildLabels: noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10513 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10514 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10515 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10516 | beforeFit: noop, |
| 10517 | fit: function() { |
| 10518 | var me = this; |
| 10519 | var opts = me.options; |
| 10520 | var labelOpts = opts.labels; |
| 10521 | var display = opts.display; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10522 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10523 | var ctx = me.ctx; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10524 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10525 | var globalDefault = Chart.defaults.global, |
| 10526 | itemOrDefault = helpers.getValueOrDefault, |
| 10527 | fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize), |
| 10528 | fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle), |
| 10529 | fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily), |
| 10530 | labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10531 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10532 | // Reset hit boxes |
| 10533 | var hitboxes = me.legendHitBoxes = []; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10534 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10535 | var minSize = me.minSize; |
| 10536 | var isHorizontal = me.isHorizontal(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10537 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10538 | if (isHorizontal) { |
| 10539 | minSize.width = me.maxWidth; // fill all the width |
| 10540 | minSize.height = display ? 10 : 0; |
| 10541 | } else { |
| 10542 | minSize.width = display ? 10 : 0; |
| 10543 | minSize.height = me.maxHeight; // fill all the height |
| 10544 | } |
| 10545 | |
| 10546 | // Increase sizes here |
| 10547 | if (display) { |
| 10548 | ctx.font = labelFont; |
| 10549 | |
| 10550 | if (isHorizontal) { |
| 10551 | // Labels |
| 10552 | |
| 10553 | // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one |
| 10554 | var lineWidths = me.lineWidths = [0]; |
| 10555 | var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; |
| 10556 | |
| 10557 | ctx.textAlign = 'left'; |
| 10558 | ctx.textBaseline = 'top'; |
| 10559 | |
| 10560 | helpers.each(me.legendItems, function(legendItem, i) { |
| 10561 | var boxWidth = getBoxWidth(labelOpts, fontSize); |
| 10562 | var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; |
| 10563 | |
| 10564 | if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { |
| 10565 | totalHeight += fontSize + (labelOpts.padding); |
| 10566 | lineWidths[lineWidths.length] = me.left; |
| 10567 | } |
| 10568 | |
| 10569 | // Store the hitbox width and height here. Final position will be updated in `draw` |
| 10570 | hitboxes[i] = { |
| 10571 | left: 0, |
| 10572 | top: 0, |
| 10573 | width: width, |
| 10574 | height: fontSize |
| 10575 | }; |
| 10576 | |
| 10577 | lineWidths[lineWidths.length - 1] += width + labelOpts.padding; |
| 10578 | }); |
| 10579 | |
| 10580 | minSize.height += totalHeight; |
| 10581 | |
| 10582 | } else { |
| 10583 | var vPadding = labelOpts.padding; |
| 10584 | var columnWidths = me.columnWidths = []; |
| 10585 | var totalWidth = labelOpts.padding; |
| 10586 | var currentColWidth = 0; |
| 10587 | var currentColHeight = 0; |
| 10588 | var itemHeight = fontSize + vPadding; |
| 10589 | |
| 10590 | helpers.each(me.legendItems, function(legendItem, i) { |
| 10591 | var boxWidth = getBoxWidth(labelOpts, fontSize); |
| 10592 | var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; |
| 10593 | |
| 10594 | // If too tall, go to new column |
| 10595 | if (currentColHeight + itemHeight > minSize.height) { |
| 10596 | totalWidth += currentColWidth + labelOpts.padding; |
| 10597 | columnWidths.push(currentColWidth); // previous column width |
| 10598 | |
| 10599 | currentColWidth = 0; |
| 10600 | currentColHeight = 0; |
| 10601 | } |
| 10602 | |
| 10603 | // Get max width |
| 10604 | currentColWidth = Math.max(currentColWidth, itemWidth); |
| 10605 | currentColHeight += itemHeight; |
| 10606 | |
| 10607 | // Store the hitbox width and height here. Final position will be updated in `draw` |
| 10608 | hitboxes[i] = { |
| 10609 | left: 0, |
| 10610 | top: 0, |
| 10611 | width: itemWidth, |
| 10612 | height: fontSize |
| 10613 | }; |
| 10614 | }); |
| 10615 | |
| 10616 | totalWidth += currentColWidth; |
| 10617 | columnWidths.push(currentColWidth); |
| 10618 | minSize.width += totalWidth; |
| 10619 | } |
| 10620 | } |
| 10621 | |
| 10622 | me.width = minSize.width; |
| 10623 | me.height = minSize.height; |
| 10624 | }, |
| 10625 | afterFit: noop, |
| 10626 | |
| 10627 | // Shared Methods |
| 10628 | isHorizontal: function() { |
| 10629 | return this.options.position === 'top' || this.options.position === 'bottom'; |
| 10630 | }, |
| 10631 | |
| 10632 | // Actually draw the legend on the canvas |
| 10633 | draw: function() { |
| 10634 | var me = this; |
| 10635 | var opts = me.options; |
| 10636 | var labelOpts = opts.labels; |
| 10637 | var globalDefault = Chart.defaults.global, |
| 10638 | lineDefault = globalDefault.elements.line, |
| 10639 | legendWidth = me.width, |
| 10640 | lineWidths = me.lineWidths; |
| 10641 | |
| 10642 | if (opts.display) { |
| 10643 | var ctx = me.ctx, |
| 10644 | cursor, |
| 10645 | itemOrDefault = helpers.getValueOrDefault, |
| 10646 | fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor), |
| 10647 | fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize), |
| 10648 | fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle), |
| 10649 | fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily), |
| 10650 | labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); |
| 10651 | |
| 10652 | // Canvas setup |
| 10653 | ctx.textAlign = 'left'; |
| 10654 | ctx.textBaseline = 'top'; |
| 10655 | ctx.lineWidth = 0.5; |
| 10656 | ctx.strokeStyle = fontColor; // for strikethrough effect |
| 10657 | ctx.fillStyle = fontColor; // render in correct colour |
| 10658 | ctx.font = labelFont; |
| 10659 | |
| 10660 | var boxWidth = getBoxWidth(labelOpts, fontSize), |
| 10661 | hitboxes = me.legendHitBoxes; |
| 10662 | |
| 10663 | // current position |
| 10664 | var drawLegendBox = function(x, y, legendItem) { |
| 10665 | if (isNaN(boxWidth) || boxWidth <= 0) { |
| 10666 | return; |
| 10667 | } |
| 10668 | |
| 10669 | // Set the ctx for the box |
| 10670 | ctx.save(); |
| 10671 | |
| 10672 | ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor); |
| 10673 | ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); |
| 10674 | ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); |
| 10675 | ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); |
| 10676 | ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth); |
| 10677 | ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); |
| 10678 | var isLineWidthZero = (itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); |
| 10679 | |
| 10680 | if (ctx.setLineDash) { |
| 10681 | // IE 9 and 10 do not support line dash |
| 10682 | ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash)); |
| 10683 | } |
| 10684 | |
| 10685 | if (opts.labels && opts.labels.usePointStyle) { |
| 10686 | // Recalculate x and y for drawPoint() because its expecting |
| 10687 | // x and y to be center of figure (instead of top left) |
| 10688 | var radius = fontSize * Math.SQRT2 / 2; |
| 10689 | var offSet = radius / Math.SQRT2; |
| 10690 | var centerX = x + offSet; |
| 10691 | var centerY = y + offSet; |
| 10692 | |
| 10693 | // Draw pointStyle as legend symbol |
| 10694 | Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); |
| 10695 | } else { |
| 10696 | // Draw box as legend symbol |
| 10697 | if (!isLineWidthZero) { |
| 10698 | ctx.strokeRect(x, y, boxWidth, fontSize); |
| 10699 | } |
| 10700 | ctx.fillRect(x, y, boxWidth, fontSize); |
| 10701 | } |
| 10702 | |
| 10703 | ctx.restore(); |
| 10704 | }; |
| 10705 | var fillText = function(x, y, legendItem, textWidth) { |
| 10706 | ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y); |
| 10707 | |
| 10708 | if (legendItem.hidden) { |
| 10709 | // Strikethrough the text if hidden |
| 10710 | ctx.beginPath(); |
| 10711 | ctx.lineWidth = 2; |
| 10712 | ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2)); |
| 10713 | ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2)); |
| 10714 | ctx.stroke(); |
| 10715 | } |
| 10716 | }; |
| 10717 | |
| 10718 | // Horizontal |
| 10719 | var isHorizontal = me.isHorizontal(); |
| 10720 | if (isHorizontal) { |
| 10721 | cursor = { |
| 10722 | x: me.left + ((legendWidth - lineWidths[0]) / 2), |
| 10723 | y: me.top + labelOpts.padding, |
| 10724 | line: 0 |
| 10725 | }; |
| 10726 | } else { |
| 10727 | cursor = { |
| 10728 | x: me.left + labelOpts.padding, |
| 10729 | y: me.top + labelOpts.padding, |
| 10730 | line: 0 |
| 10731 | }; |
| 10732 | } |
| 10733 | |
| 10734 | var itemHeight = fontSize + labelOpts.padding; |
| 10735 | helpers.each(me.legendItems, function(legendItem, i) { |
| 10736 | var textWidth = ctx.measureText(legendItem.text).width, |
| 10737 | width = boxWidth + (fontSize / 2) + textWidth, |
| 10738 | x = cursor.x, |
| 10739 | y = cursor.y; |
| 10740 | |
| 10741 | if (isHorizontal) { |
| 10742 | if (x + width >= legendWidth) { |
| 10743 | y = cursor.y += itemHeight; |
| 10744 | cursor.line++; |
| 10745 | x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); |
| 10746 | } |
| 10747 | } else if (y + itemHeight > me.bottom) { |
| 10748 | x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; |
| 10749 | y = cursor.y = me.top + labelOpts.padding; |
| 10750 | cursor.line++; |
| 10751 | } |
| 10752 | |
| 10753 | drawLegendBox(x, y, legendItem); |
| 10754 | |
| 10755 | hitboxes[i].left = x; |
| 10756 | hitboxes[i].top = y; |
| 10757 | |
| 10758 | // Fill the actual label |
| 10759 | fillText(x, y, legendItem, textWidth); |
| 10760 | |
| 10761 | if (isHorizontal) { |
| 10762 | cursor.x += width + (labelOpts.padding); |
| 10763 | } else { |
| 10764 | cursor.y += itemHeight; |
| 10765 | } |
| 10766 | |
| 10767 | }); |
| 10768 | } |
| 10769 | }, |
| 10770 | |
| 10771 | /** |
| 10772 | * Handle an event |
| 10773 | * @private |
| 10774 | * @param {IEvent} event - The event to handle |
| 10775 | * @return {Boolean} true if a change occured |
| 10776 | */ |
| 10777 | handleEvent: function(e) { |
| 10778 | var me = this; |
| 10779 | var opts = me.options; |
| 10780 | var type = e.type === 'mouseup' ? 'click' : e.type; |
| 10781 | var changed = false; |
| 10782 | |
| 10783 | if (type === 'mousemove') { |
| 10784 | if (!opts.onHover) { |
| 10785 | return; |
| 10786 | } |
| 10787 | } else if (type === 'click') { |
| 10788 | if (!opts.onClick) { |
| 10789 | return; |
| 10790 | } |
| 10791 | } else { |
| 10792 | return; |
| 10793 | } |
| 10794 | |
| 10795 | // Chart event already has relative position in it |
| 10796 | var x = e.x, |
| 10797 | y = e.y; |
| 10798 | |
| 10799 | if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { |
| 10800 | // See if we are touching one of the dataset boxes |
| 10801 | var lh = me.legendHitBoxes; |
| 10802 | for (var i = 0; i < lh.length; ++i) { |
| 10803 | var hitBox = lh[i]; |
| 10804 | |
| 10805 | if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { |
| 10806 | // Touching an element |
| 10807 | if (type === 'click') { |
| 10808 | // use e.native for backwards compatibility |
| 10809 | opts.onClick.call(me, e.native, me.legendItems[i]); |
| 10810 | changed = true; |
| 10811 | break; |
| 10812 | } else if (type === 'mousemove') { |
| 10813 | // use e.native for backwards compatibility |
| 10814 | opts.onHover.call(me, e.native, me.legendItems[i]); |
| 10815 | changed = true; |
| 10816 | break; |
| 10817 | } |
| 10818 | } |
| 10819 | } |
| 10820 | } |
| 10821 | |
| 10822 | return changed; |
| 10823 | } |
| 10824 | }); |
| 10825 | |
| 10826 | function createNewLegendAndAttach(chart, legendOpts) { |
| 10827 | var legend = new Chart.Legend({ |
| 10828 | ctx: chart.ctx, |
| 10829 | options: legendOpts, |
| 10830 | chart: chart |
| 10831 | }); |
| 10832 | |
| 10833 | layout.configure(chart, legend, legendOpts); |
| 10834 | layout.addBox(chart, legend); |
| 10835 | chart.legend = legend; |
| 10836 | } |
| 10837 | |
| 10838 | return { |
| 10839 | id: 'legend', |
| 10840 | |
| 10841 | beforeInit: function(chart) { |
| 10842 | var legendOpts = chart.options.legend; |
| 10843 | |
| 10844 | if (legendOpts) { |
| 10845 | createNewLegendAndAttach(chart, legendOpts); |
| 10846 | } |
| 10847 | }, |
| 10848 | |
| 10849 | beforeUpdate: function(chart) { |
| 10850 | var legendOpts = chart.options.legend; |
| 10851 | var legend = chart.legend; |
| 10852 | |
| 10853 | if (legendOpts) { |
| 10854 | legendOpts = helpers.configMerge(Chart.defaults.global.legend, legendOpts); |
| 10855 | |
| 10856 | if (legend) { |
| 10857 | layout.configure(chart, legend, legendOpts); |
| 10858 | legend.options = legendOpts; |
| 10859 | } else { |
| 10860 | createNewLegendAndAttach(chart, legendOpts); |
| 10861 | } |
| 10862 | } else if (legend) { |
| 10863 | layout.removeBox(chart, legend); |
| 10864 | delete chart.legend; |
| 10865 | } |
| 10866 | }, |
| 10867 | |
| 10868 | afterEvent: function(chart, e) { |
| 10869 | var legend = chart.legend; |
| 10870 | if (legend) { |
| 10871 | legend.handleEvent(e); |
| 10872 | } |
| 10873 | } |
| 10874 | }; |
| 10875 | }; |
| 10876 | |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10877 | },{}],43:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10878 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10879 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10880 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10881 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10882 | var helpers = Chart.helpers; |
| 10883 | var layout = Chart.layoutService; |
| 10884 | var noop = helpers.noop; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10885 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10886 | Chart.defaults.global.title = { |
| 10887 | display: false, |
| 10888 | position: 'top', |
| 10889 | fullWidth: true, |
| 10890 | weight: 2000, // by default greater than legend (1000) to be above |
| 10891 | fontStyle: 'bold', |
| 10892 | padding: 10, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10893 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10894 | // actual title |
| 10895 | text: '' |
| 10896 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10897 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10898 | Chart.Title = Chart.Element.extend({ |
| 10899 | initialize: function(config) { |
| 10900 | var me = this; |
| 10901 | helpers.extend(me, config); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10902 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10903 | // Contains hit boxes for each dataset (in dataset order) |
| 10904 | me.legendHitBoxes = []; |
| 10905 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10906 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10907 | // These methods are ordered by lifecycle. Utilities then follow. |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10908 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10909 | beforeUpdate: noop, |
| 10910 | update: function(maxWidth, maxHeight, margins) { |
| 10911 | var me = this; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10912 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10913 | // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) |
| 10914 | me.beforeUpdate(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10915 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10916 | // Absorb the master measurements |
| 10917 | me.maxWidth = maxWidth; |
| 10918 | me.maxHeight = maxHeight; |
| 10919 | me.margins = margins; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10920 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10921 | // Dimensions |
| 10922 | me.beforeSetDimensions(); |
| 10923 | me.setDimensions(); |
| 10924 | me.afterSetDimensions(); |
| 10925 | // Labels |
| 10926 | me.beforeBuildLabels(); |
| 10927 | me.buildLabels(); |
| 10928 | me.afterBuildLabels(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10929 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10930 | // Fit |
| 10931 | me.beforeFit(); |
| 10932 | me.fit(); |
| 10933 | me.afterFit(); |
| 10934 | // |
| 10935 | me.afterUpdate(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10936 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10937 | return me.minSize; |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 10938 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10939 | }, |
| 10940 | afterUpdate: noop, |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 10941 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10942 | // |
Jian Li | d7a5a74 | 2016-02-12 13:51:18 -0800 | [diff] [blame] | 10943 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10944 | beforeSetDimensions: noop, |
| 10945 | setDimensions: function() { |
| 10946 | var me = this; |
| 10947 | // Set the unconstrained dimension before label rotation |
| 10948 | if (me.isHorizontal()) { |
| 10949 | // Reset position before calculating rotation |
| 10950 | me.width = me.maxWidth; |
| 10951 | me.left = 0; |
| 10952 | me.right = me.width; |
| 10953 | } else { |
| 10954 | me.height = me.maxHeight; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10955 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10956 | // Reset position before calculating rotation |
| 10957 | me.top = 0; |
| 10958 | me.bottom = me.height; |
| 10959 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10960 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10961 | // Reset padding |
| 10962 | me.paddingLeft = 0; |
| 10963 | me.paddingTop = 0; |
| 10964 | me.paddingRight = 0; |
| 10965 | me.paddingBottom = 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10966 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10967 | // Reset minSize |
| 10968 | me.minSize = { |
| 10969 | width: 0, |
| 10970 | height: 0 |
| 10971 | }; |
| 10972 | }, |
| 10973 | afterSetDimensions: noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10974 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10975 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10976 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10977 | beforeBuildLabels: noop, |
| 10978 | buildLabels: noop, |
| 10979 | afterBuildLabels: noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10980 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10981 | // |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10982 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10983 | beforeFit: noop, |
| 10984 | fit: function() { |
| 10985 | var me = this, |
| 10986 | valueOrDefault = helpers.getValueOrDefault, |
| 10987 | opts = me.options, |
| 10988 | globalDefaults = Chart.defaults.global, |
| 10989 | display = opts.display, |
| 10990 | fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), |
| 10991 | minSize = me.minSize; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 10992 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 10993 | if (me.isHorizontal()) { |
| 10994 | minSize.width = me.maxWidth; // fill all the width |
| 10995 | minSize.height = display ? fontSize + (opts.padding * 2) : 0; |
| 10996 | } else { |
| 10997 | minSize.width = display ? fontSize + (opts.padding * 2) : 0; |
| 10998 | minSize.height = me.maxHeight; // fill all the height |
| 10999 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11000 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11001 | me.width = minSize.width; |
| 11002 | me.height = minSize.height; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11003 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11004 | }, |
| 11005 | afterFit: noop, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11006 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11007 | // Shared Methods |
| 11008 | isHorizontal: function() { |
| 11009 | var pos = this.options.position; |
| 11010 | return pos === 'top' || pos === 'bottom'; |
| 11011 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11012 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11013 | // Actually draw the title block on the canvas |
| 11014 | draw: function() { |
| 11015 | var me = this, |
| 11016 | ctx = me.ctx, |
| 11017 | valueOrDefault = helpers.getValueOrDefault, |
| 11018 | opts = me.options, |
| 11019 | globalDefaults = Chart.defaults.global; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11020 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11021 | if (opts.display) { |
| 11022 | var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize), |
| 11023 | fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle), |
| 11024 | fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily), |
| 11025 | titleFont = helpers.fontString(fontSize, fontStyle, fontFamily), |
| 11026 | rotation = 0, |
| 11027 | titleX, |
| 11028 | titleY, |
| 11029 | top = me.top, |
| 11030 | left = me.left, |
| 11031 | bottom = me.bottom, |
| 11032 | right = me.right, |
| 11033 | maxWidth; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11034 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11035 | ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour |
| 11036 | ctx.font = titleFont; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11037 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11038 | // Horizontal |
| 11039 | if (me.isHorizontal()) { |
| 11040 | titleX = left + ((right - left) / 2); // midpoint of the width |
| 11041 | titleY = top + ((bottom - top) / 2); // midpoint of the height |
| 11042 | maxWidth = right - left; |
| 11043 | } else { |
| 11044 | titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2); |
| 11045 | titleY = top + ((bottom - top) / 2); |
| 11046 | maxWidth = bottom - top; |
| 11047 | rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); |
| 11048 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11049 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11050 | ctx.save(); |
| 11051 | ctx.translate(titleX, titleY); |
| 11052 | ctx.rotate(rotation); |
| 11053 | ctx.textAlign = 'center'; |
| 11054 | ctx.textBaseline = 'middle'; |
| 11055 | ctx.fillText(opts.text, 0, 0, maxWidth); |
| 11056 | ctx.restore(); |
| 11057 | } |
| 11058 | } |
| 11059 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11060 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11061 | function createNewTitleBlockAndAttach(chart, titleOpts) { |
| 11062 | var title = new Chart.Title({ |
| 11063 | ctx: chart.ctx, |
| 11064 | options: titleOpts, |
| 11065 | chart: chart |
| 11066 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11067 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11068 | layout.configure(chart, title, titleOpts); |
| 11069 | layout.addBox(chart, title); |
| 11070 | chart.titleBlock = title; |
| 11071 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11072 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11073 | return { |
| 11074 | id: 'title', |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11075 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11076 | beforeInit: function(chart) { |
| 11077 | var titleOpts = chart.options.title; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11078 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11079 | if (titleOpts) { |
| 11080 | createNewTitleBlockAndAttach(chart, titleOpts); |
| 11081 | } |
| 11082 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11083 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11084 | beforeUpdate: function(chart) { |
| 11085 | var titleOpts = chart.options.title; |
| 11086 | var titleBlock = chart.titleBlock; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11087 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11088 | if (titleOpts) { |
| 11089 | titleOpts = helpers.configMerge(Chart.defaults.global.title, titleOpts); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11090 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11091 | if (titleBlock) { |
| 11092 | layout.configure(chart, titleBlock, titleOpts); |
| 11093 | titleBlock.options = titleOpts; |
| 11094 | } else { |
| 11095 | createNewTitleBlockAndAttach(chart, titleOpts); |
| 11096 | } |
| 11097 | } else if (titleBlock) { |
| 11098 | Chart.layoutService.removeBox(chart, titleBlock); |
| 11099 | delete chart.titleBlock; |
| 11100 | } |
| 11101 | } |
| 11102 | }; |
| 11103 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11104 | |
| 11105 | },{}],44:[function(require,module,exports){ |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11106 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11107 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11108 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11109 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11110 | var helpers = Chart.helpers; |
| 11111 | // Default config for a category scale |
| 11112 | var defaultConfig = { |
| 11113 | position: 'bottom' |
| 11114 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11115 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11116 | var DatasetScale = Chart.Scale.extend({ |
| 11117 | /** |
| 11118 | * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those |
| 11119 | * else fall back to data.labels |
| 11120 | * @private |
| 11121 | */ |
| 11122 | getLabels: function() { |
| 11123 | var data = this.chart.data; |
| 11124 | return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; |
| 11125 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11126 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11127 | determineDataLimits: function() { |
| 11128 | var me = this; |
| 11129 | var labels = me.getLabels(); |
| 11130 | me.minIndex = 0; |
| 11131 | me.maxIndex = labels.length - 1; |
| 11132 | var findIndex; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11133 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11134 | if (me.options.ticks.min !== undefined) { |
| 11135 | // user specified min value |
| 11136 | findIndex = helpers.indexOf(labels, me.options.ticks.min); |
| 11137 | me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; |
| 11138 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11139 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11140 | if (me.options.ticks.max !== undefined) { |
| 11141 | // user specified max value |
| 11142 | findIndex = helpers.indexOf(labels, me.options.ticks.max); |
| 11143 | me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; |
| 11144 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11145 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11146 | me.min = labels[me.minIndex]; |
| 11147 | me.max = labels[me.maxIndex]; |
| 11148 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11149 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11150 | buildTicks: function() { |
| 11151 | var me = this; |
| 11152 | var labels = me.getLabels(); |
| 11153 | // If we are viewing some subset of labels, slice the original array |
| 11154 | me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); |
| 11155 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11156 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11157 | getLabelForIndex: function(index, datasetIndex) { |
| 11158 | var me = this; |
| 11159 | var data = me.chart.data; |
| 11160 | var isHorizontal = me.isHorizontal(); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11161 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11162 | if (data.yLabels && !isHorizontal) { |
| 11163 | return me.getRightValue(data.datasets[datasetIndex].data[index]); |
| 11164 | } |
| 11165 | return me.ticks[index - me.minIndex]; |
| 11166 | }, |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11167 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11168 | // Used to get data value locations. Value can either be an index or a numerical value |
| 11169 | getPixelForValue: function(value, index, datasetIndex, includeOffset) { |
| 11170 | var me = this; |
| 11171 | // 1 is added because we need the length but we have the indexes |
| 11172 | var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11173 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11174 | // If value is a data object, then index is the index in the data array, |
| 11175 | // not the index of the scale. We need to change that. |
| 11176 | var valueCategory; |
| 11177 | if (value !== undefined && value !== null) { |
| 11178 | valueCategory = me.isHorizontal() ? value.x : value.y; |
| 11179 | } |
| 11180 | if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { |
| 11181 | var labels = me.getLabels(); |
| 11182 | value = valueCategory || value; |
| 11183 | var idx = labels.indexOf(value); |
| 11184 | index = idx !== -1 ? idx : index; |
| 11185 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11186 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11187 | if (me.isHorizontal()) { |
| 11188 | var valueWidth = me.width / offsetAmt; |
| 11189 | var widthOffset = (valueWidth * (index - me.minIndex)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11190 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11191 | if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) { |
| 11192 | widthOffset += (valueWidth / 2); |
| 11193 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11194 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11195 | return me.left + Math.round(widthOffset); |
| 11196 | } |
| 11197 | var valueHeight = me.height / offsetAmt; |
| 11198 | var heightOffset = (valueHeight * (index - me.minIndex)); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11199 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11200 | if (me.options.gridLines.offsetGridLines && includeOffset) { |
| 11201 | heightOffset += (valueHeight / 2); |
| 11202 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11203 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11204 | return me.top + Math.round(heightOffset); |
| 11205 | }, |
| 11206 | getPixelForTick: function(index, includeOffset) { |
| 11207 | return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset); |
| 11208 | }, |
| 11209 | getValueForPixel: function(pixel) { |
| 11210 | var me = this; |
| 11211 | var value; |
| 11212 | var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1); |
| 11213 | var horz = me.isHorizontal(); |
| 11214 | var valueDimension = (horz ? me.width : me.height) / offsetAmt; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11215 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11216 | pixel -= horz ? me.left : me.top; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11217 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11218 | if (me.options.gridLines.offsetGridLines) { |
| 11219 | pixel -= (valueDimension / 2); |
| 11220 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11221 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11222 | if (pixel <= 0) { |
| 11223 | value = 0; |
| 11224 | } else { |
| 11225 | value = Math.round(pixel / valueDimension); |
| 11226 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11227 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11228 | return value; |
| 11229 | }, |
| 11230 | getBasePixel: function() { |
| 11231 | return this.bottom; |
| 11232 | } |
| 11233 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11234 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11235 | Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11236 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11237 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11238 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11239 | },{}],45:[function(require,module,exports){ |
| 11240 | 'use strict'; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11241 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11242 | module.exports = function(Chart) { |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11243 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11244 | var helpers = Chart.helpers; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11245 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11246 | var defaultConfig = { |
| 11247 | position: 'left', |
| 11248 | ticks: { |
| 11249 | callback: Chart.Ticks.formatters.linear |
| 11250 | } |
| 11251 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11252 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11253 | var LinearScale = Chart.LinearScaleBase.extend({ |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11254 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11255 | determineDataLimits: function() { |
| 11256 | var me = this; |
| 11257 | var opts = me.options; |
| 11258 | var chart = me.chart; |
| 11259 | var data = chart.data; |
| 11260 | var datasets = data.datasets; |
| 11261 | var isHorizontal = me.isHorizontal(); |
| 11262 | var DEFAULT_MIN = 0; |
| 11263 | var DEFAULT_MAX = 1; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11264 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11265 | function IDMatches(meta) { |
| 11266 | return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; |
| 11267 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11268 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11269 | // First Calculate the range |
| 11270 | me.min = null; |
| 11271 | me.max = null; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11272 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11273 | var hasStacks = opts.stacked; |
| 11274 | if (hasStacks === undefined) { |
| 11275 | helpers.each(datasets, function(dataset, datasetIndex) { |
| 11276 | if (hasStacks) { |
| 11277 | return; |
| 11278 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11279 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11280 | var meta = chart.getDatasetMeta(datasetIndex); |
| 11281 | if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && |
| 11282 | meta.stack !== undefined) { |
| 11283 | hasStacks = true; |
| 11284 | } |
| 11285 | }); |
| 11286 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11287 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11288 | if (opts.stacked || hasStacks) { |
| 11289 | var valuesPerStack = {}; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11290 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11291 | helpers.each(datasets, function(dataset, datasetIndex) { |
| 11292 | var meta = chart.getDatasetMeta(datasetIndex); |
| 11293 | var key = [ |
| 11294 | meta.type, |
| 11295 | // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined |
| 11296 | ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), |
| 11297 | meta.stack |
| 11298 | ].join('.'); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11299 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11300 | if (valuesPerStack[key] === undefined) { |
| 11301 | valuesPerStack[key] = { |
| 11302 | positiveValues: [], |
| 11303 | negativeValues: [] |
| 11304 | }; |
| 11305 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11306 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11307 | // Store these per type |
| 11308 | var positiveValues = valuesPerStack[key].positiveValues; |
| 11309 | var negativeValues = valuesPerStack[key].negativeValues; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11310 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11311 | if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 11312 | helpers.each(dataset.data, function(rawValue, index) { |
| 11313 | var value = +me.getRightValue(rawValue); |
| 11314 | if (isNaN(value) || meta.data[index].hidden) { |
| 11315 | return; |
| 11316 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11317 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11318 | positiveValues[index] = positiveValues[index] || 0; |
| 11319 | negativeValues[index] = negativeValues[index] || 0; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11320 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11321 | if (opts.relativePoints) { |
| 11322 | positiveValues[index] = 100; |
| 11323 | } else if (value < 0) { |
| 11324 | negativeValues[index] += value; |
| 11325 | } else { |
| 11326 | positiveValues[index] += value; |
| 11327 | } |
| 11328 | }); |
| 11329 | } |
| 11330 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11331 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11332 | helpers.each(valuesPerStack, function(valuesForType) { |
| 11333 | var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); |
| 11334 | var minVal = helpers.min(values); |
| 11335 | var maxVal = helpers.max(values); |
| 11336 | me.min = me.min === null ? minVal : Math.min(me.min, minVal); |
| 11337 | me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); |
| 11338 | }); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11339 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11340 | } else { |
| 11341 | helpers.each(datasets, function(dataset, datasetIndex) { |
| 11342 | var meta = chart.getDatasetMeta(datasetIndex); |
| 11343 | if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 11344 | helpers.each(dataset.data, function(rawValue, index) { |
| 11345 | var value = +me.getRightValue(rawValue); |
| 11346 | if (isNaN(value) || meta.data[index].hidden) { |
| 11347 | return; |
| 11348 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11349 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11350 | if (me.min === null) { |
| 11351 | me.min = value; |
| 11352 | } else if (value < me.min) { |
| 11353 | me.min = value; |
| 11354 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11355 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11356 | if (me.max === null) { |
| 11357 | me.max = value; |
| 11358 | } else if (value > me.max) { |
| 11359 | me.max = value; |
| 11360 | } |
| 11361 | }); |
| 11362 | } |
| 11363 | }); |
| 11364 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11365 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11366 | me.min = isFinite(me.min) ? me.min : DEFAULT_MIN; |
| 11367 | me.max = isFinite(me.max) ? me.max : DEFAULT_MAX; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11368 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11369 | // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero |
| 11370 | this.handleTickRangeOptions(); |
| 11371 | }, |
| 11372 | getTickLimit: function() { |
| 11373 | var maxTicks; |
| 11374 | var me = this; |
| 11375 | var tickOpts = me.options.ticks; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11376 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11377 | if (me.isHorizontal()) { |
| 11378 | maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); |
| 11379 | } else { |
| 11380 | // The factor of 2 used to scale the font size has been experimentally determined. |
| 11381 | var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize); |
| 11382 | maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); |
| 11383 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11384 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11385 | return maxTicks; |
| 11386 | }, |
| 11387 | // Called after the ticks are built. We need |
| 11388 | handleDirectionalChanges: function() { |
| 11389 | if (!this.isHorizontal()) { |
| 11390 | // We are in a vertical orientation. The top value is the highest. So reverse the array |
| 11391 | this.ticks.reverse(); |
| 11392 | } |
| 11393 | }, |
| 11394 | getLabelForIndex: function(index, datasetIndex) { |
| 11395 | return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
| 11396 | }, |
| 11397 | // Utils |
| 11398 | getPixelForValue: function(value) { |
| 11399 | // This must be called after fit has been run so that |
| 11400 | // this.left, this.top, this.right, and this.bottom have been defined |
| 11401 | var me = this; |
| 11402 | var start = me.start; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11403 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11404 | var rightValue = +me.getRightValue(value); |
| 11405 | var pixel; |
| 11406 | var range = me.end - start; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11407 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11408 | if (me.isHorizontal()) { |
| 11409 | pixel = me.left + (me.width / range * (rightValue - start)); |
| 11410 | return Math.round(pixel); |
| 11411 | } |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11412 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11413 | pixel = me.bottom - (me.height / range * (rightValue - start)); |
| 11414 | return Math.round(pixel); |
| 11415 | }, |
| 11416 | getValueForPixel: function(pixel) { |
| 11417 | var me = this; |
| 11418 | var isHorizontal = me.isHorizontal(); |
| 11419 | var innerDimension = isHorizontal ? me.width : me.height; |
| 11420 | var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; |
| 11421 | return me.start + ((me.end - me.start) * offset); |
| 11422 | }, |
| 11423 | getPixelForTick: function(index) { |
| 11424 | return this.getPixelForValue(this.ticksAsNumbers[index]); |
| 11425 | } |
| 11426 | }); |
| 11427 | Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11428 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11429 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 11430 | |
Steven Burrows | 96ee21e | 2017-07-11 19:49:45 +0100 | [diff] [blame] | 11431 | },{}],46:[function(require,module,exports){ |
| 11432 | 'use strict'; |
| 11433 | |
| 11434 | module.exports = function(Chart) { |
| 11435 | |
| 11436 | var helpers = Chart.helpers, |
| 11437 | noop = helpers.noop; |
| 11438 | |
| 11439 | Chart.LinearScaleBase = Chart.Scale.extend({ |
| 11440 | handleTickRangeOptions: function() { |
| 11441 | var me = this; |
| 11442 | var opts = me.options; |
| 11443 | var tickOpts = opts.ticks; |
| 11444 | |
| 11445 | // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, |
| 11446 | // do nothing since that would make the chart weird. If the user really wants a weird chart |
| 11447 | // axis, they can manually override it |
| 11448 | if (tickOpts.beginAtZero) { |
| 11449 | var minSign = helpers.sign(me.min); |
| 11450 | var maxSign = helpers.sign(me.max); |
| 11451 | |
| 11452 | if (minSign < 0 && maxSign < 0) { |
| 11453 | // move the top up to 0 |
| 11454 | me.max = 0; |
| 11455 | } else if (minSign > 0 && maxSign > 0) { |
| 11456 | // move the bottom down to 0 |
| 11457 | me.min = 0; |
| 11458 | } |
| 11459 | } |
| 11460 | |
| 11461 | if (tickOpts.min !== undefined) { |
| 11462 | me.min = tickOpts.min; |
| 11463 | } else if (tickOpts.suggestedMin !== undefined) { |
| 11464 | if (me.min === null) { |
| 11465 | me.min = tickOpts.suggestedMin; |
| 11466 | } else { |
| 11467 | me.min = Math.min(me.min, tickOpts.suggestedMin); |
| 11468 | } |
| 11469 | } |
| 11470 | |
| 11471 | if (tickOpts.max !== undefined) { |
| 11472 | me.max = tickOpts.max; |
| 11473 | } else if (tickOpts.suggestedMax !== undefined) { |
| 11474 | if (me.max === null) { |
| 11475 | me.max = tickOpts.suggestedMax; |
| 11476 | } else { |
| 11477 | me.max = Math.max(me.max, tickOpts.suggestedMax); |
| 11478 | } |
| 11479 | } |
| 11480 | |
| 11481 | if (me.min === me.max) { |
| 11482 | me.max++; |
| 11483 | |
| 11484 | if (!tickOpts.beginAtZero) { |
| 11485 | me.min--; |
| 11486 | } |
| 11487 | } |
| 11488 | }, |
| 11489 | getTickLimit: noop, |
| 11490 | handleDirectionalChanges: noop, |
| 11491 | |
| 11492 | buildTicks: function() { |
| 11493 | var me = this; |
| 11494 | var opts = me.options; |
| 11495 | var tickOpts = opts.ticks; |
| 11496 | |
| 11497 | // Figure out what the max number of ticks we can support it is based on the size of |
| 11498 | // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 |
| 11499 | // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on |
| 11500 | // the graph. Make sure we always have at least 2 ticks |
| 11501 | var maxTicks = me.getTickLimit(); |
| 11502 | maxTicks = Math.max(2, maxTicks); |
| 11503 | |
| 11504 | var numericGeneratorOptions = { |
| 11505 | maxTicks: maxTicks, |
| 11506 | min: tickOpts.min, |
| 11507 | max: tickOpts.max, |
| 11508 | stepSize: helpers.getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) |
| 11509 | }; |
| 11510 | var ticks = me.ticks = Chart.Ticks.generators.linear(numericGeneratorOptions, me); |
| 11511 | |
| 11512 | me.handleDirectionalChanges(); |
| 11513 | |
| 11514 | // At this point, we need to update our max and min given the tick values since we have expanded the |
| 11515 | // range of the scale |
| 11516 | me.max = helpers.max(ticks); |
| 11517 | me.min = helpers.min(ticks); |
| 11518 | |
| 11519 | if (tickOpts.reverse) { |
| 11520 | ticks.reverse(); |
| 11521 | |
| 11522 | me.start = me.max; |
| 11523 | me.end = me.min; |
| 11524 | } else { |
| 11525 | me.start = me.min; |
| 11526 | me.end = me.max; |
| 11527 | } |
| 11528 | }, |
| 11529 | convertTicksToLabels: function() { |
| 11530 | var me = this; |
| 11531 | me.ticksAsNumbers = me.ticks.slice(); |
| 11532 | me.zeroLineIndex = me.ticks.indexOf(0); |
| 11533 | |
| 11534 | Chart.Scale.prototype.convertTicksToLabels.call(me); |
| 11535 | } |
| 11536 | }); |
| 11537 | }; |
| 11538 | |
| 11539 | },{}],47:[function(require,module,exports){ |
| 11540 | 'use strict'; |
| 11541 | |
| 11542 | module.exports = function(Chart) { |
| 11543 | |
| 11544 | var helpers = Chart.helpers; |
| 11545 | |
| 11546 | var defaultConfig = { |
| 11547 | position: 'left', |
| 11548 | |
| 11549 | // label settings |
| 11550 | ticks: { |
| 11551 | callback: Chart.Ticks.formatters.logarithmic |
| 11552 | } |
| 11553 | }; |
| 11554 | |
| 11555 | var LogarithmicScale = Chart.Scale.extend({ |
| 11556 | determineDataLimits: function() { |
| 11557 | var me = this; |
| 11558 | var opts = me.options; |
| 11559 | var tickOpts = opts.ticks; |
| 11560 | var chart = me.chart; |
| 11561 | var data = chart.data; |
| 11562 | var datasets = data.datasets; |
| 11563 | var getValueOrDefault = helpers.getValueOrDefault; |
| 11564 | var isHorizontal = me.isHorizontal(); |
| 11565 | function IDMatches(meta) { |
| 11566 | return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; |
| 11567 | } |
| 11568 | |
| 11569 | // Calculate Range |
| 11570 | me.min = null; |
| 11571 | me.max = null; |
| 11572 | me.minNotZero = null; |
| 11573 | |
| 11574 | var hasStacks = opts.stacked; |
| 11575 | if (hasStacks === undefined) { |
| 11576 | helpers.each(datasets, function(dataset, datasetIndex) { |
| 11577 | if (hasStacks) { |
| 11578 | return; |
| 11579 | } |
| 11580 | |
| 11581 | var meta = chart.getDatasetMeta(datasetIndex); |
| 11582 | if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && |
| 11583 | meta.stack !== undefined) { |
| 11584 | hasStacks = true; |
| 11585 | } |
| 11586 | }); |
| 11587 | } |
| 11588 | |
| 11589 | if (opts.stacked || hasStacks) { |
| 11590 | var valuesPerStack = {}; |
| 11591 | |
| 11592 | helpers.each(datasets, function(dataset, datasetIndex) { |
| 11593 | var meta = chart.getDatasetMeta(datasetIndex); |
| 11594 | var key = [ |
| 11595 | meta.type, |
| 11596 | // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined |
| 11597 | ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), |
| 11598 | meta.stack |
| 11599 | ].join('.'); |
| 11600 | |
| 11601 | if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 11602 | if (valuesPerStack[key] === undefined) { |
| 11603 | valuesPerStack[key] = []; |
| 11604 | } |
| 11605 | |
| 11606 | helpers.each(dataset.data, function(rawValue, index) { |
| 11607 | var values = valuesPerStack[key]; |
| 11608 | var value = +me.getRightValue(rawValue); |
| 11609 | if (isNaN(value) || meta.data[index].hidden) { |
| 11610 | return; |
| 11611 | } |
| 11612 | |
| 11613 | values[index] = values[index] || 0; |
| 11614 | |
| 11615 | if (opts.relativePoints) { |
| 11616 | values[index] = 100; |
| 11617 | } else { |
| 11618 | // Don't need to split positive and negative since the log scale can't handle a 0 crossing |
| 11619 | values[index] += value; |
| 11620 | } |
| 11621 | }); |
| 11622 | } |
| 11623 | }); |
| 11624 | |
| 11625 | helpers.each(valuesPerStack, function(valuesForType) { |
| 11626 | var minVal = helpers.min(valuesForType); |
| 11627 | var maxVal = helpers.max(valuesForType); |
| 11628 | me.min = me.min === null ? minVal : Math.min(me.min, minVal); |
| 11629 | me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); |
| 11630 | }); |
| 11631 | |
| 11632 | } else { |
| 11633 | helpers.each(datasets, function(dataset, datasetIndex) { |
| 11634 | var meta = chart.getDatasetMeta(datasetIndex); |
| 11635 | if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { |
| 11636 | helpers.each(dataset.data, function(rawValue, index) { |
| 11637 | var value = +me.getRightValue(rawValue); |
| 11638 | if (isNaN(value) || meta.data[index].hidden) { |
| 11639 | return; |
| 11640 | } |
| 11641 | |
| 11642 | if (me.min === null) { |
| 11643 | me.min = value; |
| 11644 | } else if (value < me.min) { |
| 11645 | me.min = value; |
| 11646 | } |
| 11647 | |
| 11648 | if (me.max === null) { |
| 11649 | me.max = value; |
| 11650 | } else if (value > me.max) { |
| 11651 | me.max = value; |
| 11652 | } |
| 11653 | |
| 11654 | if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { |
| 11655 | me.minNotZero = value; |
| 11656 | } |
| 11657 | }); |
| 11658 | } |
| 11659 | }); |
| 11660 | } |
| 11661 | |
| 11662 | me.min = getValueOrDefault(tickOpts.min, me.min); |
| 11663 | me.max = getValueOrDefault(tickOpts.max, me.max); |
| 11664 | |
| 11665 | if (me.min === me.max) { |
| 11666 | if (me.min !== 0 && me.min !== null) { |
| 11667 | me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); |
| 11668 | me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); |
| 11669 | } else { |
| 11670 | me.min = 1; |
| 11671 | me.max = 10; |
| 11672 | } |
| 11673 | } |
| 11674 | }, |
| 11675 | buildTicks: function() { |
| 11676 | var me = this; |
| 11677 | var opts = me.options; |
| 11678 | var tickOpts = opts.ticks; |
| 11679 | |
| 11680 | var generationOptions = { |
| 11681 | min: tickOpts.min, |
| 11682 | max: tickOpts.max |
| 11683 | }; |
| 11684 | var ticks = me.ticks = Chart.Ticks.generators.logarithmic(generationOptions, me); |
| 11685 | |
| 11686 | if (!me.isHorizontal()) { |
| 11687 | // We are in a vertical orientation. The top value is the highest. So reverse the array |
| 11688 | ticks.reverse(); |
| 11689 | } |
| 11690 | |
| 11691 | // At this point, we need to update our max and min given the tick values since we have expanded the |
| 11692 | // range of the scale |
| 11693 | me.max = helpers.max(ticks); |
| 11694 | me.min = helpers.min(ticks); |
| 11695 | |
| 11696 | if (tickOpts.reverse) { |
| 11697 | ticks.reverse(); |
| 11698 | |
| 11699 | me.start = me.max; |
| 11700 | me.end = me.min; |
| 11701 | } else { |
| 11702 | me.start = me.min; |
| 11703 | me.end = me.max; |
| 11704 | } |
| 11705 | }, |
| 11706 | convertTicksToLabels: function() { |
| 11707 | this.tickValues = this.ticks.slice(); |
| 11708 | |
| 11709 | Chart.Scale.prototype.convertTicksToLabels.call(this); |
| 11710 | }, |
| 11711 | // Get the correct tooltip label |
| 11712 | getLabelForIndex: function(index, datasetIndex) { |
| 11713 | return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
| 11714 | }, |
| 11715 | getPixelForTick: function(index) { |
| 11716 | return this.getPixelForValue(this.tickValues[index]); |
| 11717 | }, |
| 11718 | getPixelForValue: function(value) { |
| 11719 | var me = this; |
| 11720 | var innerDimension; |
| 11721 | var pixel; |
| 11722 | |
| 11723 | var start = me.start; |
| 11724 | var newVal = +me.getRightValue(value); |
| 11725 | var range; |
| 11726 | var opts = me.options; |
| 11727 | var tickOpts = opts.ticks; |
| 11728 | |
| 11729 | if (me.isHorizontal()) { |
| 11730 | range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0 |
| 11731 | if (newVal === 0) { |
| 11732 | pixel = me.left; |
| 11733 | } else { |
| 11734 | innerDimension = me.width; |
| 11735 | pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); |
| 11736 | } |
| 11737 | } else { |
| 11738 | // Bottom - top since pixels increase downward on a screen |
| 11739 | innerDimension = me.height; |
| 11740 | if (start === 0 && !tickOpts.reverse) { |
| 11741 | range = helpers.log10(me.end) - helpers.log10(me.minNotZero); |
| 11742 | if (newVal === start) { |
| 11743 | pixel = me.bottom; |
| 11744 | } else if (newVal === me.minNotZero) { |
| 11745 | pixel = me.bottom - innerDimension * 0.02; |
| 11746 | } else { |
| 11747 | pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero))); |
| 11748 | } |
| 11749 | } else if (me.end === 0 && tickOpts.reverse) { |
| 11750 | range = helpers.log10(me.start) - helpers.log10(me.minNotZero); |
| 11751 | if (newVal === me.end) { |
| 11752 | pixel = me.top; |
| 11753 | } else if (newVal === me.minNotZero) { |
| 11754 | pixel = me.top + innerDimension * 0.02; |
| 11755 | } else { |
| 11756 | pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero))); |
| 11757 | } |
| 11758 | } else if (newVal === 0) { |
| 11759 | pixel = tickOpts.reverse ? me.top : me.bottom; |
| 11760 | } else { |
| 11761 | range = helpers.log10(me.end) - helpers.log10(start); |
| 11762 | innerDimension = me.height; |
| 11763 | pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); |
| 11764 | } |
| 11765 | } |
| 11766 | return pixel; |
| 11767 | }, |
| 11768 | getValueForPixel: function(pixel) { |
| 11769 | var me = this; |
| 11770 | var range = helpers.log10(me.end) - helpers.log10(me.start); |
| 11771 | var value, innerDimension; |
| 11772 | |
| 11773 | if (me.isHorizontal()) { |
| 11774 | innerDimension = me.width; |
| 11775 | value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension); |
| 11776 | } else { // todo: if start === 0 |
| 11777 | innerDimension = me.height; |
| 11778 | value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start; |
| 11779 | } |
| 11780 | return value; |
| 11781 | } |
| 11782 | }); |
| 11783 | Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); |
| 11784 | |
| 11785 | }; |
| 11786 | |
| 11787 | },{}],48:[function(require,module,exports){ |
| 11788 | 'use strict'; |
| 11789 | |
| 11790 | module.exports = function(Chart) { |
| 11791 | |
| 11792 | var helpers = Chart.helpers; |
| 11793 | var globalDefaults = Chart.defaults.global; |
| 11794 | |
| 11795 | var defaultConfig = { |
| 11796 | display: true, |
| 11797 | |
| 11798 | // Boolean - Whether to animate scaling the chart from the centre |
| 11799 | animate: true, |
| 11800 | position: 'chartArea', |
| 11801 | |
| 11802 | angleLines: { |
| 11803 | display: true, |
| 11804 | color: 'rgba(0, 0, 0, 0.1)', |
| 11805 | lineWidth: 1 |
| 11806 | }, |
| 11807 | |
| 11808 | gridLines: { |
| 11809 | circular: false |
| 11810 | }, |
| 11811 | |
| 11812 | // label settings |
| 11813 | ticks: { |
| 11814 | // Boolean - Show a backdrop to the scale label |
| 11815 | showLabelBackdrop: true, |
| 11816 | |
| 11817 | // String - The colour of the label backdrop |
| 11818 | backdropColor: 'rgba(255,255,255,0.75)', |
| 11819 | |
| 11820 | // Number - The backdrop padding above & below the label in pixels |
| 11821 | backdropPaddingY: 2, |
| 11822 | |
| 11823 | // Number - The backdrop padding to the side of the label in pixels |
| 11824 | backdropPaddingX: 2, |
| 11825 | |
| 11826 | callback: Chart.Ticks.formatters.linear |
| 11827 | }, |
| 11828 | |
| 11829 | pointLabels: { |
| 11830 | // Boolean - if true, show point labels |
| 11831 | display: true, |
| 11832 | |
| 11833 | // Number - Point label font size in pixels |
| 11834 | fontSize: 10, |
| 11835 | |
| 11836 | // Function - Used to convert point labels |
| 11837 | callback: function(label) { |
| 11838 | return label; |
| 11839 | } |
| 11840 | } |
| 11841 | }; |
| 11842 | |
| 11843 | function getValueCount(scale) { |
| 11844 | var opts = scale.options; |
| 11845 | return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; |
| 11846 | } |
| 11847 | |
| 11848 | function getPointLabelFontOptions(scale) { |
| 11849 | var pointLabelOptions = scale.options.pointLabels; |
| 11850 | var fontSize = helpers.getValueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); |
| 11851 | var fontStyle = helpers.getValueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); |
| 11852 | var fontFamily = helpers.getValueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); |
| 11853 | var font = helpers.fontString(fontSize, fontStyle, fontFamily); |
| 11854 | |
| 11855 | return { |
| 11856 | size: fontSize, |
| 11857 | style: fontStyle, |
| 11858 | family: fontFamily, |
| 11859 | font: font |
| 11860 | }; |
| 11861 | } |
| 11862 | |
| 11863 | function measureLabelSize(ctx, fontSize, label) { |
| 11864 | if (helpers.isArray(label)) { |
| 11865 | return { |
| 11866 | w: helpers.longestText(ctx, ctx.font, label), |
| 11867 | h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) |
| 11868 | }; |
| 11869 | } |
| 11870 | |
| 11871 | return { |
| 11872 | w: ctx.measureText(label).width, |
| 11873 | h: fontSize |
| 11874 | }; |
| 11875 | } |
| 11876 | |
| 11877 | function determineLimits(angle, pos, size, min, max) { |
| 11878 | if (angle === min || angle === max) { |
| 11879 | return { |
| 11880 | start: pos - (size / 2), |
| 11881 | end: pos + (size / 2) |
| 11882 | }; |
| 11883 | } else if (angle < min || angle > max) { |
| 11884 | return { |
| 11885 | start: pos - size - 5, |
| 11886 | end: pos |
| 11887 | }; |
| 11888 | } |
| 11889 | |
| 11890 | return { |
| 11891 | start: pos, |
| 11892 | end: pos + size + 5 |
| 11893 | }; |
| 11894 | } |
| 11895 | |
| 11896 | /** |
| 11897 | * Helper function to fit a radial linear scale with point labels |
| 11898 | */ |
| 11899 | function fitWithPointLabels(scale) { |
| 11900 | /* |
| 11901 | * Right, this is really confusing and there is a lot of maths going on here |
| 11902 | * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 |
| 11903 | * |
| 11904 | * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif |
| 11905 | * |
| 11906 | * Solution: |
| 11907 | * |
| 11908 | * We assume the radius of the polygon is half the size of the canvas at first |
| 11909 | * at each index we check if the text overlaps. |
| 11910 | * |
| 11911 | * Where it does, we store that angle and that index. |
| 11912 | * |
| 11913 | * After finding the largest index and angle we calculate how much we need to remove |
| 11914 | * from the shape radius to move the point inwards by that x. |
| 11915 | * |
| 11916 | * We average the left and right distances to get the maximum shape radius that can fit in the box |
| 11917 | * along with labels. |
| 11918 | * |
| 11919 | * Once we have that, we can find the centre point for the chart, by taking the x text protrusion |
| 11920 | * on each side, removing that from the size, halving it and adding the left x protrusion width. |
| 11921 | * |
| 11922 | * This will mean we have a shape fitted to the canvas, as large as it can be with the labels |
| 11923 | * and position it in the most space efficient manner |
| 11924 | * |
| 11925 | * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif |
| 11926 | */ |
| 11927 | |
| 11928 | var plFont = getPointLabelFontOptions(scale); |
| 11929 | |
| 11930 | // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. |
| 11931 | // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points |
| 11932 | var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); |
| 11933 | var furthestLimits = { |
| 11934 | r: scale.width, |
| 11935 | l: 0, |
| 11936 | t: scale.height, |
| 11937 | b: 0 |
| 11938 | }; |
| 11939 | var furthestAngles = {}; |
| 11940 | var i; |
| 11941 | var textSize; |
| 11942 | var pointPosition; |
| 11943 | |
| 11944 | scale.ctx.font = plFont.font; |
| 11945 | scale._pointLabelSizes = []; |
| 11946 | |
| 11947 | var valueCount = getValueCount(scale); |
| 11948 | for (i = 0; i < valueCount; i++) { |
| 11949 | pointPosition = scale.getPointPosition(i, largestPossibleRadius); |
| 11950 | textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); |
| 11951 | scale._pointLabelSizes[i] = textSize; |
| 11952 | |
| 11953 | // Add quarter circle to make degree 0 mean top of circle |
| 11954 | var angleRadians = scale.getIndexAngle(i); |
| 11955 | var angle = helpers.toDegrees(angleRadians) % 360; |
| 11956 | var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); |
| 11957 | var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); |
| 11958 | |
| 11959 | if (hLimits.start < furthestLimits.l) { |
| 11960 | furthestLimits.l = hLimits.start; |
| 11961 | furthestAngles.l = angleRadians; |
| 11962 | } |
| 11963 | |
| 11964 | if (hLimits.end > furthestLimits.r) { |
| 11965 | furthestLimits.r = hLimits.end; |
| 11966 | furthestAngles.r = angleRadians; |
| 11967 | } |
| 11968 | |
| 11969 | if (vLimits.start < furthestLimits.t) { |
| 11970 | furthestLimits.t = vLimits.start; |
| 11971 | furthestAngles.t = angleRadians; |
| 11972 | } |
| 11973 | |
| 11974 | if (vLimits.end > furthestLimits.b) { |
| 11975 | furthestLimits.b = vLimits.end; |
| 11976 | furthestAngles.b = angleRadians; |
| 11977 | } |
| 11978 | } |
| 11979 | |
| 11980 | scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); |
| 11981 | } |
| 11982 | |
| 11983 | /** |
| 11984 | * Helper function to fit a radial linear scale with no point labels |
| 11985 | */ |
| 11986 | function fit(scale) { |
| 11987 | var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); |
| 11988 | scale.drawingArea = Math.round(largestPossibleRadius); |
| 11989 | scale.setCenterPoint(0, 0, 0, 0); |
| 11990 | } |
| 11991 | |
| 11992 | function getTextAlignForAngle(angle) { |
| 11993 | if (angle === 0 || angle === 180) { |
| 11994 | return 'center'; |
| 11995 | } else if (angle < 180) { |
| 11996 | return 'left'; |
| 11997 | } |
| 11998 | |
| 11999 | return 'right'; |
| 12000 | } |
| 12001 | |
| 12002 | function fillText(ctx, text, position, fontSize) { |
| 12003 | if (helpers.isArray(text)) { |
| 12004 | var y = position.y; |
| 12005 | var spacing = 1.5 * fontSize; |
| 12006 | |
| 12007 | for (var i = 0; i < text.length; ++i) { |
| 12008 | ctx.fillText(text[i], position.x, y); |
| 12009 | y+= spacing; |
| 12010 | } |
| 12011 | } else { |
| 12012 | ctx.fillText(text, position.x, position.y); |
| 12013 | } |
| 12014 | } |
| 12015 | |
| 12016 | function adjustPointPositionForLabelHeight(angle, textSize, position) { |
| 12017 | if (angle === 90 || angle === 270) { |
| 12018 | position.y -= (textSize.h / 2); |
| 12019 | } else if (angle > 270 || angle < 90) { |
| 12020 | position.y -= textSize.h; |
| 12021 | } |
| 12022 | } |
| 12023 | |
| 12024 | function drawPointLabels(scale) { |
| 12025 | var ctx = scale.ctx; |
| 12026 | var getValueOrDefault = helpers.getValueOrDefault; |
| 12027 | var opts = scale.options; |
| 12028 | var angleLineOpts = opts.angleLines; |
| 12029 | var pointLabelOpts = opts.pointLabels; |
| 12030 | |
| 12031 | ctx.lineWidth = angleLineOpts.lineWidth; |
| 12032 | ctx.strokeStyle = angleLineOpts.color; |
| 12033 | |
| 12034 | var outerDistance = scale.getDistanceFromCenterForValue(opts.reverse ? scale.min : scale.max); |
| 12035 | |
| 12036 | // Point Label Font |
| 12037 | var plFont = getPointLabelFontOptions(scale); |
| 12038 | |
| 12039 | ctx.textBaseline = 'top'; |
| 12040 | |
| 12041 | for (var i = getValueCount(scale) - 1; i >= 0; i--) { |
| 12042 | if (angleLineOpts.display) { |
| 12043 | var outerPosition = scale.getPointPosition(i, outerDistance); |
| 12044 | ctx.beginPath(); |
| 12045 | ctx.moveTo(scale.xCenter, scale.yCenter); |
| 12046 | ctx.lineTo(outerPosition.x, outerPosition.y); |
| 12047 | ctx.stroke(); |
| 12048 | ctx.closePath(); |
| 12049 | } |
| 12050 | |
| 12051 | if (pointLabelOpts.display) { |
| 12052 | // Extra 3px out for some label spacing |
| 12053 | var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); |
| 12054 | |
| 12055 | // Keep this in loop since we may support array properties here |
| 12056 | var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); |
| 12057 | ctx.font = plFont.font; |
| 12058 | ctx.fillStyle = pointLabelFontColor; |
| 12059 | |
| 12060 | var angleRadians = scale.getIndexAngle(i); |
| 12061 | var angle = helpers.toDegrees(angleRadians); |
| 12062 | ctx.textAlign = getTextAlignForAngle(angle); |
| 12063 | adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); |
| 12064 | fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); |
| 12065 | } |
| 12066 | } |
| 12067 | } |
| 12068 | |
| 12069 | function drawRadiusLine(scale, gridLineOpts, radius, index) { |
| 12070 | var ctx = scale.ctx; |
| 12071 | ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1); |
| 12072 | ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); |
| 12073 | |
| 12074 | if (scale.options.gridLines.circular) { |
| 12075 | // Draw circular arcs between the points |
| 12076 | ctx.beginPath(); |
| 12077 | ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); |
| 12078 | ctx.closePath(); |
| 12079 | ctx.stroke(); |
| 12080 | } else { |
| 12081 | // Draw straight lines connecting each index |
| 12082 | var valueCount = getValueCount(scale); |
| 12083 | |
| 12084 | if (valueCount === 0) { |
| 12085 | return; |
| 12086 | } |
| 12087 | |
| 12088 | ctx.beginPath(); |
| 12089 | var pointPosition = scale.getPointPosition(0, radius); |
| 12090 | ctx.moveTo(pointPosition.x, pointPosition.y); |
| 12091 | |
| 12092 | for (var i = 1; i < valueCount; i++) { |
| 12093 | pointPosition = scale.getPointPosition(i, radius); |
| 12094 | ctx.lineTo(pointPosition.x, pointPosition.y); |
| 12095 | } |
| 12096 | |
| 12097 | ctx.closePath(); |
| 12098 | ctx.stroke(); |
| 12099 | } |
| 12100 | } |
| 12101 | |
| 12102 | function numberOrZero(param) { |
| 12103 | return helpers.isNumber(param) ? param : 0; |
| 12104 | } |
| 12105 | |
| 12106 | var LinearRadialScale = Chart.LinearScaleBase.extend({ |
| 12107 | setDimensions: function() { |
| 12108 | var me = this; |
| 12109 | var opts = me.options; |
| 12110 | var tickOpts = opts.ticks; |
| 12111 | // Set the unconstrained dimension before label rotation |
| 12112 | me.width = me.maxWidth; |
| 12113 | me.height = me.maxHeight; |
| 12114 | me.xCenter = Math.round(me.width / 2); |
| 12115 | me.yCenter = Math.round(me.height / 2); |
| 12116 | |
| 12117 | var minSize = helpers.min([me.height, me.width]); |
| 12118 | var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
| 12119 | me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); |
| 12120 | }, |
| 12121 | determineDataLimits: function() { |
| 12122 | var me = this; |
| 12123 | var chart = me.chart; |
| 12124 | var min = Number.POSITIVE_INFINITY; |
| 12125 | var max = Number.NEGATIVE_INFINITY; |
| 12126 | |
| 12127 | helpers.each(chart.data.datasets, function(dataset, datasetIndex) { |
| 12128 | if (chart.isDatasetVisible(datasetIndex)) { |
| 12129 | var meta = chart.getDatasetMeta(datasetIndex); |
| 12130 | |
| 12131 | helpers.each(dataset.data, function(rawValue, index) { |
| 12132 | var value = +me.getRightValue(rawValue); |
| 12133 | if (isNaN(value) || meta.data[index].hidden) { |
| 12134 | return; |
| 12135 | } |
| 12136 | |
| 12137 | min = Math.min(value, min); |
| 12138 | max = Math.max(value, max); |
| 12139 | }); |
| 12140 | } |
| 12141 | }); |
| 12142 | |
| 12143 | me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); |
| 12144 | me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); |
| 12145 | |
| 12146 | // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero |
| 12147 | me.handleTickRangeOptions(); |
| 12148 | }, |
| 12149 | getTickLimit: function() { |
| 12150 | var tickOpts = this.options.ticks; |
| 12151 | var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
| 12152 | return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); |
| 12153 | }, |
| 12154 | convertTicksToLabels: function() { |
| 12155 | var me = this; |
| 12156 | Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); |
| 12157 | |
| 12158 | // Point labels |
| 12159 | me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); |
| 12160 | }, |
| 12161 | getLabelForIndex: function(index, datasetIndex) { |
| 12162 | return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); |
| 12163 | }, |
| 12164 | fit: function() { |
| 12165 | if (this.options.pointLabels.display) { |
| 12166 | fitWithPointLabels(this); |
| 12167 | } else { |
| 12168 | fit(this); |
| 12169 | } |
| 12170 | }, |
| 12171 | /** |
| 12172 | * Set radius reductions and determine new radius and center point |
| 12173 | * @private |
| 12174 | */ |
| 12175 | setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { |
| 12176 | var me = this; |
| 12177 | var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); |
| 12178 | var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); |
| 12179 | var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); |
| 12180 | var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); |
| 12181 | |
| 12182 | radiusReductionLeft = numberOrZero(radiusReductionLeft); |
| 12183 | radiusReductionRight = numberOrZero(radiusReductionRight); |
| 12184 | radiusReductionTop = numberOrZero(radiusReductionTop); |
| 12185 | radiusReductionBottom = numberOrZero(radiusReductionBottom); |
| 12186 | |
| 12187 | me.drawingArea = Math.min( |
| 12188 | Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), |
| 12189 | Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); |
| 12190 | me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); |
| 12191 | }, |
| 12192 | setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { |
| 12193 | var me = this; |
| 12194 | var maxRight = me.width - rightMovement - me.drawingArea, |
| 12195 | maxLeft = leftMovement + me.drawingArea, |
| 12196 | maxTop = topMovement + me.drawingArea, |
| 12197 | maxBottom = me.height - bottomMovement - me.drawingArea; |
| 12198 | |
| 12199 | me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); |
| 12200 | me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); |
| 12201 | }, |
| 12202 | |
| 12203 | getIndexAngle: function(index) { |
| 12204 | var angleMultiplier = (Math.PI * 2) / getValueCount(this); |
| 12205 | var startAngle = this.chart.options && this.chart.options.startAngle ? |
| 12206 | this.chart.options.startAngle : |
| 12207 | 0; |
| 12208 | |
| 12209 | var startAngleRadians = startAngle * Math.PI * 2 / 360; |
| 12210 | |
| 12211 | // Start from the top instead of right, so remove a quarter of the circle |
| 12212 | return index * angleMultiplier + startAngleRadians; |
| 12213 | }, |
| 12214 | getDistanceFromCenterForValue: function(value) { |
| 12215 | var me = this; |
| 12216 | |
| 12217 | if (value === null) { |
| 12218 | return 0; // null always in center |
| 12219 | } |
| 12220 | |
| 12221 | // Take into account half font size + the yPadding of the top value |
| 12222 | var scalingFactor = me.drawingArea / (me.max - me.min); |
| 12223 | if (me.options.reverse) { |
| 12224 | return (me.max - value) * scalingFactor; |
| 12225 | } |
| 12226 | return (value - me.min) * scalingFactor; |
| 12227 | }, |
| 12228 | getPointPosition: function(index, distanceFromCenter) { |
| 12229 | var me = this; |
| 12230 | var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); |
| 12231 | return { |
| 12232 | x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, |
| 12233 | y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter |
| 12234 | }; |
| 12235 | }, |
| 12236 | getPointPositionForValue: function(index, value) { |
| 12237 | return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); |
| 12238 | }, |
| 12239 | |
| 12240 | getBasePosition: function() { |
| 12241 | var me = this; |
| 12242 | var min = me.min; |
| 12243 | var max = me.max; |
| 12244 | |
| 12245 | return me.getPointPositionForValue(0, |
| 12246 | me.beginAtZero? 0: |
| 12247 | min < 0 && max < 0? max : |
| 12248 | min > 0 && max > 0? min : |
| 12249 | 0); |
| 12250 | }, |
| 12251 | |
| 12252 | draw: function() { |
| 12253 | var me = this; |
| 12254 | var opts = me.options; |
| 12255 | var gridLineOpts = opts.gridLines; |
| 12256 | var tickOpts = opts.ticks; |
| 12257 | var getValueOrDefault = helpers.getValueOrDefault; |
| 12258 | |
| 12259 | if (opts.display) { |
| 12260 | var ctx = me.ctx; |
| 12261 | |
| 12262 | // Tick Font |
| 12263 | var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); |
| 12264 | var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); |
| 12265 | var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); |
| 12266 | var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); |
| 12267 | |
| 12268 | helpers.each(me.ticks, function(label, index) { |
| 12269 | // Don't draw a centre value (if it is minimum) |
| 12270 | if (index > 0 || opts.reverse) { |
| 12271 | var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); |
| 12272 | var yHeight = me.yCenter - yCenterOffset; |
| 12273 | |
| 12274 | // Draw circular lines around the scale |
| 12275 | if (gridLineOpts.display && index !== 0) { |
| 12276 | drawRadiusLine(me, gridLineOpts, yCenterOffset, index); |
| 12277 | } |
| 12278 | |
| 12279 | if (tickOpts.display) { |
| 12280 | var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); |
| 12281 | ctx.font = tickLabelFont; |
| 12282 | |
| 12283 | if (tickOpts.showLabelBackdrop) { |
| 12284 | var labelWidth = ctx.measureText(label).width; |
| 12285 | ctx.fillStyle = tickOpts.backdropColor; |
| 12286 | ctx.fillRect( |
| 12287 | me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX, |
| 12288 | yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY, |
| 12289 | labelWidth + tickOpts.backdropPaddingX * 2, |
| 12290 | tickFontSize + tickOpts.backdropPaddingY * 2 |
| 12291 | ); |
| 12292 | } |
| 12293 | |
| 12294 | ctx.textAlign = 'center'; |
| 12295 | ctx.textBaseline = 'middle'; |
| 12296 | ctx.fillStyle = tickFontColor; |
| 12297 | ctx.fillText(label, me.xCenter, yHeight); |
| 12298 | } |
| 12299 | } |
| 12300 | }); |
| 12301 | |
| 12302 | if (opts.angleLines.display || opts.pointLabels.display) { |
| 12303 | drawPointLabels(me); |
| 12304 | } |
| 12305 | } |
| 12306 | } |
| 12307 | }); |
| 12308 | Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); |
| 12309 | |
| 12310 | }; |
| 12311 | |
| 12312 | },{}],49:[function(require,module,exports){ |
| 12313 | /* global window: false */ |
| 12314 | 'use strict'; |
| 12315 | |
| 12316 | var moment = require(1); |
| 12317 | moment = typeof(moment) === 'function' ? moment : window.moment; |
| 12318 | |
| 12319 | module.exports = function(Chart) { |
| 12320 | |
| 12321 | var helpers = Chart.helpers; |
| 12322 | var interval = { |
| 12323 | millisecond: { |
| 12324 | size: 1, |
| 12325 | steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] |
| 12326 | }, |
| 12327 | second: { |
| 12328 | size: 1000, |
| 12329 | steps: [1, 2, 5, 10, 30] |
| 12330 | }, |
| 12331 | minute: { |
| 12332 | size: 60000, |
| 12333 | steps: [1, 2, 5, 10, 30] |
| 12334 | }, |
| 12335 | hour: { |
| 12336 | size: 3600000, |
| 12337 | steps: [1, 2, 3, 6, 12] |
| 12338 | }, |
| 12339 | day: { |
| 12340 | size: 86400000, |
| 12341 | steps: [1, 2, 5] |
| 12342 | }, |
| 12343 | week: { |
| 12344 | size: 604800000, |
| 12345 | maxStep: 4 |
| 12346 | }, |
| 12347 | month: { |
| 12348 | size: 2.628e9, |
| 12349 | maxStep: 3 |
| 12350 | }, |
| 12351 | quarter: { |
| 12352 | size: 7.884e9, |
| 12353 | maxStep: 4 |
| 12354 | }, |
| 12355 | year: { |
| 12356 | size: 3.154e10, |
| 12357 | maxStep: false |
| 12358 | } |
| 12359 | }; |
| 12360 | |
| 12361 | var defaultConfig = { |
| 12362 | position: 'bottom', |
| 12363 | |
| 12364 | time: { |
| 12365 | 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 |
| 12366 | format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ |
| 12367 | unit: false, // false == automatic or override with week, month, year, etc. |
| 12368 | round: false, // none, or override with week, month, year, etc. |
| 12369 | displayFormat: false, // DEPRECATED |
| 12370 | isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ |
| 12371 | minUnit: 'millisecond', |
| 12372 | |
| 12373 | // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ |
| 12374 | displayFormats: { |
| 12375 | millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, |
| 12376 | second: 'h:mm:ss a', // 11:20:01 AM |
| 12377 | minute: 'h:mm:ss a', // 11:20:01 AM |
| 12378 | hour: 'MMM D, hA', // Sept 4, 5PM |
| 12379 | day: 'll', // Sep 4 2015 |
| 12380 | week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? |
| 12381 | month: 'MMM YYYY', // Sept 2015 |
| 12382 | quarter: '[Q]Q - YYYY', // Q3 |
| 12383 | year: 'YYYY' // 2015 |
| 12384 | }, |
| 12385 | }, |
| 12386 | ticks: { |
| 12387 | autoSkip: false |
| 12388 | } |
| 12389 | }; |
| 12390 | |
| 12391 | /** |
| 12392 | * Helper function to parse time to a moment object |
| 12393 | * @param axis {TimeAxis} the time axis |
| 12394 | * @param label {Date|string|number|Moment} The thing to parse |
| 12395 | * @return {Moment} parsed time |
| 12396 | */ |
| 12397 | function parseTime(axis, label) { |
| 12398 | var timeOpts = axis.options.time; |
| 12399 | if (typeof timeOpts.parser === 'string') { |
| 12400 | return moment(label, timeOpts.parser); |
| 12401 | } |
| 12402 | if (typeof timeOpts.parser === 'function') { |
| 12403 | return timeOpts.parser(label); |
| 12404 | } |
| 12405 | if (typeof label.getMonth === 'function' || typeof label === 'number') { |
| 12406 | // Date objects |
| 12407 | return moment(label); |
| 12408 | } |
| 12409 | if (label.isValid && label.isValid()) { |
| 12410 | // Moment support |
| 12411 | return label; |
| 12412 | } |
| 12413 | var format = timeOpts.format; |
| 12414 | if (typeof format !== 'string' && format.call) { |
| 12415 | // Custom parsing (return an instance of moment) |
| 12416 | console.warn('options.time.format is deprecated and replaced by options.time.parser.'); |
| 12417 | return format(label); |
| 12418 | } |
| 12419 | // Moment format parsing |
| 12420 | return moment(label, format); |
| 12421 | } |
| 12422 | |
| 12423 | /** |
| 12424 | * Figure out which is the best unit for the scale |
| 12425 | * @param minUnit {String} minimum unit to use |
| 12426 | * @param min {Number} scale minimum |
| 12427 | * @param max {Number} scale maximum |
| 12428 | * @return {String} the unit to use |
| 12429 | */ |
| 12430 | function determineUnit(minUnit, min, max, maxTicks) { |
| 12431 | var units = Object.keys(interval); |
| 12432 | var unit; |
| 12433 | var numUnits = units.length; |
| 12434 | |
| 12435 | for (var i = units.indexOf(minUnit); i < numUnits; i++) { |
| 12436 | unit = units[i]; |
| 12437 | var unitDetails = interval[unit]; |
| 12438 | var steps = (unitDetails.steps && unitDetails.steps[unitDetails.steps.length - 1]) || unitDetails.maxStep; |
| 12439 | if (steps === undefined || Math.ceil((max - min) / (steps * unitDetails.size)) <= maxTicks) { |
| 12440 | break; |
| 12441 | } |
| 12442 | } |
| 12443 | |
| 12444 | return unit; |
| 12445 | } |
| 12446 | |
| 12447 | /** |
| 12448 | * Determines how we scale the unit |
| 12449 | * @param min {Number} the scale minimum |
| 12450 | * @param max {Number} the scale maximum |
| 12451 | * @param unit {String} the unit determined by the {@see determineUnit} method |
| 12452 | * @return {Number} the axis step size as a multiple of unit |
| 12453 | */ |
| 12454 | function determineStepSize(min, max, unit, maxTicks) { |
| 12455 | // Using our unit, figoure out what we need to scale as |
| 12456 | var unitDefinition = interval[unit]; |
| 12457 | var unitSizeInMilliSeconds = unitDefinition.size; |
| 12458 | var sizeInUnits = Math.ceil((max - min) / unitSizeInMilliSeconds); |
| 12459 | var multiplier = 1; |
| 12460 | var range = max - min; |
| 12461 | |
| 12462 | if (unitDefinition.steps) { |
| 12463 | // Have an array of steps |
| 12464 | var numSteps = unitDefinition.steps.length; |
| 12465 | for (var i = 0; i < numSteps && sizeInUnits > maxTicks; i++) { |
| 12466 | multiplier = unitDefinition.steps[i]; |
| 12467 | sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier)); |
| 12468 | } |
| 12469 | } else { |
| 12470 | while (sizeInUnits > maxTicks && maxTicks > 0) { |
| 12471 | ++multiplier; |
| 12472 | sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier)); |
| 12473 | } |
| 12474 | } |
| 12475 | |
| 12476 | return multiplier; |
| 12477 | } |
| 12478 | |
| 12479 | /** |
| 12480 | * Helper for generating axis labels. |
| 12481 | * @param options {ITimeGeneratorOptions} the options for generation |
| 12482 | * @param dataRange {IRange} the data range |
| 12483 | * @param niceRange {IRange} the pretty range to display |
| 12484 | * @return {Number[]} ticks |
| 12485 | */ |
| 12486 | function generateTicks(options, dataRange, niceRange) { |
| 12487 | var ticks = []; |
| 12488 | if (options.maxTicks) { |
| 12489 | var stepSize = options.stepSize; |
| 12490 | ticks.push(options.min !== undefined ? options.min : niceRange.min); |
| 12491 | var cur = moment(niceRange.min); |
| 12492 | while (cur.add(stepSize, options.unit).valueOf() < niceRange.max) { |
| 12493 | ticks.push(cur.valueOf()); |
| 12494 | } |
| 12495 | var realMax = options.max || niceRange.max; |
| 12496 | if (ticks[ticks.length - 1] !== realMax) { |
| 12497 | ticks.push(realMax); |
| 12498 | } |
| 12499 | } |
| 12500 | return ticks; |
| 12501 | } |
| 12502 | |
| 12503 | /** |
| 12504 | * @function Chart.Ticks.generators.time |
| 12505 | * @param options {ITimeGeneratorOptions} the options for generation |
| 12506 | * @param dataRange {IRange} the data range |
| 12507 | * @return {Number[]} ticks |
| 12508 | */ |
| 12509 | Chart.Ticks.generators.time = function(options, dataRange) { |
| 12510 | var niceMin; |
| 12511 | var niceMax; |
| 12512 | var isoWeekday = options.isoWeekday; |
| 12513 | if (options.unit === 'week' && isoWeekday !== false) { |
| 12514 | niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf(); |
| 12515 | niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday); |
| 12516 | if (dataRange.max - niceMax > 0) { |
| 12517 | niceMax.add(1, 'week'); |
| 12518 | } |
| 12519 | niceMax = niceMax.valueOf(); |
| 12520 | } else { |
| 12521 | niceMin = moment(dataRange.min).startOf(options.unit).valueOf(); |
| 12522 | niceMax = moment(dataRange.max).startOf(options.unit); |
| 12523 | if (dataRange.max - niceMax > 0) { |
| 12524 | niceMax.add(1, options.unit); |
| 12525 | } |
| 12526 | niceMax = niceMax.valueOf(); |
| 12527 | } |
| 12528 | return generateTicks(options, dataRange, { |
| 12529 | min: niceMin, |
| 12530 | max: niceMax |
| 12531 | }); |
| 12532 | }; |
| 12533 | |
| 12534 | var TimeScale = Chart.Scale.extend({ |
| 12535 | initialize: function() { |
| 12536 | if (!moment) { |
| 12537 | 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'); |
| 12538 | } |
| 12539 | |
| 12540 | Chart.Scale.prototype.initialize.call(this); |
| 12541 | }, |
| 12542 | determineDataLimits: function() { |
| 12543 | var me = this; |
| 12544 | var timeOpts = me.options.time; |
| 12545 | |
| 12546 | // We store the data range as unix millisecond timestamps so dataMin and dataMax will always be integers. |
| 12547 | var dataMin = Number.MAX_SAFE_INTEGER; |
| 12548 | var dataMax = Number.MIN_SAFE_INTEGER; |
| 12549 | |
| 12550 | var chartData = me.chart.data; |
| 12551 | var parsedData = { |
| 12552 | labels: [], |
| 12553 | datasets: [] |
| 12554 | }; |
| 12555 | |
| 12556 | var timestamp; |
| 12557 | |
| 12558 | helpers.each(chartData.labels, function(label, labelIndex) { |
| 12559 | var labelMoment = parseTime(me, label); |
| 12560 | |
| 12561 | if (labelMoment.isValid()) { |
| 12562 | // We need to round the time |
| 12563 | if (timeOpts.round) { |
| 12564 | labelMoment.startOf(timeOpts.round); |
| 12565 | } |
| 12566 | |
| 12567 | timestamp = labelMoment.valueOf(); |
| 12568 | dataMin = Math.min(timestamp, dataMin); |
| 12569 | dataMax = Math.max(timestamp, dataMax); |
| 12570 | |
| 12571 | // Store this value for later |
| 12572 | parsedData.labels[labelIndex] = timestamp; |
| 12573 | } |
| 12574 | }); |
| 12575 | |
| 12576 | helpers.each(chartData.datasets, function(dataset, datasetIndex) { |
| 12577 | var timestamps = []; |
| 12578 | |
| 12579 | if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null && me.chart.isDatasetVisible(datasetIndex)) { |
| 12580 | // We have potential point data, so we need to parse this |
| 12581 | helpers.each(dataset.data, function(value, dataIndex) { |
| 12582 | var dataMoment = parseTime(me, me.getRightValue(value)); |
| 12583 | |
| 12584 | if (dataMoment.isValid()) { |
| 12585 | if (timeOpts.round) { |
| 12586 | dataMoment.startOf(timeOpts.round); |
| 12587 | } |
| 12588 | |
| 12589 | timestamp = dataMoment.valueOf(); |
| 12590 | dataMin = Math.min(timestamp, dataMin); |
| 12591 | dataMax = Math.max(timestamp, dataMax); |
| 12592 | timestamps[dataIndex] = timestamp; |
| 12593 | } |
| 12594 | }); |
| 12595 | } else { |
| 12596 | // We have no x coordinates, so use the ones from the labels |
| 12597 | timestamps = parsedData.labels.slice(); |
| 12598 | } |
| 12599 | |
| 12600 | parsedData.datasets[datasetIndex] = timestamps; |
| 12601 | }); |
| 12602 | |
| 12603 | me.dataMin = dataMin; |
| 12604 | me.dataMax = dataMax; |
| 12605 | me._parsedData = parsedData; |
| 12606 | }, |
| 12607 | buildTicks: function() { |
| 12608 | var me = this; |
| 12609 | var timeOpts = me.options.time; |
| 12610 | |
| 12611 | var minTimestamp; |
| 12612 | var maxTimestamp; |
| 12613 | var dataMin = me.dataMin; |
| 12614 | var dataMax = me.dataMax; |
| 12615 | |
| 12616 | if (timeOpts.min) { |
| 12617 | var minMoment = parseTime(me, timeOpts.min); |
| 12618 | if (timeOpts.round) { |
| 12619 | minMoment.round(timeOpts.round); |
| 12620 | } |
| 12621 | minTimestamp = minMoment.valueOf(); |
| 12622 | } |
| 12623 | |
| 12624 | if (timeOpts.max) { |
| 12625 | maxTimestamp = parseTime(me, timeOpts.max).valueOf(); |
| 12626 | } |
| 12627 | |
| 12628 | var maxTicks = me.getLabelCapacity(minTimestamp || dataMin); |
| 12629 | var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, minTimestamp || dataMin, maxTimestamp || dataMax, maxTicks); |
| 12630 | me.displayFormat = timeOpts.displayFormats[unit]; |
| 12631 | |
| 12632 | var stepSize = timeOpts.stepSize || determineStepSize(minTimestamp || dataMin, maxTimestamp || dataMax, unit, maxTicks); |
| 12633 | me.ticks = Chart.Ticks.generators.time({ |
| 12634 | maxTicks: maxTicks, |
| 12635 | min: minTimestamp, |
| 12636 | max: maxTimestamp, |
| 12637 | stepSize: stepSize, |
| 12638 | unit: unit, |
| 12639 | isoWeekday: timeOpts.isoWeekday |
| 12640 | }, { |
| 12641 | min: dataMin, |
| 12642 | max: dataMax |
| 12643 | }); |
| 12644 | |
| 12645 | // At this point, we need to update our max and min given the tick values since we have expanded the |
| 12646 | // range of the scale |
| 12647 | me.max = helpers.max(me.ticks); |
| 12648 | me.min = helpers.min(me.ticks); |
| 12649 | }, |
| 12650 | // Get tooltip label |
| 12651 | getLabelForIndex: function(index, datasetIndex) { |
| 12652 | var me = this; |
| 12653 | var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : ''; |
| 12654 | var value = me.chart.data.datasets[datasetIndex].data[index]; |
| 12655 | |
| 12656 | if (value !== null && typeof value === 'object') { |
| 12657 | label = me.getRightValue(value); |
| 12658 | } |
| 12659 | |
| 12660 | // Format nicely |
| 12661 | if (me.options.time.tooltipFormat) { |
| 12662 | label = parseTime(me, label).format(me.options.time.tooltipFormat); |
| 12663 | } |
| 12664 | |
| 12665 | return label; |
| 12666 | }, |
| 12667 | // Function to format an individual tick mark |
| 12668 | tickFormatFunction: function(tick, index, ticks) { |
| 12669 | var formattedTick = tick.format(this.displayFormat); |
| 12670 | var tickOpts = this.options.ticks; |
| 12671 | var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback); |
| 12672 | |
| 12673 | if (callback) { |
| 12674 | return callback(formattedTick, index, ticks); |
| 12675 | } |
| 12676 | return formattedTick; |
| 12677 | }, |
| 12678 | convertTicksToLabels: function() { |
| 12679 | var me = this; |
| 12680 | me.ticksAsTimestamps = me.ticks; |
| 12681 | me.ticks = me.ticks.map(function(tick) { |
| 12682 | return moment(tick); |
| 12683 | }).map(me.tickFormatFunction, me); |
| 12684 | }, |
| 12685 | getPixelForOffset: function(offset) { |
| 12686 | var me = this; |
| 12687 | var epochWidth = me.max - me.min; |
| 12688 | var decimal = epochWidth ? (offset - me.min) / epochWidth : 0; |
| 12689 | |
| 12690 | if (me.isHorizontal()) { |
| 12691 | var valueOffset = (me.width * decimal); |
| 12692 | return me.left + Math.round(valueOffset); |
| 12693 | } |
| 12694 | |
| 12695 | var heightOffset = (me.height * decimal); |
| 12696 | return me.top + Math.round(heightOffset); |
| 12697 | }, |
| 12698 | getPixelForValue: function(value, index, datasetIndex) { |
| 12699 | var me = this; |
| 12700 | var offset = null; |
| 12701 | if (index !== undefined && datasetIndex !== undefined) { |
| 12702 | offset = me._parsedData.datasets[datasetIndex][index]; |
| 12703 | } |
| 12704 | |
| 12705 | if (offset === null) { |
| 12706 | if (!value || !value.isValid) { |
| 12707 | // not already a moment object |
| 12708 | value = parseTime(me, me.getRightValue(value)); |
| 12709 | } |
| 12710 | |
| 12711 | if (value && value.isValid && value.isValid()) { |
| 12712 | offset = value.valueOf(); |
| 12713 | } |
| 12714 | } |
| 12715 | |
| 12716 | if (offset !== null) { |
| 12717 | return me.getPixelForOffset(offset); |
| 12718 | } |
| 12719 | }, |
| 12720 | getPixelForTick: function(index) { |
| 12721 | return this.getPixelForOffset(this.ticksAsTimestamps[index]); |
| 12722 | }, |
| 12723 | getValueForPixel: function(pixel) { |
| 12724 | var me = this; |
| 12725 | var innerDimension = me.isHorizontal() ? me.width : me.height; |
| 12726 | var offset = (pixel - (me.isHorizontal() ? me.left : me.top)) / innerDimension; |
| 12727 | return moment(me.min + (offset * (me.max - me.min))); |
| 12728 | }, |
| 12729 | // Crude approximation of what the label width might be |
| 12730 | getLabelWidth: function(label) { |
| 12731 | var me = this; |
| 12732 | var ticks = me.options.ticks; |
| 12733 | |
| 12734 | var tickLabelWidth = me.ctx.measureText(label).width; |
| 12735 | var cosRotation = Math.cos(helpers.toRadians(ticks.maxRotation)); |
| 12736 | var sinRotation = Math.sin(helpers.toRadians(ticks.maxRotation)); |
| 12737 | var tickFontSize = helpers.getValueOrDefault(ticks.fontSize, Chart.defaults.global.defaultFontSize); |
| 12738 | return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); |
| 12739 | }, |
| 12740 | getLabelCapacity: function(exampleTime) { |
| 12741 | var me = this; |
| 12742 | |
| 12743 | me.displayFormat = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation |
| 12744 | var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []); |
| 12745 | var tickLabelWidth = me.getLabelWidth(exampleLabel); |
| 12746 | |
| 12747 | var innerWidth = me.isHorizontal() ? me.width : me.height; |
| 12748 | var labelCapacity = innerWidth / tickLabelWidth; |
| 12749 | return labelCapacity; |
| 12750 | } |
| 12751 | }); |
| 12752 | Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); |
| 12753 | |
| 12754 | }; |
Jian Li | 46770fc | 2016-08-03 02:32:45 +0900 | [diff] [blame] | 12755 | |
| 12756 | },{"1":1}]},{},[7])(7) |
| 12757 | }); |