1/*! 2 * Chart.js 3 * http://chartjs.org/ 4 * Version: 2.7.3 5 * 6 * Copyright 2018 Chart.js Contributors 7 * Released under the MIT license 8 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md 9 */ 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(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ 11/* MIT license */ 12var colorNames = require(5); 13 14module.exports = { 15 getRgba: getRgba, 16 getHsla: getHsla, 17 getRgb: getRgb, 18 getHsl: getHsl, 19 getHwb: getHwb, 20 getAlpha: getAlpha, 21 22 hexString: hexString, 23 rgbString: rgbString, 24 rgbaString: rgbaString, 25 percentString: percentString, 26 percentaString: percentaString, 27 hslString: hslString, 28 hslaString: hslaString, 29 hwbString: hwbString, 30 keyword: keyword 31} 32 33function getRgba(string) { 34 if (!string) { 35 return; 36 } 37 var abbr = /^#([a-fA-F0-9]{3})$/i, 38 hex = /^#([a-fA-F0-9]{6})$/i, 39 rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, 40 per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i, 41 keyword = /(\w+)/; 42 43 var rgb = [0, 0, 0], 44 a = 1, 45 match = string.match(abbr); 46 if (match) { 47 match = match[1]; 48 for (var i = 0; i < rgb.length; i++) { 49 rgb[i] = parseInt(match[i] + match[i], 16); 50 } 51 } 52 else if (match = string.match(hex)) { 53 match = match[1]; 54 for (var i = 0; i < rgb.length; i++) { 55 rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); 56 } 57 } 58 else if (match = string.match(rgba)) { 59 for (var i = 0; i < rgb.length; i++) { 60 rgb[i] = parseInt(match[i + 1]); 61 } 62 a = parseFloat(match[4]); 63 } 64 else if (match = string.match(per)) { 65 for (var i = 0; i < rgb.length; i++) { 66 rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); 67 } 68 a = parseFloat(match[4]); 69 } 70 else if (match = string.match(keyword)) { 71 if (match[1] == "transparent") { 72 return [0, 0, 0, 0]; 73 } 74 rgb = colorNames[match[1]]; 75 if (!rgb) { 76 return; 77 } 78 } 79 80 for (var i = 0; i < rgb.length; i++) { 81 rgb[i] = scale(rgb[i], 0, 255); 82 } 83 if (!a && a != 0) { 84 a = 1; 85 } 86 else { 87 a = scale(a, 0, 1); 88 } 89 rgb[3] = a; 90 return rgb; 91} 92 93function getHsla(string) { 94 if (!string) { 95 return; 96 } 97 var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; 98 var match = string.match(hsl); 99 if (match) { 100 var alpha = parseFloat(match[4]); 101 var h = scale(parseInt(match[1]), 0, 360), 102 s = scale(parseFloat(match[2]), 0, 100), 103 l = scale(parseFloat(match[3]), 0, 100), 104 a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); 105 return [h, s, l, a]; 106 } 107} 108 109function getHwb(string) { 110 if (!string) { 111 return; 112 } 113 var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; 114 var match = string.match(hwb); 115 if (match) { 116 var alpha = parseFloat(match[4]); 117 var h = scale(parseInt(match[1]), 0, 360), 118 w = scale(parseFloat(match[2]), 0, 100), 119 b = scale(parseFloat(match[3]), 0, 100), 120 a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); 121 return [h, w, b, a]; 122 } 123} 124 125function getRgb(string) { 126 var rgba = getRgba(string); 127 return rgba && rgba.slice(0, 3); 128} 129 130function getHsl(string) { 131 var hsla = getHsla(string); 132 return hsla && hsla.slice(0, 3); 133} 134 135function getAlpha(string) { 136 var vals = getRgba(string); 137 if (vals) { 138 return vals[3]; 139 } 140 else if (vals = getHsla(string)) { 141 return vals[3]; 142 } 143 else if (vals = getHwb(string)) { 144 return vals[3]; 145 } 146} 147 148// generators 149function hexString(rgb) { 150 return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) 151 + hexDouble(rgb[2]); 152} 153 154function rgbString(rgba, alpha) {
vendor: 14,231 bytes, lines 155-759
155 if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { 156 return rgbaString(rgba, alpha); 157 } 158 return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; 159} 160 161function rgbaString(rgba, alpha) { 162 if (alpha === undefined) { 163 alpha = (rgba[3] !== undefined ? rgba[3] : 1); 164 } 165 return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] 166 + ", " + alpha + ")"; 167} 168 169function percentString(rgba, alpha) { 170 if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { 171 return percentaString(rgba, alpha); 172 } 173 var r = Math.round(rgba[0]/255 * 100), 174 g = Math.round(rgba[1]/255 * 100), 175 b = Math.round(rgba[2]/255 * 100); 176 177 return "rgb(" + r + "%, " + g + "%, " + b + "%)"; 178} 179 180function percentaString(rgba, alpha) { 181 var r = Math.round(rgba[0]/255 * 100), 182 g = Math.round(rgba[1]/255 * 100), 183 b = Math.round(rgba[2]/255 * 100); 184 return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; 185} 186 187function hslString(hsla, alpha) { 188 if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { 189 return hslaString(hsla, alpha); 190 } 191 return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; 192} 193 194function hslaString(hsla, alpha) { 195 if (alpha === undefined) { 196 alpha = (hsla[3] !== undefined ? hsla[3] : 1); 197 } 198 return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " 199 + alpha + ")"; 200} 201 202// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax 203// (hwb have alpha optional & 1 is default value) 204function hwbString(hwb, alpha) { 205 if (alpha === undefined) { 206 alpha = (hwb[3] !== undefined ? hwb[3] : 1); 207 } 208 return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" 209 + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; 210} 211 212function keyword(rgb) { 213 return reverseNames[rgb.slice(0, 3)]; 214} 215 216// helpers 217function scale(num, min, max) { 218 return Math.min(Math.max(min, num), max); 219} 220 221function hexDouble(num) { 222 var str = num.toString(16).toUpperCase(); 223 return (str.length < 2) ? "0" + str : str; 224} 225 226 227//create a list of reverse color names 228var reverseNames = {}; 229for (var name in colorNames) { 230 reverseNames[colorNames[name]] = name; 231} 232 233},{"5":5}],2:[function(require,module,exports){ 234/* MIT license */ 235var convert = require(4); 236var string = require(1); 237 238var Color = function (obj) { 239 if (obj instanceof Color) { 240 return obj; 241 } 242 if (!(this instanceof Color)) { 243 return new Color(obj); 244 } 245 246 this.valid = false; 247 this.values = { 248 rgb: [0, 0, 0], 249 hsl: [0, 0, 0], 250 hsv: [0, 0, 0], 251 hwb: [0, 0, 0], 252 cmyk: [0, 0, 0, 0], 253 alpha: 1 254 }; 255 256 // parse Color() argument 257 var vals; 258 if (typeof obj === 'string') { 259 vals = string.getRgba(obj); 260 if (vals) { 261 this.setValues('rgb', vals); 262 } else if (vals = string.getHsla(obj)) { 263 this.setValues('hsl', vals); 264 } else if (vals = string.getHwb(obj)) { 265 this.setValues('hwb', vals); 266 } 267 } else if (typeof obj === 'object') { 268 vals = obj; 269 if (vals.r !== undefined || vals.red !== undefined) { 270 this.setValues('rgb', vals); 271 } else if (vals.l !== undefined || vals.lightness !== undefined) { 272 this.setValues('hsl', vals); 273 } else if (vals.v !== undefined || vals.value !== undefined) { 274 this.setValues('hsv', vals); 275 } else if (vals.w !== undefined || vals.whiteness !== undefined) { 276 this.setValues('hwb', vals); 277 } else if (vals.c !== undefined || vals.cyan !== undefined) { 278 this.setValues('cmyk', vals); 279 } 280 } 281}; 282 283Color.prototype = { 284 isValid: function () { 285 return this.valid; 286 }, 287 rgb: function () { 288 return this.setSpace('rgb', arguments); 289 }, 290 hsl: function () { 291 return this.setSpace('hsl', arguments); 292 }, 293 hsv: function () { 294 return this.setSpace('hsv', arguments); 295 }, 296 hwb: function () { 297 return this.setSpace('hwb', arguments); 298 }, 299 cmyk: function () { 300 return this.setSpace('cmyk', arguments); 301 }, 302 303 rgbArray: function () { 304 return this.values.rgb; 305 }, 306 hslArray: function () { 307 return this.values.hsl; 308 }, 309 hsvArray: function () { 310 return this.values.hsv; 311 }, 312 hwbArray: function () { 313 var values = this.values; 314 if (values.alpha !== 1) { 315 return values.hwb.concat([values.alpha]); 316 } 317 return values.hwb; 318 }, 319 cmykArray: function () { 320 return this.values.cmyk; 321 }, 322 rgbaArray: function () { 323 var values = this.values; 324 return values.rgb.concat([values.alpha]); 325 }, 326 hslaArray: function () { 327 var values = this.values; 328 return values.hsl.concat([values.alpha]); 329 }, 330 alpha: function (val) { 331 if (val === undefined) { 332 return this.values.alpha; 333 } 334 this.setValues('alpha', val); 335 return this; 336 }, 337 338 red: function (val) { 339 return this.setChannel('rgb', 0, val); 340 }, 341 green: function (val) { 342 return this.setChannel('rgb', 1, val); 343 }, 344 blue: function (val) { 345 return this.setChannel('rgb', 2, val); 346 }, 347 hue: function (val) { 348 if (val) { 349 val %= 360; 350 val = val < 0 ? 360 + val : val; 351 } 352 return this.setChannel('hsl', 0, val); 353 }, 354 saturation: function (val) { 355 return this.setChannel('hsl', 1, val); 356 }, 357 lightness: function (val) { 358 return this.setChannel('hsl', 2, val); 359 }, 360 saturationv: function (val) { 361 return this.setChannel('hsv', 1, val); 362 }, 363 whiteness: function (val) { 364 return this.setChannel('hwb', 1, val); 365 }, 366 blackness: function (val) { 367 return this.setChannel('hwb', 2, val); 368 }, 369 value: function (val) { 370 return this.setChannel('hsv', 2, val); 371 }, 372 cyan: function (val) { 373 return this.setChannel('cmyk', 0, val); 374 }, 375 magenta: function (val) { 376 return this.setChannel('cmyk', 1, val); 377 }, 378 yellow: function (val) { 379 return this.setChannel('cmyk', 2, val); 380 }, 381 black: function (val) { 382 return this.setChannel('cmyk', 3, val); 383 }, 384 385 hexString: function () { 386 return string.hexString(this.values.rgb); 387 }, 388 rgbString: function () { 389 return string.rgbString(this.values.rgb, this.values.alpha); 390 }, 391 rgbaString: function () { 392 return string.rgbaString(this.values.rgb, this.values.alpha); 393 }, 394 percentString: function () { 395 return string.percentString(this.values.rgb, this.values.alpha); 396 }, 397 hslString: function () { 398 return string.hslString(this.values.hsl, this.values.alpha); 399 }, 400 hslaString: function () { 401 return string.hslaString(this.values.hsl, this.values.alpha); 402 }, 403 hwbString: function () { 404 return string.hwbString(this.values.hwb, this.values.alpha); 405 }, 406 keyword: function () { 407 return string.keyword(this.values.rgb, this.values.alpha); 408 }, 409 410 rgbNumber: function () { 411 var rgb = this.values.rgb; 412 return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; 413 }, 414 415 luminosity: function () { 416 // http://www.w3.org/TR/WCAG20/#relativeluminancedef 417 var rgb = this.values.rgb; 418 var lum = []; 419 for (var i = 0; i < rgb.length; i++) { 420 var chan = rgb[i] / 255; 421 lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); 422 } 423 return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; 424 }, 425 426 contrast: function (color2) { 427 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef 428 var lum1 = this.luminosity(); 429 var lum2 = color2.luminosity(); 430 if (lum1 > lum2) { 431 return (lum1 + 0.05) / (lum2 + 0.05); 432 } 433 return (lum2 + 0.05) / (lum1 + 0.05); 434 }, 435 436 level: function (color2) { 437 var contrastRatio = this.contrast(color2); 438 if (contrastRatio >= 7.1) { 439 return 'AAA'; 440 } 441 442 return (contrastRatio >= 4.5) ? 'AA' : ''; 443 }, 444 445 dark: function () { 446 // YIQ equation from http://24ways.org/2010/calculating-color-contrast 447 var rgb = this.values.rgb; 448 var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; 449 return yiq < 128; 450 }, 451 452 light: function () { 453 return !this.dark(); 454 }, 455 456 negate: function () { 457 var rgb = []; 458 for (var i = 0; i < 3; i++) { 459 rgb[i] = 255 - this.values.rgb[i]; 460 } 461 this.setValues('rgb', rgb); 462 return this; 463 }, 464 465 lighten: function (ratio) { 466 var hsl = this.values.hsl; 467 hsl[2] += hsl[2] * ratio; 468 this.setValues('hsl', hsl); 469 return this; 470 }, 471 472 darken: function (ratio) { 473 var hsl = this.values.hsl; 474 hsl[2] -= hsl[2] * ratio; 475 this.setValues('hsl', hsl); 476 return this; 477 }, 478 479 saturate: function (ratio) { 480 var hsl = this.values.hsl; 481 hsl[1] += hsl[1] * ratio; 482 this.setValues('hsl', hsl); 483 return this; 484 }, 485 486 desaturate: function (ratio) { 487 var hsl = this.values.hsl; 488 hsl[1] -= hsl[1] * ratio; 489 this.setValues('hsl', hsl); 490 return this; 491 }, 492 493 whiten: function (ratio) { 494 var hwb = this.values.hwb; 495 hwb[1] += hwb[1] * ratio; 496 this.setValues('hwb', hwb); 497 return this; 498 }, 499 500 blacken: function (ratio) { 501 var hwb = this.values.hwb; 502 hwb[2] += hwb[2] * ratio; 503 this.setValues('hwb', hwb); 504 return this; 505 }, 506 507 greyscale: function () { 508 var rgb = this.values.rgb; 509 // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale 510 var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; 511 this.setValues('rgb', [val, val, val]); 512 return this; 513 }, 514 515 clearer: function (ratio) { 516 var alpha = this.values.alpha; 517 this.setValues('alpha', alpha - (alpha * ratio)); 518 return this; 519 }, 520 521 opaquer: function (ratio) { 522 var alpha = this.values.alpha; 523 this.setValues('alpha', alpha + (alpha * ratio)); 524 return this; 525 }, 526 527 rotate: function (degrees) { 528 var hsl = this.values.hsl; 529 var hue = (hsl[0] + degrees) % 360; 530 hsl[0] = hue < 0 ? 360 + hue : hue; 531 this.setValues('hsl', hsl); 532 return this; 533 }, 534 535 /** 536 * Ported from sass implementation in C 537 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 538 */ 539 mix: function (mixinColor, weight) { 540 var color1 = this; 541 var color2 = mixinColor; 542 var p = weight === undefined ? 0.5 : weight; 543 544 var w = 2 * p - 1; 545 var a = color1.alpha() - color2.alpha(); 546 547 var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; 548 var w2 = 1 - w1; 549 550 return this 551 .rgb( 552 w1 * color1.red() + w2 * color2.red(), 553 w1 * color1.green() + w2 * color2.green(), 554 w1 * color1.blue() + w2 * color2.blue() 555 ) 556 .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); 557 }, 558 559 toJSON: function () { 560 return this.rgb(); 561 }, 562 563 clone: function () { 564 // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, 565 // making the final build way to big to embed in Chart.js. So let's do it manually, 566 // assuming that values to clone are 1 dimension arrays containing only numbers, 567 // except 'alpha' which is a number. 568 var result = new Color(); 569 var source = this.values; 570 var target = result.values; 571 var value, type; 572 573 for (var prop in source) { 574 if (source.hasOwnProperty(prop)) { 575 value = source[prop]; 576 type = ({}).toString.call(value); 577 if (type === '[object Array]') { 578 target[prop] = value.slice(0); 579 } else if (type === '[object Number]') { 580 target[prop] = value; 581 } else { 582 console.error('unexpected color value:', value); 583 } 584 } 585 } 586 587 return result; 588 } 589}; 590 591Color.prototype.spaces = { 592 rgb: ['red', 'green', 'blue'], 593 hsl: ['hue', 'saturation', 'lightness'], 594 hsv: ['hue', 'saturation', 'value'], 595 hwb: ['hue', 'whiteness', 'blackness'], 596 cmyk: ['cyan', 'magenta', 'yellow', 'black'] 597}; 598 599Color.prototype.maxes = { 600 rgb: [255, 255, 255], 601 hsl: [360, 100, 100], 602 hsv: [360, 100, 100], 603 hwb: [360, 100, 100], 604 cmyk: [100, 100, 100, 100] 605}; 606 607Color.prototype.getValues = function (space) { 608 var values = this.values; 609 var vals = {}; 610 611 for (var i = 0; i < space.length; i++) { 612 vals[space.charAt(i)] = values[space][i]; 613 } 614 615 if (values.alpha !== 1) { 616 vals.a = values.alpha; 617 } 618 619 // {r: 255, g: 255, b: 255, a: 0.4} 620 return vals; 621}; 622 623Color.prototype.setValues = function (space, vals) { 624 var values = this.values; 625 var spaces = this.spaces; 626 var maxes = this.maxes; 627 var alpha = 1; 628 var i; 629 630 this.valid = true; 631 632 if (space === 'alpha') { 633 alpha = vals; 634 } else if (vals.length) { 635 // [10, 10, 10] 636 values[space] = vals.slice(0, space.length); 637 alpha = vals[space.length]; 638 } else if (vals[space.charAt(0)] !== undefined) { 639 // {r: 10, g: 10, b: 10} 640 for (i = 0; i < space.length; i++) { 641 values[space][i] = vals[space.charAt(i)]; 642 } 643 644 alpha = vals.a; 645 } else if (vals[spaces[space][0]] !== undefined) { 646 // {red: 10, green: 10, blue: 10} 647 var chans = spaces[space]; 648 649 for (i = 0; i < space.length; i++) { 650 values[space][i] = vals[chans[i]]; 651 } 652 653 alpha = vals.alpha; 654 } 655 656 values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); 657 658 if (space === 'alpha') { 659 return false; 660 } 661 662 var capped; 663 664 // cap values of the space prior converting all values 665 for (i = 0; i < space.length; i++) { 666 capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); 667 values[space][i] = Math.round(capped); 668 } 669 670 // convert to all the other color spaces 671 for (var sname in spaces) { 672 if (sname !== space) { 673 values[sname] = convert[space][sname](values[space]); 674 } 675 } 676 677 return true; 678}; 679 680Color.prototype.setSpace = function (space, args) { 681 var vals = args[0]; 682 683 if (vals === undefined) { 684 // color.rgb() 685 return this.getValues(space); 686 } 687 688 // color.rgb(10, 10, 10) 689 if (typeof vals === 'number') { 690 vals = Array.prototype.slice.call(args); 691 } 692 693 this.setValues(space, vals); 694 return this; 695}; 696 697Color.prototype.setChannel = function (space, index, val) { 698 var svalues = this.values[space]; 699 if (val === undefined) { 700 // color.red() 701 return svalues[index]; 702 } else if (val === svalues[index]) { 703 // color.red(color.red()) 704 return this; 705 } 706 707 // color.red(100) 708 svalues[index] = val; 709 this.setValues(space, svalues); 710 711 return this; 712}; 713 714if (typeof window !== 'undefined') { 715 window.Color = Color; 716} 717 718module.exports = Color; 719 720},{"1":1,"4":4}],3:[function(require,module,exports){ 721/* MIT license */ 722 723module.exports = { 724 rgb2hsl: rgb2hsl, 725 rgb2hsv: rgb2hsv, 726 rgb2hwb: rgb2hwb, 727 rgb2cmyk: rgb2cmyk, 728 rgb2keyword: rgb2keyword, 729 rgb2xyz: rgb2xyz, 730 rgb2lab: rgb2lab, 731 rgb2lch: rgb2lch, 732 733 hsl2rgb: hsl2rgb, 734 hsl2hsv: hsl2hsv, 735 hsl2hwb: hsl2hwb, 736 hsl2cmyk: hsl2cmyk, 737 hsl2keyword: hsl2keyword, 738 739 hsv2rgb: hsv2rgb, 740 hsv2hsl: hsv2hsl, 741 hsv2hwb: hsv2hwb, 742 hsv2cmyk: hsv2cmyk, 743 hsv2keyword: hsv2keyword, 744 745 hwb2rgb: hwb2rgb, 746 hwb2hsl: hwb2hsl, 747 hwb2hsv: hwb2hsv, 748 hwb2cmyk: hwb2cmyk, 749 hwb2keyword: hwb2keyword, 750 751 cmyk2rgb: cmyk2rgb, 752 cmyk2hsl: cmyk2hsl, 753 cmyk2hsv: cmyk2hsv, 754 cmyk2hwb: cmyk2hwb, 755 cmyk2keyword: cmyk2keyword, 756 757 keyword2rgb: keyword2rgb, 758 keyword2hsl: keyword2hsl, 759 keyword2hsv: keyword2hsv,
760 keyword2hwb: keyword2hwb, 761 keyword2cmyk: keyword2cmyk, 762 keyword2lab: keyword2lab, 763 keyword2xyz: keyword2xyz, 764 765 xyz2rgb: xyz2rgb, 766 xyz2lab: xyz2lab, 767 xyz2lch: xyz2lch, 768 769 lab2xyz: lab2xyz, 770 lab2rgb: lab2rgb, 771 lab2lch: lab2lch, 772 773 lch2lab: lch2lab, 774 lch2xyz: lch2xyz, 775 lch2rgb: lch2rgb 776} 777 778 779function rgb2hsl(rgb) { 780 var r = rgb[0]/255, 781 g = rgb[1]/255, 782 b = rgb[2]/255, 783 min = Math.min(r, g, b), 784 max = Math.max(r, g, b), 785 delta = max - min, 786 h, s, l; 787 788 if (max == min) 789 h = 0; 790 else if (r == max) 791 h = (g - b) / delta; 792 else if (g == max) 793 h = 2 + (b - r) / delta; 794 else if (b == max) 795 h = 4 + (r - g)/ delta; 796 797 h = Math.min(h * 60, 360); 798 799 if (h < 0) 800 h += 360; 801 802 l = (min + max) / 2; 803 804 if (max == min) 805 s = 0; 806 else if (l <= 0.5) 807 s = delta / (max + min); 808 else 809 s = delta / (2 - max - min); 810 811 return [h, s * 100, l * 100]; 812} 813 814function rgb2hsv(rgb) { 815 var r = rgb[0], 816 g = rgb[1], 817 b = rgb[2], 818 min = Math.min(r, g, b), 819 max = Math.max(r, g, b), 820 delta = max - min, 821 h, s, v; 822 823 if (max == 0) 824 s = 0; 825 else 826 s = (delta/max * 1000)/10; 827 828 if (max == min) 829 h = 0; 830 else if (r == max) 831 h = (g - b) / delta; 832 else if (g == max) 833 h = 2 + (b - r) / delta; 834 else if (b == max) 835 h = 4 + (r - g) / delta; 836 837 h = Math.min(h * 60, 360); 838 839 if (h < 0) 840 h += 360; 841 842 v = ((max / 255) * 1000) / 10; 843 844 return [h, s, v]; 845} 846 847function rgb2hwb(rgb) { 848 var r = rgb[0], 849 g = rgb[1], 850 b = rgb[2], 851 h = rgb2hsl(rgb)[0], 852 w = 1/255 * Math.min(r, Math.min(g, b)), 853 b = 1 - 1/255 * Math.max(r, Math.max(g, b)); 854 855 return [h, w * 100, b * 100]; 856} 857 858function rgb2cmyk(rgb) { 859 var r = rgb[0] / 255, 860 g = rgb[1] / 255, 861 b = rgb[2] / 255, 862 c, m, y, k; 863 864 k = Math.min(1 - r, 1 - g, 1 - b); 865 c = (1 - r - k) / (1 - k) || 0; 866 m = (1 - g - k) / (1 - k) || 0; 867 y = (1 - b - k) / (1 - k) || 0; 868 return [c * 100, m * 100, y * 100, k * 100]; 869} 870 871function rgb2keyword(rgb) { 872 return reverseKeywords[JSON.stringify(rgb)]; 873} 874 875function rgb2xyz(rgb) { 876 var r = rgb[0] / 255, 877 g = rgb[1] / 255, 878 b = rgb[2] / 255; 879 880 // assume sRGB 881 r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); 882 g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); 883 b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); 884 885 var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); 886 var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); 887 var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); 888 889 return [x * 100, y *100, z * 100]; 890} 891 892function rgb2lab(rgb) { 893 var xyz = rgb2xyz(rgb), 894 x = xyz[0], 895 y = xyz[1], 896 z = xyz[2], 897 l, a, b; 898 899 x /= 95.047; 900 y /= 100; 901 z /= 108.883; 902 903 x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); 904 y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); 905 z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); 906 907 l = (116 * y) - 16; 908 a = 500 * (x - y); 909 b = 200 * (y - z); 910 911 return [l, a, b]; 912} 913 914function rgb2lch(args) { 915 return lab2lch(rgb2lab(args)); 916} 917 918function hsl2rgb(hsl) { 919 var h = hsl[0] / 360, 920 s = hsl[1] / 100, 921 l = hsl[2] / 100, 922 t1, t2, t3, rgb, val; 923 924 if (s == 0) { 925 val = l * 255; 926 return [val, val, val]; 927 } 928 929 if (l < 0.5) 930 t2 = l * (1 + s); 931 else 932 t2 = l + s - l * s; 933 t1 = 2 * l - t2; 934 935 rgb = [0, 0, 0]; 936 for (var i = 0; i < 3; i++) { 937 t3 = h + 1 / 3 * - (i - 1); 938 t3 < 0 && t3++; 939 t3 > 1 && t3--; 940 941 if (6 * t3 < 1) 942 val = t1 + (t2 - t1) * 6 * t3; 943 else if (2 * t3 < 1) 944 val = t2; 945 else if (3 * t3 < 2) 946 val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; 947 else 948 val = t1; 949 950 rgb[i] = val * 255; 951 } 952 953 return rgb; 954} 955 956function hsl2hsv(hsl) { 957 var h = hsl[0], 958 s = hsl[1] / 100, 959 l = hsl[2] / 100, 960 sv, v; 961 962 if(l === 0) { 963 // no need to do calc on black 964 // also avoids divide by 0 error 965 return [0, 0, 0]; 966 } 967 968 l *= 2; 969 s *= (l <= 1) ? l : 2 - l; 970 v = (l + s) / 2; 971 sv = (2 * s) / (l + s); 972 return [h, sv * 100, v * 100]; 973} 974 975function hsl2hwb(args) { 976 return rgb2hwb(hsl2rgb(args)); 977} 978 979function hsl2cmyk(args) { 980 return rgb2cmyk(hsl2rgb(args)); 981} 982 983function hsl2keyword(args) { 984 return rgb2keyword(hsl2rgb(args)); 985} 986 987 988function hsv2rgb(hsv) {
989 var h = hsv[0] / 60, 990 s = hsv[1] / 100, 991 v = hsv[2] / 100, 992 hi = Math.floor(h) % 6; 993 994 var f = h - Math.floor(h), 995 p = 255 * v * (1 - s), 996 q = 255 * v * (1 - (s * f)), 997 t = 255 * v * (1 - (s * (1 - f))), 998 v = 255 * v; 999 1000 switch(hi) { 1001 case 0: 1002 return [v, t, p]; 1003 case 1: 1004 return [q, v, p]; 1005 case 2: 1006 return [p, v, t]; 1007 case 3: 1008 return [p, q, v]; 1009 case 4: 1010 return [t, p, v]; 1011 case 5: 1012 return [v, p, q]; 1013 } 1014} 1015 1016function hsv2hsl(hsv) { 1017 var h = hsv[0], 1018 s = hsv[1] / 100, 1019 v = hsv[2] / 100, 1020 sl, l; 1021 1022 l = (2 - s) * v; 1023 sl = s * v; 1024 sl /= (l <= 1) ? l : 2 - l; 1025 sl = sl || 0; 1026 l /= 2; 1027 return [h, sl * 100, l * 100]; 1028} 1029 1030function hsv2hwb(args) { 1031 return rgb2hwb(hsv2rgb(args)) 1032} 1033 1034function hsv2cmyk(args) { 1035 return rgb2cmyk(hsv2rgb(args)); 1036} 1037 1038function hsv2keyword(args) { 1039 return rgb2keyword(hsv2rgb(args)); 1040} 1041 1042// http://dev.w3.org/csswg/css-color/#hwb-to-rgb 1043function hwb2rgb(hwb) { 1044 var h = hwb[0] / 360, 1045 wh = hwb[1] / 100, 1046 bl = hwb[2] / 100, 1047 ratio = wh + bl, 1048 i, v, f, n; 1049 1050 // wh + bl cant be > 1 1051 if (ratio > 1) { 1052 wh /= ratio; 1053 bl /= ratio; 1054 } 1055 1056 i = Math.floor(6 * h); 1057 v = 1 - bl; 1058 f = 6 * h - i; 1059 if ((i & 0x01) != 0) { 1060 f = 1 - f; 1061 } 1062 n = wh + f * (v - wh); // linear interpolation 1063 1064 switch (i) { 1065 default: 1066 case 6: 1067 case 0: r = v; g = n; b = wh; break; 1068 case 1: r = n; g = v; b = wh; break; 1069 case 2: r = wh; g = v; b = n; break; 1070 case 3: r = wh; g = n; b = v; break; 1071 case 4: r = n; g = wh; b = v; break; 1072 case 5: r = v; g = wh; b = n; break; 1073 } 1074 1075 return [r * 255, g * 255, b * 255]; 1076} 1077 1078function hwb2hsl(args) { 1079 return rgb2hsl(hwb2rgb(args)); 1080} 1081 1082function hwb2hsv(args) { 1083 return rgb2hsv(hwb2rgb(args)); 1084} 1085 1086function hwb2cmyk(args) { 1087 return rgb2cmyk(hwb2rgb(args)); 1088} 1089 1090function hwb2keyword(args) { 1091 return rgb2keyword(hwb2rgb(args)); 1092} 1093 1094function cmyk2rgb(cmyk) { 1095 var c = cmyk[0] / 100, 1096 m = cmyk[1] / 100, 1097 y = cmyk[2] / 100, 1098 k = cmyk[3] / 100, 1099 r, g, b; 1100 1101 r = 1 - Math.min(1, c * (1 - k) + k); 1102 g = 1 - Math.min(1, m * (1 - k) + k); 1103 b = 1 - Math.min(1, y * (1 - k) + k); 1104 return [r * 255, g * 255, b * 255]; 1105} 1106 1107function cmyk2hsl(args) { 1108 return rgb2hsl(cmyk2rgb(args)); 1109} 1110 1111function cmyk2hsv(args) { 1112 return rgb2hsv(cmyk2rgb(args)); 1113} 1114 1115function cmyk2hwb(args) { 1116 return rgb2hwb(cmyk2rgb(args)); 1117} 1118 1119function cmyk2keyword(args) { 1120 return rgb2keyword(cmyk2rgb(args)); 1121} 1122 1123 1124function xyz2rgb(xyz) { 1125 var x = xyz[0] / 100, 1126 y = xyz[1] / 100, 1127 z = xyz[2] / 100, 1128 r, g, b; 1129 1130 r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); 1131 g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); 1132 b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); 1133 1134 // assume sRGB 1135 r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) 1136 : r = (r * 12.92); 1137 1138 g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) 1139 : g = (g * 12.92); 1140 1141 b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) 1142 : b = (b * 12.92); 1143 1144 r = Math.min(Math.max(0, r), 1); 1145 g = Math.min(Math.max(0, g), 1); 1146 b = Math.min(Math.max(0, b), 1); 1147 1148 return [r * 255, g * 255, b * 255]; 1149} 1150 1151function xyz2lab(xyz) { 1152 var x = xyz[0], 1153 y = xyz[1], 1154 z = xyz[2], 1155 l, a, b; 1156 1157 x /= 95.047; 1158 y /= 100; 1159 z /= 108.883; 1160 1161 x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); 1162 y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); 1163 z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); 1164 1165 l = (116 * y) - 16; 1166 a = 500 * (x - y); 1167 b = 200 * (y - z); 1168 1169 return [l, a, b]; 1170} 1171 1172function xyz2lch(args) { 1173 return lab2lch(xyz2lab(args)); 1174} 1175 1176function lab2xyz(lab) { 1177 var l = lab[0], 1178 a = lab[1], 1179 b = lab[2], 1180 x, y, z, y2; 1181 1182 if (l <= 8) { 1183 y = (l * 100) / 903.3; 1184 y2 = (7.787 * (y / 100)) + (16 / 116); 1185 } else { 1186 y = 100 * Math.pow((l + 16) / 116, 3); 1187 y2 = Math.pow(y / 100, 1/3); 1188 } 1189 1190 x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); 1191 1192 z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); 1193 1194 return [x, y, z]; 1195} 1196 1197function lab2lch(lab) { 1198 var l = lab[0], 1199 a = lab[1], 1200 b = lab[2], 1201 hr, h, c; 1202 1203 hr = Math.atan2(b, a);
1204 h = hr * 360 / 2 / Math.PI; 1205 if (h < 0) { 1206 h += 360; 1207 } 1208 c = Math.sqrt(a * a + b * b); 1209 return [l, c, h]; 1210} 1211 1212function lab2rgb(args) { 1213 return xyz2rgb(lab2xyz(args)); 1214} 1215 1216function lch2lab(lch) { 1217 var l = lch[0], 1218 c = lch[1], 1219 h = lch[2], 1220 a, b, hr; 1221 1222 hr = h / 360 * 2 * Math.PI; 1223 a = c * Math.cos(hr); 1224 b = c * Math.sin(hr); 1225 return [l, a, b]; 1226} 1227 1228function lch2xyz(args) { 1229 return lab2xyz(lch2lab(args)); 1230} 1231 1232function lch2rgb(args) { 1233 return lab2rgb(lch2lab(args)); 1234} 1235 1236function keyword2rgb(keyword) { 1237 return cssKeywords[keyword]; 1238} 1239 1240function keyword2hsl(args) { 1241 return rgb2hsl(keyword2rgb(args)); 1242} 1243 1244function keyword2hsv(args) { 1245 return rgb2hsv(keyword2rgb(args)); 1246} 1247 1248function keyword2hwb(args) { 1249 return rgb2hwb(keyword2rgb(args)); 1250} 1251 1252function keyword2cmyk(args) { 1253 return rgb2cmyk(keyword2rgb(args)); 1254} 1255 1256function keyword2lab(args) { 1257 return rgb2lab(keyword2rgb(args)); 1258} 1259 1260function keyword2xyz(args) { 1261 return rgb2xyz(keyword2rgb(args)); 1262} 1263 1264var cssKeywords = { 1265 aliceblue: [240,248,255], 1266 antiquewhite: [250,235,215], 1267 aqua: [0,255,255], 1268 aquamarine: [127,255,212], 1269 azure: [240,255,255], 1270 beige: [245,245,220], 1271 bisque: [255,228,196], 1272 black: [0,0,0], 1273 blanchedalmond: [255,235,205], 1274 blue: [0,0,255], 1275 blueviolet: [138,43,226], 1276 brown: [165,42,42], 1277 burlywood: [222,184,135], 1278 cadetblue: [95,158,160], 1279 chartreuse: [127,255,0], 1280 chocolate: [210,105,30], 1281 coral: [255,127,80], 1282 cornflowerblue: [100,149,237], 1283 cornsilk: [255,248,220], 1284 crimson: [220,20,60], 1285 cyan: [0,255,255], 1286 darkblue: [0,0,139], 1287 darkcyan: [0,139,139], 1288 darkgoldenrod: [184,134,11], 1289 darkgray: [169,169,169], 1290 darkgreen: [0,100,0], 1291 darkgrey: [169,169,169], 1292 darkkhaki: [189,183,107], 1293 darkmagenta: [139,0,139], 1294 darkolivegreen: [85,107,47], 1295 darkorange: [255,140,0], 1296 darkorchid: [153,50,204], 1297 darkred: [139,0,0], 1298 darksalmon: [233,150,122], 1299 darkseagreen: [143,188,143], 1300 darkslateblue: [72,61,139], 1301 darkslategray: [47,79,79], 1302 darkslategrey: [47,79,79], 1303 darkturquoise: [0,206,209], 1304 darkviolet: [148,0,211], 1305 deeppink: [255,20,147], 1306 deepskyblue: [0,191,255], 1307 dimgray: [105,105,105], 1308 dimgrey: [105,105,105], 1309 dodgerblue: [30,144,255], 1310 firebrick: [178,34,34], 1311 floralwhite: [255,250,240], 1312 forestgreen: [34,139,34], 1313 fuchsia: [255,0,255], 1314 gainsboro: [220,220,220], 1315 ghostwhite: [248,248,255], 1316 gold: [255,215,0], 1317 goldenrod: [218,165,32], 1318 gray: [128,128,128], 1319 green: [0,128,0], 1320 greenyellow: [173,255,47], 1321 grey: [128,128,128], 1322 honeydew: [240,255,240], 1323 hotpink: [255,105,180], 1324 indianred: [205,92,92], 1325 indigo: [75,0,130], 1326 ivory: [255,255,240], 1327 khaki: [240,230,140], 1328 lavender: [230,230,250], 1329 lavenderblush: [255,240,245], 1330 lawngreen: [124,252,0], 1331 lemonchiffon: [255,250,205], 1332 lightblue: [173,216,230], 1333 lightcoral: [240,128,128], 1334 lightcyan: [224,255,255], 1335 lightgoldenrodyellow: [250,250,210], 1336 lightgray: [211,211,211], 1337 lightgreen: [144,238,144], 1338 lightgrey: [211,211,211], 1339 lightpink: [255,182,193], 1340 lightsalmon: [255,160,122], 1341 lightseagreen: [32,178,170], 1342 lightskyblue: [135,206,250], 1343 lightslategray: [119,136,153], 1344 lightslategrey: [119,136,153], 1345 lightsteelblue: [176,196,222], 1346 lightyellow: [255,255,224], 1347 lime: [0,255,0], 1348 limegreen: [50,205,50], 1349 linen: [250,240,230], 1350 magenta: [255,0,255], 1351 maroon: [128,0,0], 1352 mediumaquamarine: [102,205,170], 1353 mediumblue: [0,0,205], 1354 mediumorchid: [186,85,211], 1355 mediumpurple: [147,112,219], 1356 mediumseagreen: [60,179,113], 1357 mediumslateblue: [123,104,238], 1358 mediumspringgreen: [0,250,154], 1359 mediumturquoise: [72,209,204], 1360 mediumvioletred: [199,21,133], 1361 midnightblue: [25,25,112], 1362 mintcream: [245,255,250], 1363 mistyrose: [255,228,225], 1364 moccasin: [255,228,181], 1365 navajowhite: [255,222,173], 1366 navy: [0,0,128], 1367 oldlace: [253,245,230], 1368 olive: [128,128,0], 1369 olivedrab: [107,142,35], 1370 orange: [255,165,0], 1371 orangered: [255,69,0], 1372 orchid: [218,112,214], 1373 palegoldenrod: [238,232,170], 1374 palegreen: [152,251,152], 1375 paleturquoise: [175,238,238], 1376 palevioletred: [219,112,147], 1377 papayawhip: [255,239,213], 1378 peachpuff: [255,218,185], 1379 peru: [205,133,63], 1380 pink: [255,192,203], 1381 plum: [221,160,221], 1382 powderblue: [176,224,230], 1383 purple: [128,0,128], 1384 rebeccapurple: [102, 51, 153], 1385 red: [255,0,0], 1386 rosybrown: [188,143,143], 1387 royalblue: [65,105,225], 1388 saddlebrown: [139,69,19], 1389 salmon: [250,128,114], 1390 sandybrown: [244,164,96],
1391 seagreen: [46,139,87], 1392 seashell: [255,245,238], 1393 sienna: [160,82,45], 1394 silver: [192,192,192], 1395 skyblue: [135,206,235], 1396 slateblue: [106,90,205], 1397 slategray: [112,128,144], 1398 slategrey: [112,128,144], 1399 snow: [255,250,250], 1400 springgreen: [0,255,127], 1401 steelblue: [70,130,180], 1402 tan: [210,180,140], 1403 teal: [0,128,128], 1404 thistle: [216,191,216], 1405 tomato: [255,99,71], 1406 turquoise: [64,224,208], 1407 violet: [238,130,238], 1408 wheat: [245,222,179], 1409 white: [255,255,255], 1410 whitesmoke: [245,245,245], 1411 yellow: [255,255,0], 1412 yellowgreen: [154,205,50] 1413}; 1414 1415var reverseKeywords = {}; 1416for (var key in cssKeywords) { 1417 reverseKeywords[JSON.stringify(cssKeywords[key])] = key; 1418} 1419 1420},{}],4:[function(require,module,exports){ 1421var conversions = require(3); 1422 1423var convert = function() { 1424 return new Converter(); 1425} 1426 1427for (var func in conversions) { 1428 // export Raw versions 1429 convert[func + "Raw"] = (function(func) { 1430 // accept array or plain args 1431 return function(arg) { 1432 if (typeof arg == "number") 1433 arg = Array.prototype.slice.call(arguments); 1434 return conversions[func](arg); 1435 } 1436 })(func); 1437 1438 var pair = /(\w+)2(\w+)/.exec(func), 1439 from = pair[1], 1440 to = pair[2]; 1441 1442 // export rgb2hsl and ["rgb"]["hsl"] 1443 convert[from] = convert[from] || {}; 1444 1445 convert[from][to] = convert[func] = (function(func) { 1446 return function(arg) { 1447 if (typeof arg == "number") 1448 arg = Array.prototype.slice.call(arguments); 1449 1450 var val = conversions[func](arg); 1451 if (typeof val == "string" || val === undefined) 1452 return val; // keyword 1453 1454 for (var i = 0; i < val.length; i++) 1455 val[i] = Math.round(val[i]); 1456 return val; 1457 } 1458 })(func); 1459} 1460 1461 1462/* Converter does lazy conversion and caching */ 1463var Converter = function() { 1464 this.convs = {}; 1465}; 1466 1467/* Either get the values for a space or 1468 set the values for a space, depending on args */ 1469Converter.prototype.routeSpace = function(space, args) { 1470 var values = args[0]; 1471 if (values === undefined) { 1472 // color.rgb() 1473 return this.getValues(space); 1474 } 1475 // color.rgb(10, 10, 10) 1476 if (typeof values == "number") { 1477 values = Array.prototype.slice.call(args); 1478 } 1479 1480 return this.setValues(space, values); 1481}; 1482 1483/* Set the values for a space, invalidating cache */ 1484Converter.prototype.setValues = function(space, values) { 1485 this.space = space; 1486 this.convs = {}; 1487 this.convs[space] = values; 1488 return this; 1489}; 1490 1491/* Get the values for a space. If there's already 1492 a conversion for the space, fetch it, otherwise 1493 compute it */ 1494Converter.prototype.getValues = function(space) { 1495 var vals = this.convs[space]; 1496 if (!vals) { 1497 var fspace = this.space, 1498 from = this.convs[fspace]; 1499 vals = convert[fspace][space](from); 1500 1501 this.convs[space] = vals; 1502 } 1503 return vals; 1504}; 1505 1506["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { 1507 Converter.prototype[space] = function(vals) { 1508 return this.routeSpace(space, arguments); 1509 } 1510}); 1511 1512module.exports = convert; 1513},{"3":3}],5:[function(require,module,exports){ 1514'use strict' 1515 1516module.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}; 1666 1667},{}],6:[function(require,module,exports){ 1668//! moment.js 1669 1670;(function (global, factory) { 1671 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 1672 typeof define === 'function' && define.amd ? define(factory) : 1673 global.moment = factory()
vendor: 35,273 bytes, lines 1673-2812
1673 1674}(this, (function () { 'use strict'; 1675 1676 var hookCallback; 1677 1678 function hooks () { 1679 return hookCallback.apply(null, arguments); 1680 } 1681 1682 // This is done to register the method called with moment() 1683 // without creating circular dependencies. 1684 function setHookCallback (callback) { 1685 hookCallback = callback; 1686 } 1687 1688 function isArray(input) { 1689 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; 1690 } 1691 1692 function isObject(input) { 1693 // IE8 will treat undefined and null as object if it wasn't for 1694 // input != null 1695 return input != null && Object.prototype.toString.call(input) === '[object Object]'; 1696 } 1697 1698 function isObjectEmpty(obj) { 1699 if (Object.getOwnPropertyNames) { 1700 return (Object.getOwnPropertyNames(obj).length === 0); 1701 } else { 1702 var k; 1703 for (k in obj) { 1704 if (obj.hasOwnProperty(k)) { 1705 return false; 1706 } 1707 } 1708 return true; 1709 } 1710 } 1711 1712 function isUndefined(input) { 1713 return input === void 0; 1714 } 1715 1716 function isNumber(input) { 1717 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; 1718 } 1719 1720 function isDate(input) { 1721 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; 1722 } 1723 1724 function map(arr, fn) { 1725 var res = [], i; 1726 for (i = 0; i < arr.length; ++i) { 1727 res.push(fn(arr[i], i)); 1728 } 1729 return res; 1730 } 1731 1732 function hasOwnProp(a, b) { 1733 return Object.prototype.hasOwnProperty.call(a, b); 1734 } 1735 1736 function extend(a, b) { 1737 for (var i in b) { 1738 if (hasOwnProp(b, i)) { 1739 a[i] = b[i]; 1740 } 1741 } 1742 1743 if (hasOwnProp(b, 'toString')) { 1744 a.toString = b.toString; 1745 } 1746 1747 if (hasOwnProp(b, 'valueOf')) { 1748 a.valueOf = b.valueOf; 1749 } 1750 1751 return a; 1752 } 1753 1754 function createUTC (input, format, locale, strict) { 1755 return createLocalOrUTC(input, format, locale, strict, true).utc(); 1756 } 1757 1758 function defaultParsingFlags() { 1759 // We need to deep clone this object. 1760 return { 1761 empty : false, 1762 unusedTokens : [], 1763 unusedInput : [], 1764 overflow : -2, 1765 charsLeftOver : 0, 1766 nullInput : false, 1767 invalidMonth : null, 1768 invalidFormat : false, 1769 userInvalidated : false, 1770 iso : false, 1771 parsedDateParts : [], 1772 meridiem : null, 1773 rfc2822 : false, 1774 weekdayMismatch : false 1775 }; 1776 } 1777 1778 function getParsingFlags(m) { 1779 if (m._pf == null) { 1780 m._pf = defaultParsingFlags(); 1781 } 1782 return m._pf; 1783 } 1784 1785 var some; 1786 if (Array.prototype.some) { 1787 some = Array.prototype.some; 1788 } else { 1789 some = function (fun) { 1790 var t = Object(this); 1791 var len = t.length >>> 0; 1792 1793 for (var i = 0; i < len; i++) { 1794 if (i in t && fun.call(this, t[i], i, t)) { 1795 return true; 1796 } 1797 } 1798 1799 return false; 1800 }; 1801 } 1802 1803 function isValid(m) { 1804 if (m._isValid == null) { 1805 var flags = getParsingFlags(m); 1806 var parsedParts = some.call(flags.parsedDateParts, function (i) { 1807 return i != null; 1808 }); 1809 var isNowValid = !isNaN(m._d.getTime()) && 1810 flags.overflow < 0 && 1811 !flags.empty && 1812 !flags.invalidMonth && 1813 !flags.invalidWeekday && 1814 !flags.weekdayMismatch && 1815 !flags.nullInput && 1816 !flags.invalidFormat && 1817 !flags.userInvalidated && 1818 (!flags.meridiem || (flags.meridiem && parsedParts)); 1819 1820 if (m._strict) { 1821 isNowValid = isNowValid && 1822 flags.charsLeftOver === 0 && 1823 flags.unusedTokens.length === 0 && 1824 flags.bigHour === undefined; 1825 } 1826 1827 if (Object.isFrozen == null || !Object.isFrozen(m)) { 1828 m._isValid = isNowValid; 1829 } 1830 else { 1831 return isNowValid; 1832 } 1833 } 1834 return m._isValid; 1835 } 1836 1837 function createInvalid (flags) { 1838 var m = createUTC(NaN); 1839 if (flags != null) { 1840 extend(getParsingFlags(m), flags); 1841 } 1842 else { 1843 getParsingFlags(m).userInvalidated = true; 1844 } 1845 1846 return m; 1847 } 1848 1849 // Plugins that add properties should also add the key here (null value), 1850 // so we can properly clone ourselves. 1851 var momentProperties = hooks.momentProperties = []; 1852 1853 function copyConfig(to, from) { 1854 var i, prop, val; 1855 1856 if (!isUndefined(from._isAMomentObject)) { 1857 to._isAMomentObject = from._isAMomentObject; 1858 } 1859 if (!isUndefined(from._i)) { 1860 to._i = from._i; 1861 } 1862 if (!isUndefined(from._f)) { 1863 to._f = from._f; 1864 } 1865 if (!isUndefined(from._l)) { 1866 to._l = from._l; 1867 } 1868 if (!isUndefined(from._strict)) { 1869 to._strict = from._strict; 1870 } 1871 if (!isUndefined(from._tzm)) { 1872 to._tzm = from._tzm; 1873 } 1874 if (!isUndefined(from._isUTC)) { 1875 to._isUTC = from._isUTC; 1876 } 1877 if (!isUndefined(from._offset)) { 1878 to._offset = from._offset; 1879 } 1880 if (!isUndefined(from._pf)) { 1881 to._pf = getParsingFlags(from); 1882 } 1883 if (!isUndefined(from._locale)) { 1884 to._locale = from._locale; 1885 } 1886 1887 if (momentProperties.length > 0) { 1888 for (i = 0; i < momentProperties.length; i++) { 1889 prop = momentProperties[i]; 1890 val = from[prop]; 1891 if (!isUndefined(val)) { 1892 to[prop] = val; 1893 } 1894 } 1895 } 1896 1897 return to; 1898 } 1899 1900 var updateInProgress = false; 1901 1902 // Moment prototype object 1903 function Moment(config) { 1904 copyConfig(this, config); 1905 this._d = new Date(config._d != null ? config._d.getTime() : NaN); 1906 if (!this.isValid()) { 1907 this._d = new Date(NaN); 1908 } 1909 // Prevent infinite loop in case updateOffset creates new moment 1910 // objects. 1911 if (updateInProgress === false) { 1912 updateInProgress = true; 1913 hooks.updateOffset(this); 1914 updateInProgress = false; 1915 } 1916 } 1917 1918 function isMoment (obj) { 1919 return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); 1920 } 1921 1922 function absFloor (number) { 1923 if (number < 0) { 1924 // -0 -> 0 1925 return Math.ceil(number) || 0; 1926 } else { 1927 return Math.floor(number); 1928 } 1929 } 1930 1931 function toInt(argumentForCoercion) { 1932 var coercedNumber = +argumentForCoercion, 1933 value = 0; 1934 1935 if (coercedNumber !== 0 && isFinite(coercedNumber)) { 1936 value = absFloor(coercedNumber); 1937 } 1938 1939 return value; 1940 } 1941 1942 // compare two arrays, return the number of differences 1943 function compareArrays(array1, array2, dontConvert) { 1944 var len = Math.min(array1.length, array2.length), 1945 lengthDiff = Math.abs(array1.length - array2.length), 1946 diffs = 0, 1947 i; 1948 for (i = 0; i < len; i++) { 1949 if ((dontConvert && array1[i] !== array2[i]) || 1950 (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { 1951 diffs++; 1952 } 1953 } 1954 return diffs + lengthDiff; 1955 } 1956 1957 function warn(msg) { 1958 if (hooks.suppressDeprecationWarnings === false && 1959 (typeof console !== 'undefined') && console.warn) { 1960 console.warn('Deprecation warning: ' + msg); 1961 } 1962 } 1963 1964 function deprecate(msg, fn) { 1965 var firstTime = true; 1966 1967 return extend(function () { 1968 if (hooks.deprecationHandler != null) { 1969 hooks.deprecationHandler(null, msg); 1970 } 1971 if (firstTime) { 1972 var args = []; 1973 var arg; 1974 for (var i = 0; i < arguments.length; i++) { 1975 arg = ''; 1976 if (typeof arguments[i] === 'object') { 1977 arg += '\n[' + i + '] '; 1978 for (var key in arguments[0]) { 1979 arg += key + ': ' + arguments[0][key] + ', '; 1980 } 1981 arg = arg.slice(0, -2); // Remove trailing comma and space 1982 } else { 1983 arg = arguments[i]; 1984 } 1985 args.push(arg); 1986 } 1987 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); 1988 firstTime = false; 1989 } 1990 return fn.apply(this, arguments); 1991 }, fn); 1992 } 1993 1994 var deprecations = {}; 1995 1996 function deprecateSimple(name, msg) { 1997 if (hooks.deprecationHandler != null) { 1998 hooks.deprecationHandler(name, msg); 1999 } 2000 if (!deprecations[name]) { 2001 warn(msg); 2002 deprecations[name] = true; 2003 } 2004 } 2005 2006 hooks.suppressDeprecationWarnings = false; 2007 hooks.deprecationHandler = null; 2008 2009 function isFunction(input) { 2010 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; 2011 } 2012 2013 function set (config) { 2014 var prop, i; 2015 for (i in config) { 2016 prop = config[i]; 2017 if (isFunction(prop)) { 2018 this[i] = prop; 2019 } else { 2020 this['_' + i] = prop; 2021 } 2022 } 2023 this._config = config; 2024 // Lenient ordinal parsing accepts just a number in addition to 2025 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. 2026 // TODO: Remove "ordinalParse" fallback in next major release. 2027 this._dayOfMonthOrdinalParseLenient = new RegExp( 2028 (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + 2029 '|' + (/\d{1,2}/).source); 2030 } 2031 2032 function mergeConfigs(parentConfig, childConfig) { 2033 var res = extend({}, parentConfig), prop; 2034 for (prop in childConfig) { 2035 if (hasOwnProp(childConfig, prop)) { 2036 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { 2037 res[prop] = {}; 2038 extend(res[prop], parentConfig[prop]); 2039 extend(res[prop], childConfig[prop]); 2040 } else if (childConfig[prop] != null) { 2041 res[prop] = childConfig[prop]; 2042 } else { 2043 delete res[prop]; 2044 } 2045 } 2046 } 2047 for (prop in parentConfig) { 2048 if (hasOwnProp(parentConfig, prop) && 2049 !hasOwnProp(childConfig, prop) && 2050 isObject(parentConfig[prop])) { 2051 // make sure changes to properties don't modify parent config 2052 res[prop] = extend({}, res[prop]); 2053 } 2054 } 2055 return res; 2056 } 2057 2058 function Locale(config) { 2059 if (config != null) { 2060 this.set(config); 2061 } 2062 } 2063 2064 var keys; 2065 2066 if (Object.keys) { 2067 keys = Object.keys; 2068 } else { 2069 keys = function (obj) { 2070 var i, res = []; 2071 for (i in obj) { 2072 if (hasOwnProp(obj, i)) { 2073 res.push(i); 2074 } 2075 } 2076 return res; 2077 }; 2078 } 2079 2080 var defaultCalendar = { 2081 sameDay : '[Today at] LT', 2082 nextDay : '[Tomorrow at] LT', 2083 nextWeek : 'dddd [at] LT', 2084 lastDay : '[Yesterday at] LT', 2085 lastWeek : '[Last] dddd [at] LT', 2086 sameElse : 'L' 2087 }; 2088 2089 function calendar (key, mom, now) { 2090 var output = this._calendar[key] || this._calendar['sameElse']; 2091 return isFunction(output) ? output.call(mom, now) : output; 2092 } 2093 2094 var defaultLongDateFormat = { 2095 LTS : 'h:mm:ss A', 2096 LT : 'h:mm A', 2097 L : 'MM/DD/YYYY', 2098 LL : 'MMMM D, YYYY', 2099 LLL : 'MMMM D, YYYY h:mm A', 2100 LLLL : 'dddd, MMMM D, YYYY h:mm A' 2101 }; 2102 2103 function longDateFormat (key) { 2104 var format = this._longDateFormat[key], 2105 formatUpper = this._longDateFormat[key.toUpperCase()]; 2106 2107 if (format || !formatUpper) { 2108 return format; 2109 } 2110 2111 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { 2112 return val.slice(1); 2113 }); 2114 2115 return this._longDateFormat[key]; 2116 } 2117 2118 var defaultInvalidDate = 'Invalid date'; 2119 2120 function invalidDate () { 2121 return this._invalidDate; 2122 } 2123 2124 var defaultOrdinal = '%d'; 2125 var defaultDayOfMonthOrdinalParse = /\d{1,2}/; 2126 2127 function ordinal (number) { 2128 return this._ordinal.replace('%d', number); 2129 } 2130 2131 var defaultRelativeTime = { 2132 future : 'in %s', 2133 past : '%s ago', 2134 s : 'a few seconds', 2135 ss : '%d seconds', 2136 m : 'a minute', 2137 mm : '%d minutes', 2138 h : 'an hour', 2139 hh : '%d hours', 2140 d : 'a day', 2141 dd : '%d days', 2142 M : 'a month', 2143 MM : '%d months', 2144 y : 'a year', 2145 yy : '%d years' 2146 }; 2147 2148 function relativeTime (number, withoutSuffix, string, isFuture) { 2149 var output = this._relativeTime[string]; 2150 return (isFunction(output)) ? 2151 output(number, withoutSuffix, string, isFuture) : 2152 output.replace(/%d/i, number); 2153 } 2154 2155 function pastFuture (diff, output) { 2156 var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 2157 return isFunction(format) ? format(output) : format.replace(/%s/i, output); 2158 } 2159 2160 var aliases = {}; 2161 2162 function addUnitAlias (unit, shorthand) { 2163 var lowerCase = unit.toLowerCase(); 2164 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; 2165 } 2166 2167 function normalizeUnits(units) { 2168 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; 2169 } 2170 2171 function normalizeObjectUnits(inputObject) { 2172 var normalizedInput = {}, 2173 normalizedProp, 2174 prop; 2175 2176 for (prop in inputObject) { 2177 if (hasOwnProp(inputObject, prop)) { 2178 normalizedProp = normalizeUnits(prop); 2179 if (normalizedProp) { 2180 normalizedInput[normalizedProp] = inputObject[prop]; 2181 } 2182 } 2183 } 2184 2185 return normalizedInput; 2186 } 2187 2188 var priorities = {}; 2189 2190 function addUnitPriority(unit, priority) { 2191 priorities[unit] = priority; 2192 } 2193 2194 function getPrioritizedUnits(unitsObj) { 2195 var units = []; 2196 for (var u in unitsObj) { 2197 units.push({unit: u, priority: priorities[u]}); 2198 } 2199 units.sort(function (a, b) { 2200 return a.priority - b.priority; 2201 }); 2202 return units; 2203 } 2204 2205 function zeroFill(number, targetLength, forceSign) { 2206 var absNumber = '' + Math.abs(number), 2207 zerosToFill = targetLength - absNumber.length, 2208 sign = number >= 0; 2209 return (sign ? (forceSign ? '+' : '') : '-') + 2210 Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; 2211 } 2212 2213 var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; 2214 2215 var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; 2216 2217 var formatFunctions = {}; 2218 2219 var formatTokenFunctions = {}; 2220 2221 // token: 'M' 2222 // padded: ['MM', 2] 2223 // ordinal: 'Mo' 2224 // callback: function () { this.month() + 1 } 2225 function addFormatToken (token, padded, ordinal, callback) { 2226 var func = callback; 2227 if (typeof callback === 'string') { 2228 func = function () { 2229 return this[callback](); 2230 }; 2231 } 2232 if (token) { 2233 formatTokenFunctions[token] = func; 2234 } 2235 if (padded) { 2236 formatTokenFunctions[padded[0]] = function () { 2237 return zeroFill(func.apply(this, arguments), padded[1], padded[2]); 2238 }; 2239 } 2240 if (ordinal) { 2241 formatTokenFunctions[ordinal] = function () { 2242 return this.localeData().ordinal(func.apply(this, arguments), token); 2243 }; 2244 } 2245 } 2246 2247 function removeFormattingTokens(input) { 2248 if (input.match(/\[[\s\S]/)) { 2249 return input.replace(/^\[|\]$/g, ''); 2250 } 2251 return input.replace(/\\/g, ''); 2252 } 2253 2254 function makeFormatFunction(format) { 2255 var array = format.match(formattingTokens), i, length; 2256 2257 for (i = 0, length = array.length; i < length; i++) { 2258 if (formatTokenFunctions[array[i]]) { 2259 array[i] = formatTokenFunctions[array[i]]; 2260 } else { 2261 array[i] = removeFormattingTokens(array[i]); 2262 } 2263 } 2264 2265 return function (mom) { 2266 var output = '', i; 2267 for (i = 0; i < length; i++) { 2268 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; 2269 } 2270 return output; 2271 }; 2272 } 2273 2274 // format date using native date object 2275 function formatMoment(m, format) { 2276 if (!m.isValid()) { 2277 return m.localeData().invalidDate(); 2278 } 2279 2280 format = expandFormat(format, m.localeData()); 2281 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); 2282 2283 return formatFunctions[format](m); 2284 } 2285 2286 function expandFormat(format, locale) { 2287 var i = 5; 2288 2289 function replaceLongDateFormatTokens(input) { 2290 return locale.longDateFormat(input) || input; 2291 } 2292 2293 localFormattingTokens.lastIndex = 0; 2294 while (i >= 0 && localFormattingTokens.test(format)) { 2295 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 2296 localFormattingTokens.lastIndex = 0; 2297 i -= 1; 2298 } 2299 2300 return format; 2301 } 2302 2303 var match1 = /\d/; // 0 - 9 2304 var match2 = /\d\d/; // 00 - 99 2305 var match3 = /\d{3}/; // 000 - 999 2306 var match4 = /\d{4}/; // 0000 - 9999 2307 var match6 = /[+-]?\d{6}/; // -999999 - 999999 2308 var match1to2 = /\d\d?/; // 0 - 99 2309 var match3to4 = /\d\d\d\d?/; // 999 - 9999 2310 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 2311 var match1to3 = /\d{1,3}/; // 0 - 999 2312 var match1to4 = /\d{1,4}/; // 0 - 9999 2313 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 2314 2315 var matchUnsigned = /\d+/; // 0 - inf 2316 var matchSigned = /[+-]?\d+/; // -inf - inf 2317 2318 var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z 2319 var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z 2320 2321 var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 2322 2323 // any word (or two) characters or numbers including two/three word month in arabic. 2324 // includes scottish gaelic two word and hyphenated months 2325 var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; 2326 2327 var regexes = {}; 2328 2329 function addRegexToken (token, regex, strictRegex) { 2330 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { 2331 return (isStrict && strictRegex) ? strictRegex : regex; 2332 }; 2333 } 2334 2335 function getParseRegexForToken (token, config) { 2336 if (!hasOwnProp(regexes, token)) { 2337 return new RegExp(unescapeFormat(token)); 2338 } 2339 2340 return regexes[token](config._strict, config._locale); 2341 } 2342 2343 // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript 2344 function unescapeFormat(s) { 2345 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { 2346 return p1 || p2 || p3 || p4; 2347 })); 2348 } 2349 2350 function regexEscape(s) { 2351 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 2352 } 2353 2354 var tokens = {}; 2355 2356 function addParseToken (token, callback) { 2357 var i, func = callback; 2358 if (typeof token === 'string') { 2359 token = [token]; 2360 } 2361 if (isNumber(callback)) { 2362 func = function (input, array) { 2363 array[callback] = toInt(input); 2364 }; 2365 } 2366 for (i = 0; i < token.length; i++) { 2367 tokens[token[i]] = func; 2368 } 2369 } 2370 2371 function addWeekParseToken (token, callback) { 2372 addParseToken(token, function (input, array, config, token) { 2373 config._w = config._w || {}; 2374 callback(input, config._w, config, token); 2375 }); 2376 } 2377 2378 function addTimeToArrayFromToken(token, input, config) { 2379 if (input != null && hasOwnProp(tokens, token)) { 2380 tokens[token](input, config._a, config, token); 2381 } 2382 } 2383 2384 var YEAR = 0; 2385 var MONTH = 1; 2386 var DATE = 2; 2387 var HOUR = 3; 2388 var MINUTE = 4; 2389 var SECOND = 5; 2390 var MILLISECOND = 6; 2391 var WEEK = 7; 2392 var WEEKDAY = 8; 2393 2394 // FORMATTING 2395 2396 addFormatToken('Y', 0, 0, function () { 2397 var y = this.year(); 2398 return y <= 9999 ? '' + y : '+' + y; 2399 }); 2400 2401 addFormatToken(0, ['YY', 2], 0, function () { 2402 return this.year() % 100; 2403 }); 2404 2405 addFormatToken(0, ['YYYY', 4], 0, 'year'); 2406 addFormatToken(0, ['YYYYY', 5], 0, 'year'); 2407 addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); 2408 2409 // ALIASES 2410 2411 addUnitAlias('year', 'y'); 2412 2413 // PRIORITIES 2414 2415 addUnitPriority('year', 1); 2416 2417 // PARSING 2418 2419 addRegexToken('Y', matchSigned); 2420 addRegexToken('YY', match1to2, match2); 2421 addRegexToken('YYYY', match1to4, match4); 2422 addRegexToken('YYYYY', match1to6, match6); 2423 addRegexToken('YYYYYY', match1to6, match6); 2424 2425 addParseToken(['YYYYY', 'YYYYYY'], YEAR); 2426 addParseToken('YYYY', function (input, array) { 2427 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); 2428 }); 2429 addParseToken('YY', function (input, array) { 2430 array[YEAR] = hooks.parseTwoDigitYear(input); 2431 }); 2432 addParseToken('Y', function (input, array) { 2433 array[YEAR] = parseInt(input, 10); 2434 }); 2435 2436 // HELPERS 2437 2438 function daysInYear(year) { 2439 return isLeapYear(year) ? 366 : 365; 2440 } 2441 2442 function isLeapYear(year) { 2443 return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 2444 } 2445 2446 // HOOKS 2447 2448 hooks.parseTwoDigitYear = function (input) { 2449 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); 2450 }; 2451 2452 // MOMENTS 2453 2454 var getSetYear = makeGetSet('FullYear', true); 2455 2456 function getIsLeapYear () { 2457 return isLeapYear(this.year()); 2458 } 2459 2460 function makeGetSet (unit, keepTime) { 2461 return function (value) { 2462 if (value != null) { 2463 set$1(this, unit, value); 2464 hooks.updateOffset(this, keepTime); 2465 return this; 2466 } else { 2467 return get(this, unit); 2468 } 2469 }; 2470 } 2471 2472 function get (mom, unit) { 2473 return mom.isValid() ? 2474 mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; 2475 } 2476 2477 function set$1 (mom, unit, value) { 2478 if (mom.isValid() && !isNaN(value)) { 2479 if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { 2480 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); 2481 } 2482 else { 2483 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); 2484 } 2485 } 2486 } 2487 2488 // MOMENTS 2489 2490 function stringGet (units) { 2491 units = normalizeUnits(units); 2492 if (isFunction(this[units])) { 2493 return this[units](); 2494 } 2495 return this; 2496 } 2497 2498 2499 function stringSet (units, value) { 2500 if (typeof units === 'object') { 2501 units = normalizeObjectUnits(units); 2502 var prioritized = getPrioritizedUnits(units); 2503 for (var i = 0; i < prioritized.length; i++) { 2504 this[prioritized[i].unit](units[prioritized[i].unit]); 2505 } 2506 } else { 2507 units = normalizeUnits(units); 2508 if (isFunction(this[units])) { 2509 return this[units](value); 2510 } 2511 } 2512 return this; 2513 } 2514 2515 function mod(n, x) { 2516 return ((n % x) + x) % x; 2517 } 2518 2519 var indexOf; 2520 2521 if (Array.prototype.indexOf) { 2522 indexOf = Array.prototype.indexOf; 2523 } else { 2524 indexOf = function (o) { 2525 // I know 2526 var i; 2527 for (i = 0; i < this.length; ++i) { 2528 if (this[i] === o) { 2529 return i; 2530 } 2531 } 2532 return -1; 2533 }; 2534 } 2535 2536 function daysInMonth(year, month) { 2537 if (isNaN(year) || isNaN(month)) { 2538 return NaN; 2539 } 2540 var modMonth = mod(month, 12); 2541 year += (month - modMonth) / 12; 2542 return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); 2543 } 2544 2545 // FORMATTING 2546 2547 addFormatToken('M', ['MM', 2], 'Mo', function () { 2548 return this.month() + 1; 2549 }); 2550 2551 addFormatToken('MMM', 0, 0, function (format) { 2552 return this.localeData().monthsShort(this, format); 2553 }); 2554 2555 addFormatToken('MMMM', 0, 0, function (format) { 2556 return this.localeData().months(this, format); 2557 }); 2558 2559 // ALIASES 2560 2561 addUnitAlias('month', 'M'); 2562 2563 // PRIORITY 2564 2565 addUnitPriority('month', 8); 2566 2567 // PARSING 2568 2569 addRegexToken('M', match1to2); 2570 addRegexToken('MM', match1to2, match2); 2571 addRegexToken('MMM', function (isStrict, locale) { 2572 return locale.monthsShortRegex(isStrict); 2573 }); 2574 addRegexToken('MMMM', function (isStrict, locale) { 2575 return locale.monthsRegex(isStrict); 2576 }); 2577 2578 addParseToken(['M', 'MM'], function (input, array) { 2579 array[MONTH] = toInt(input) - 1; 2580 }); 2581 2582 addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { 2583 var month = config._locale.monthsParse(input, token, config._strict); 2584 // if we didn't find a month name, mark the date as invalid. 2585 if (month != null) { 2586 array[MONTH] = month; 2587 } else { 2588 getParsingFlags(config).invalidMonth = input; 2589 } 2590 }); 2591 2592 // LOCALES 2593 2594 var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; 2595 var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); 2596 function localeMonths (m, format) { 2597 if (!m) { 2598 return isArray(this._months) ? this._months : 2599 this._months['standalone']; 2600 } 2601 return isArray(this._months) ? this._months[m.month()] : 2602 this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; 2603 } 2604 2605 var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); 2606 function localeMonthsShort (m, format) { 2607 if (!m) { 2608 return isArray(this._monthsShort) ? this._monthsShort : 2609 this._monthsShort['standalone']; 2610 } 2611 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : 2612 this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; 2613 } 2614 2615 function handleStrictParse(monthName, format, strict) { 2616 var i, ii, mom, llc = monthName.toLocaleLowerCase(); 2617 if (!this._monthsParse) { 2618 // this is not used 2619 this._monthsParse = []; 2620 this._longMonthsParse = []; 2621 this._shortMonthsParse = []; 2622 for (i = 0; i < 12; ++i) { 2623 mom = createUTC([2000, i]); 2624 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); 2625 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); 2626 } 2627 } 2628 2629 if (strict) { 2630 if (format === 'MMM') { 2631 ii = indexOf.call(this._shortMonthsParse, llc); 2632 return ii !== -1 ? ii : null; 2633 } else { 2634 ii = indexOf.call(this._longMonthsParse, llc); 2635 return ii !== -1 ? ii : null; 2636 } 2637 } else { 2638 if (format === 'MMM') { 2639 ii = indexOf.call(this._shortMonthsParse, llc); 2640 if (ii !== -1) { 2641 return ii; 2642 } 2643 ii = indexOf.call(this._longMonthsParse, llc); 2644 return ii !== -1 ? ii : null; 2645 } else { 2646 ii = indexOf.call(this._longMonthsParse, llc); 2647 if (ii !== -1) { 2648 return ii; 2649 } 2650 ii = indexOf.call(this._shortMonthsParse, llc); 2651 return ii !== -1 ? ii : null; 2652 } 2653 } 2654 } 2655 2656 function localeMonthsParse (monthName, format, strict) { 2657 var i, mom, regex; 2658 2659 if (this._monthsParseExact) { 2660 return handleStrictParse.call(this, monthName, format, strict); 2661 } 2662 2663 if (!this._monthsParse) { 2664 this._monthsParse = []; 2665 this._longMonthsParse = []; 2666 this._shortMonthsParse = []; 2667 } 2668 2669 // TODO: add sorting 2670 // Sorting makes sure if one month (or abbr) is a prefix of another 2671 // see sorting in computeMonthsParse 2672 for (i = 0; i < 12; i++) { 2673 // make the regex if we don't have it already 2674 mom = createUTC([2000, i]); 2675 if (strict && !this._longMonthsParse[i]) { 2676 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); 2677 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); 2678 } 2679 if (!strict && !this._monthsParse[i]) { 2680 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 2681 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 2682 } 2683 // test the regex 2684 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { 2685 return i; 2686 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { 2687 return i; 2688 } else if (!strict && this._monthsParse[i].test(monthName)) { 2689 return i; 2690 } 2691 } 2692 } 2693 2694 // MOMENTS 2695 2696 function setMonth (mom, value) { 2697 var dayOfMonth; 2698 2699 if (!mom.isValid()) { 2700 // No op 2701 return mom; 2702 } 2703 2704 if (typeof value === 'string') { 2705 if (/^\d+$/.test(value)) { 2706 value = toInt(value); 2707 } else { 2708 value = mom.localeData().monthsParse(value); 2709 // TODO: Another silent failure? 2710 if (!isNumber(value)) { 2711 return mom; 2712 } 2713 } 2714 } 2715 2716 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); 2717 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); 2718 return mom; 2719 } 2720 2721 function getSetMonth (value) { 2722 if (value != null) { 2723 setMonth(this, value); 2724 hooks.updateOffset(this, true); 2725 return this; 2726 } else { 2727 return get(this, 'Month'); 2728 } 2729 } 2730 2731 function getDaysInMonth () { 2732 return daysInMonth(this.year(), this.month()); 2733 } 2734 2735 var defaultMonthsShortRegex = matchWord; 2736 function monthsShortRegex (isStrict) { 2737 if (this._monthsParseExact) { 2738 if (!hasOwnProp(this, '_monthsRegex')) { 2739 computeMonthsParse.call(this); 2740 } 2741 if (isStrict) { 2742 return this._monthsShortStrictRegex; 2743 } else { 2744 return this._monthsShortRegex; 2745 } 2746 } else { 2747 if (!hasOwnProp(this, '_monthsShortRegex')) { 2748 this._monthsShortRegex = defaultMonthsShortRegex; 2749 } 2750 return this._monthsShortStrictRegex && isStrict ? 2751 this._monthsShortStrictRegex : this._monthsShortRegex; 2752 } 2753 } 2754 2755 var defaultMonthsRegex = matchWord; 2756 function monthsRegex (isStrict) { 2757 if (this._monthsParseExact) { 2758 if (!hasOwnProp(this, '_monthsRegex')) { 2759 computeMonthsParse.call(this); 2760 } 2761 if (isStrict) { 2762 return this._monthsStrictRegex; 2763 } else { 2764 return this._monthsRegex; 2765 } 2766 } else { 2767 if (!hasOwnProp(this, '_monthsRegex')) { 2768 this._monthsRegex = defaultMonthsRegex; 2769 } 2770 return this._monthsStrictRegex && isStrict ? 2771 this._monthsStrictRegex : this._monthsRegex; 2772 } 2773 } 2774 2775 function computeMonthsParse () { 2776 function cmpLenRev(a, b) { 2777 return b.length - a.length; 2778 } 2779 2780 var shortPieces = [], longPieces = [], mixedPieces = [], 2781 i, mom; 2782 for (i = 0; i < 12; i++) { 2783 // make the regex if we don't have it already 2784 mom = createUTC([2000, i]); 2785 shortPieces.push(this.monthsShort(mom, '')); 2786 longPieces.push(this.months(mom, '')); 2787 mixedPieces.push(this.months(mom, '')); 2788 mixedPieces.push(this.monthsShort(mom, '')); 2789 } 2790 // Sorting makes sure if one month (or abbr) is a prefix of another it 2791 // will match the longer piece. 2792 shortPieces.sort(cmpLenRev); 2793 longPieces.sort(cmpLenRev); 2794 mixedPieces.sort(cmpLenRev); 2795 for (i = 0; i < 12; i++) { 2796 shortPieces[i] = regexEscape(shortPieces[i]); 2797 longPieces[i] = regexEscape(longPieces[i]); 2798 } 2799 for (i = 0; i < 24; i++) { 2800 mixedPieces[i] = regexEscape(mixedPieces[i]); 2801 } 2802 2803 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 2804 this._monthsShortRegex = this._monthsRegex; 2805 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 2806 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 2807 } 2808 2809 function createDate (y, m, d, h, M, s, ms) { 2810 // can't just apply() to create a date: 2811 // https://stackoverflow.com/q/181348 2812 var date
vendor: 3,190 bytes, lines 2812-2914
2812 = new Date(y, m, d, h, M, s, ms); 2813 2814 // the date constructor remaps years 0-99 to 1900-1999 2815 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { 2816 date.setFullYear(y); 2817 } 2818 return date; 2819 } 2820 2821 function createUTCDate (y) { 2822 var date = new Date(Date.UTC.apply(null, arguments)); 2823 2824 // the Date.UTC function remaps years 0-99 to 1900-1999 2825 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { 2826 date.setUTCFullYear(y); 2827 } 2828 return date; 2829 } 2830 2831 // start-of-first-week - start-of-year 2832 function firstWeekOffset(year, dow, doy) { 2833 var // first-week day -- which january is always in the first week (4 for iso, 1 for other) 2834 fwd = 7 + dow - doy, 2835 // first-week day local weekday -- which local weekday is fwd 2836 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; 2837 2838 return -fwdlw + fwd - 1; 2839 } 2840 2841 // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday 2842 function dayOfYearFromWeeks(year, week, weekday, dow, doy) { 2843 var localWeekday = (7 + weekday - dow) % 7, 2844 weekOffset = firstWeekOffset(year, dow, doy), 2845 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, 2846 resYear, resDayOfYear; 2847 2848 if (dayOfYear <= 0) { 2849 resYear = year - 1; 2850 resDayOfYear = daysInYear(resYear) + dayOfYear; 2851 } else if (dayOfYear > daysInYear(year)) { 2852 resYear = year + 1; 2853 resDayOfYear = dayOfYear - daysInYear(year); 2854 } else { 2855 resYear = year; 2856 resDayOfYear = dayOfYear; 2857 } 2858 2859 return { 2860 year: resYear, 2861 dayOfYear: resDayOfYear 2862 }; 2863 } 2864 2865 function weekOfYear(mom, dow, doy) { 2866 var weekOffset = firstWeekOffset(mom.year(), dow, doy), 2867 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, 2868 resWeek, resYear; 2869 2870 if (week < 1) { 2871 resYear = mom.year() - 1; 2872 resWeek = week + weeksInYear(resYear, dow, doy); 2873 } else if (week > weeksInYear(mom.year(), dow, doy)) { 2874 resWeek = week - weeksInYear(mom.year(), dow, doy); 2875 resYear = mom.year() + 1; 2876 } else { 2877 resYear = mom.year(); 2878 resWeek = week; 2879 } 2880 2881 return { 2882 week: resWeek, 2883 year: resYear 2884 }; 2885 } 2886 2887 function weeksInYear(year, dow, doy) { 2888 var weekOffset = firstWeekOffset(year, dow, doy), 2889 weekOffsetNext = firstWeekOffset(year + 1, dow, doy); 2890 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; 2891 } 2892 2893 // FORMATTING 2894 2895 addFormatToken('w', ['ww', 2], 'wo', 'week'); 2896 addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); 2897 2898 // ALIASES 2899 2900 addUnitAlias('week', 'w'); 2901 addUnitAlias('isoWeek', 'W'); 2902 2903 // PRIORITIES 2904 2905 addUnitPriority('week', 5); 2906 addUnitPriority('isoWeek', 5); 2907 2908 // PARSING 2909 2910 addRegexToken('w', match1to2); 2911 addRegexToken('ww', match1to2, match2); 2912 addRegexToken('W', match1to2); 2913 addRegexToken('WW', match1to2, match2); 2914
vendor: 16,384 bytes, lines 2915-3397
2915 addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { 2916 week[token.substr(0, 1)] = toInt(input); 2917 }); 2918 2919 // HELPERS 2920 2921 // LOCALES 2922 2923 function localeWeek (mom) { 2924 return weekOfYear(mom, this._week.dow, this._week.doy).week; 2925 } 2926 2927 var defaultLocaleWeek = { 2928 dow : 0, // Sunday is the first day of the week. 2929 doy : 6 // The week that contains Jan 1st is the first week of the year. 2930 }; 2931 2932 function localeFirstDayOfWeek () { 2933 return this._week.dow; 2934 } 2935 2936 function localeFirstDayOfYear () { 2937 return this._week.doy; 2938 } 2939 2940 // MOMENTS 2941 2942 function getSetWeek (input) { 2943 var week = this.localeData().week(this); 2944 return input == null ? week : this.add((input - week) * 7, 'd'); 2945 } 2946 2947 function getSetISOWeek (input) { 2948 var week = weekOfYear(this, 1, 4).week; 2949 return input == null ? week : this.add((input - week) * 7, 'd'); 2950 } 2951 2952 // FORMATTING 2953 2954 addFormatToken('d', 0, 'do', 'day'); 2955 2956 addFormatToken('dd', 0, 0, function (format) { 2957 return this.localeData().weekdaysMin(this, format); 2958 }); 2959 2960 addFormatToken('ddd', 0, 0, function (format) { 2961 return this.localeData().weekdaysShort(this, format); 2962 }); 2963 2964 addFormatToken('dddd', 0, 0, function (format) { 2965 return this.localeData().weekdays(this, format); 2966 }); 2967 2968 addFormatToken('e', 0, 0, 'weekday'); 2969 addFormatToken('E', 0, 0, 'isoWeekday'); 2970 2971 // ALIASES 2972 2973 addUnitAlias('day', 'd'); 2974 addUnitAlias('weekday', 'e'); 2975 addUnitAlias('isoWeekday', 'E'); 2976 2977 // PRIORITY 2978 addUnitPriority('day', 11); 2979 addUnitPriority('weekday', 11); 2980 addUnitPriority('isoWeekday', 11); 2981 2982 // PARSING 2983 2984 addRegexToken('d', match1to2); 2985 addRegexToken('e', match1to2); 2986 addRegexToken('E', match1to2); 2987 addRegexToken('dd', function (isStrict, locale) { 2988 return locale.weekdaysMinRegex(isStrict); 2989 }); 2990 addRegexToken('ddd', function (isStrict, locale) { 2991 return locale.weekdaysShortRegex(isStrict); 2992 }); 2993 addRegexToken('dddd', function (isStrict, locale) { 2994 return locale.weekdaysRegex(isStrict); 2995 }); 2996 2997 addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { 2998 var weekday = config._locale.weekdaysParse(input, token, config._strict); 2999 // if we didn't get a weekday name, mark the date as invalid 3000 if (weekday != null) { 3001 week.d = weekday; 3002 } else { 3003 getParsingFlags(config).invalidWeekday = input; 3004 } 3005 }); 3006 3007 addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { 3008 week[token] = toInt(input); 3009 }); 3010 3011 // HELPERS 3012 3013 function parseWeekday(input, locale) { 3014 if (typeof input !== 'string') { 3015 return input; 3016 } 3017 3018 if (!isNaN(input)) { 3019 return parseInt(input, 10); 3020 } 3021 3022 input = locale.weekdaysParse(input); 3023 if (typeof input === 'number') { 3024 return input; 3025 } 3026 3027 return null; 3028 } 3029 3030 function parseIsoWeekday(input, locale) { 3031 if (typeof input === 'string') { 3032 return locale.weekdaysParse(input) % 7 || 7; 3033 } 3034 return isNaN(input) ? null : input; 3035 } 3036 3037 // LOCALES 3038 3039 var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); 3040 function localeWeekdays (m, format) { 3041 if (!m) { 3042 return isArray(this._weekdays) ? this._weekdays : 3043 this._weekdays['standalone']; 3044 } 3045 return isArray(this._weekdays) ? this._weekdays[m.day()] : 3046 this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; 3047 } 3048 3049 var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); 3050 function localeWeekdaysShort (m) { 3051 return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; 3052 } 3053 3054 var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); 3055 function localeWeekdaysMin (m) { 3056 return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; 3057 } 3058 3059 function handleStrictParse$1(weekdayName, format, strict) { 3060 var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); 3061 if (!this._weekdaysParse) { 3062 this._weekdaysParse = []; 3063 this._shortWeekdaysParse = []; 3064 this._minWeekdaysParse = []; 3065 3066 for (i = 0; i < 7; ++i) { 3067 mom = createUTC([2000, 1]).day(i); 3068 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); 3069 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); 3070 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); 3071 } 3072 } 3073 3074 if (strict) { 3075 if (format === 'dddd') { 3076 ii = indexOf.call(this._weekdaysParse, llc); 3077 return ii !== -1 ? ii : null; 3078 } else if (format === 'ddd') { 3079 ii = indexOf.call(this._shortWeekdaysParse, llc); 3080 return ii !== -1 ? ii : null; 3081 } else { 3082 ii = indexOf.call(this._minWeekdaysParse, llc); 3083 return ii !== -1 ? ii : null; 3084 } 3085 } else { 3086 if (format === 'dddd') { 3087 ii = indexOf.call(this._weekdaysParse, llc); 3088 if (ii !== -1) { 3089 return ii; 3090 } 3091 ii = indexOf.call(this._shortWeekdaysParse, llc); 3092 if (ii !== -1) { 3093 return ii; 3094 } 3095 ii = indexOf.call(this._minWeekdaysParse, llc); 3096 return ii !== -1 ? ii : null; 3097 } else if (format === 'ddd') { 3098 ii = indexOf.call(this._shortWeekdaysParse, llc); 3099 if (ii !== -1) { 3100 return ii; 3101 } 3102 ii = indexOf.call(this._weekdaysParse, llc); 3103 if (ii !== -1) { 3104 return ii; 3105 } 3106 ii = indexOf.call(this._minWeekdaysParse, llc); 3107 return ii !== -1 ? ii : null; 3108 } else { 3109 ii = indexOf.call(this._minWeekdaysParse, llc); 3110 if (ii !== -1) { 3111 return ii; 3112 } 3113 ii = indexOf.call(this._weekdaysParse, llc); 3114 if (ii !== -1) { 3115 return ii; 3116 } 3117 ii = indexOf.call(this._shortWeekdaysParse, llc); 3118 return ii !== -1 ? ii : null; 3119 } 3120 } 3121 } 3122 3123 function localeWeekdaysParse (weekdayName, format, strict) { 3124 var i, mom, regex; 3125 3126 if (this._weekdaysParseExact) { 3127 return handleStrictParse$1.call(this, weekdayName, format, strict); 3128 } 3129 3130 if (!this._weekdaysParse) { 3131 this._weekdaysParse = []; 3132 this._minWeekdaysParse = []; 3133 this._shortWeekdaysParse = []; 3134 this._fullWeekdaysParse = []; 3135 } 3136 3137 for (i = 0; i < 7; i++) { 3138 // make the regex if we don't have it already 3139 3140 mom = createUTC([2000, 1]).day(i); 3141 if (strict && !this._fullWeekdaysParse[i]) { 3142 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\\.?') + '$', 'i'); 3143 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\\.?') + '$', 'i'); 3144 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\\.?') + '$', 'i'); 3145 } 3146 if (!this._weekdaysParse[i]) { 3147 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); 3148 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); 3149 } 3150 // test the regex 3151 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { 3152 return i; 3153 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { 3154 return i; 3155 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { 3156 return i; 3157 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { 3158 return i; 3159 } 3160 } 3161 } 3162 3163 // MOMENTS 3164 3165 function getSetDayOfWeek (input) { 3166 if (!this.isValid()) { 3167 return input != null ? this : NaN; 3168 } 3169 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 3170 if (input != null) { 3171 input = parseWeekday(input, this.localeData()); 3172 return this.add(input - day, 'd'); 3173 } else { 3174 return day; 3175 } 3176 } 3177 3178 function getSetLocaleDayOfWeek (input) { 3179 if (!this.isValid()) { 3180 return input != null ? this : NaN; 3181 } 3182 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; 3183 return input == null ? weekday : this.add(input - weekday, 'd'); 3184 } 3185 3186 function getSetISODayOfWeek (input) { 3187 if (!this.isValid()) { 3188 return input != null ? this : NaN; 3189 } 3190 3191 // behaves the same as moment#day except 3192 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) 3193 // as a setter, sunday should belong to the previous week. 3194 3195 if (input != null) { 3196 var weekday = parseIsoWeekday(input, this.localeData()); 3197 return this.day(this.day() % 7 ? weekday : weekday - 7); 3198 } else { 3199 return this.day() || 7; 3200 } 3201 } 3202 3203 var defaultWeekdaysRegex = matchWord; 3204 function weekdaysRegex (isStrict) { 3205 if (this._weekdaysParseExact) { 3206 if (!hasOwnProp(this, '_weekdaysRegex')) { 3207 computeWeekdaysParse.call(this); 3208 } 3209 if (isStrict) { 3210 return this._weekdaysStrictRegex; 3211 } else { 3212 return this._weekdaysRegex; 3213 } 3214 } else { 3215 if (!hasOwnProp(this, '_weekdaysRegex')) { 3216 this._weekdaysRegex = defaultWeekdaysRegex; 3217 } 3218 return this._weekdaysStrictRegex && isStrict ? 3219 this._weekdaysStrictRegex : this._weekdaysRegex; 3220 } 3221 } 3222 3223 var defaultWeekdaysShortRegex = matchWord; 3224 function weekdaysShortRegex (isStrict) { 3225 if (this._weekdaysParseExact) { 3226 if (!hasOwnProp(this, '_weekdaysRegex')) { 3227 computeWeekdaysParse.call(this); 3228 } 3229 if (isStrict) { 3230 return this._weekdaysShortStrictRegex; 3231 } else { 3232 return this._weekdaysShortRegex; 3233 } 3234 } else { 3235 if (!hasOwnProp(this, '_weekdaysShortRegex')) { 3236 this._weekdaysShortRegex = defaultWeekdaysShortRegex; 3237 } 3238 return this._weekdaysShortStrictRegex && isStrict ? 3239 this._weekdaysShortStrictRegex : this._weekdaysShortRegex; 3240 } 3241 } 3242 3243 var defaultWeekdaysMinRegex = matchWord; 3244 function weekdaysMinRegex (isStrict) { 3245 if (this._weekdaysParseExact) { 3246 if (!hasOwnProp(this, '_weekdaysRegex')) { 3247 computeWeekdaysParse.call(this); 3248 } 3249 if (isStrict) { 3250 return this._weekdaysMinStrictRegex; 3251 } else { 3252 return this._weekdaysMinRegex; 3253 } 3254 } else { 3255 if (!hasOwnProp(this, '_weekdaysMinRegex')) { 3256 this._weekdaysMinRegex = defaultWeekdaysMinRegex; 3257 } 3258 return this._weekdaysMinStrictRegex && isStrict ? 3259 this._weekdaysMinStrictRegex : this._weekdaysMinRegex; 3260 } 3261 } 3262 3263 3264 function computeWeekdaysParse () { 3265 function cmpLenRev(a, b) { 3266 return b.length - a.length; 3267 } 3268 3269 var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], 3270 i, mom, minp, shortp, longp; 3271 for (i = 0; i < 7; i++) { 3272 // make the regex if we don't have it already 3273 mom = createUTC([2000, 1]).day(i); 3274 minp = this.weekdaysMin(mom, ''); 3275 shortp = this.weekdaysShort(mom, ''); 3276 longp = this.weekdays(mom, ''); 3277 minPieces.push(minp); 3278 shortPieces.push(shortp); 3279 longPieces.push(longp); 3280 mixedPieces.push(minp); 3281 mixedPieces.push(shortp); 3282 mixedPieces.push(longp); 3283 } 3284 // Sorting makes sure if one weekday (or abbr) is a prefix of another it 3285 // will match the longer piece. 3286 minPieces.sort(cmpLenRev); 3287 shortPieces.sort(cmpLenRev); 3288 longPieces.sort(cmpLenRev); 3289 mixedPieces.sort(cmpLenRev); 3290 for (i = 0; i < 7; i++) { 3291 shortPieces[i] = regexEscape(shortPieces[i]); 3292 longPieces[i] = regexEscape(longPieces[i]); 3293 mixedPieces[i] = regexEscape(mixedPieces[i]); 3294 } 3295 3296 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 3297 this._weekdaysShortRegex = this._weekdaysRegex; 3298 this._weekdaysMinRegex = this._weekdaysRegex; 3299 3300 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 3301 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 3302 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); 3303 } 3304 3305 // FORMATTING 3306 3307 function hFormat() { 3308 return this.hours() % 12 || 12; 3309 } 3310 3311 function kFormat() { 3312 return this.hours() || 24; 3313 } 3314 3315 addFormatToken('H', ['HH', 2], 0, 'hour'); 3316 addFormatToken('h', ['hh', 2], 0, hFormat); 3317 addFormatToken('k', ['kk', 2], 0, kFormat); 3318 3319 addFormatToken('hmm', 0, 0, function () { 3320 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); 3321 }); 3322 3323 addFormatToken('hmmss', 0, 0, function () { 3324 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + 3325 zeroFill(this.seconds(), 2); 3326 }); 3327 3328 addFormatToken('Hmm', 0, 0, function () { 3329 return '' + this.hours() + zeroFill(this.minutes(), 2); 3330 }); 3331 3332 addFormatToken('Hmmss', 0, 0, function () { 3333 return '' + this.hours() + zeroFill(this.minutes(), 2) + 3334 zeroFill(this.seconds(), 2); 3335 }); 3336 3337 function meridiem (token, lowercase) { 3338 addFormatToken(token, 0, 0, function () { 3339 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); 3340 }); 3341 } 3342 3343 meridiem('a', true); 3344 meridiem('A', false); 3345 3346 // ALIASES 3347 3348 addUnitAlias('hour', 'h'); 3349 3350 // PRIORITY 3351 addUnitPriority('hour', 13); 3352 3353 // PARSING 3354 3355 function matchMeridiem (isStrict, locale) { 3356 return locale._meridiemParse; 3357 } 3358 3359 addRegexToken('a', matchMeridiem); 3360 addRegexToken('A', matchMeridiem); 3361 addRegexToken('H', match1to2); 3362 addRegexToken('h', match1to2); 3363 addRegexToken('k', match1to2); 3364 addRegexToken('HH', match1to2, match2); 3365 addRegexToken('hh', match1to2, match2); 3366 addRegexToken('kk', match1to2, match2); 3367 3368 addRegexToken('hmm', match3to4); 3369 addRegexToken('hmmss', match5to6); 3370 addRegexToken('Hmm', match3to4); 3371 addRegexToken('Hmmss', match5to6); 3372 3373 addParseToken(['H', 'HH'], HOUR); 3374 addParseToken(['k', 'kk'], function (input, array, config) { 3375 var kInput = toInt(input); 3376 array[HOUR] = kInput === 24 ? 0 : kInput; 3377 }); 3378 addParseToken(['a', 'A'], function (input, array, config) { 3379 config._isPm = config._locale.isPM(input); 3380 config._meridiem = input; 3381 }); 3382 addParseToken(['h', 'hh'], function (input, array, config) { 3383 array[HOUR] = toInt(input); 3384 getParsingFlags(config).bigHour = true; 3385 }); 3386 addParseToken('hmm', function (input, array, config) { 3387 var pos = input.length - 2; 3388 array[HOUR] = toInt(input.substr(0, pos)); 3389 array[MINUTE] = toInt(input.substr(pos)); 3390 getParsingFlags(config).bigHour = true; 3391 }); 3392 addParseToken('hmmss', function (input, array, config) { 3393 var pos1 = input.length - 4; 3394 var pos2 = input.length - 2; 3395 array[HOUR] = toInt(input.substr(0, pos1)); 3396 array[MINUTE] = toInt(input.substr(pos1, 2)); 3397 array[SECOND] = toInt(input
vendor: 14,721 bytes, lines 3397-3811
3397.substr(pos2)); 3398 getParsingFlags(config).bigHour = true; 3399 }); 3400 addParseToken('Hmm', function (input, array, config) { 3401 var pos = input.length - 2; 3402 array[HOUR] = toInt(input.substr(0, pos)); 3403 array[MINUTE] = toInt(input.substr(pos)); 3404 }); 3405 addParseToken('Hmmss', function (input, array, config) { 3406 var pos1 = input.length - 4; 3407 var pos2 = input.length - 2; 3408 array[HOUR] = toInt(input.substr(0, pos1)); 3409 array[MINUTE] = toInt(input.substr(pos1, 2)); 3410 array[SECOND] = toInt(input.substr(pos2)); 3411 }); 3412 3413 // LOCALES 3414 3415 function localeIsPM (input) { 3416 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays 3417 // Using charAt should be more compatible. 3418 return ((input + '').toLowerCase().charAt(0) === 'p'); 3419 } 3420 3421 var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; 3422 function localeMeridiem (hours, minutes, isLower) { 3423 if (hours > 11) { 3424 return isLower ? 'pm' : 'PM'; 3425 } else { 3426 return isLower ? 'am' : 'AM'; 3427 } 3428 } 3429 3430 3431 // MOMENTS 3432 3433 // Setting the hour should keep the time, because the user explicitly 3434 // specified which hour they want. So trying to maintain the same hour (in 3435 // a new timezone) makes sense. Adding/subtracting hours does not follow 3436 // this rule. 3437 var getSetHour = makeGetSet('Hours', true); 3438 3439 var baseConfig = { 3440 calendar: defaultCalendar, 3441 longDateFormat: defaultLongDateFormat, 3442 invalidDate: defaultInvalidDate, 3443 ordinal: defaultOrdinal, 3444 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, 3445 relativeTime: defaultRelativeTime, 3446 3447 months: defaultLocaleMonths, 3448 monthsShort: defaultLocaleMonthsShort, 3449 3450 week: defaultLocaleWeek, 3451 3452 weekdays: defaultLocaleWeekdays, 3453 weekdaysMin: defaultLocaleWeekdaysMin, 3454 weekdaysShort: defaultLocaleWeekdaysShort, 3455 3456 meridiemParse: defaultLocaleMeridiemParse 3457 }; 3458 3459 // internal storage for locale config files 3460 var locales = {}; 3461 var localeFamilies = {}; 3462 var globalLocale; 3463 3464 function normalizeLocale(key) { 3465 return key ? key.toLowerCase().replace('_', '-') : key; 3466 } 3467 3468 // pick the locale from the array 3469 // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each 3470 // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root 3471 function chooseLocale(names) { 3472 var i = 0, j, next, locale, split; 3473 3474 while (i < names.length) { 3475 split = normalizeLocale(names[i]).split('-'); 3476 j = split.length; 3477 next = normalizeLocale(names[i + 1]); 3478 next = next ? next.split('-') : null; 3479 while (j > 0) { 3480 locale = loadLocale(split.slice(0, j).join('-')); 3481 if (locale) { 3482 return locale; 3483 } 3484 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { 3485 //the next array item is better than a shallower substring of this one 3486 break; 3487 } 3488 j--; 3489 } 3490 i++; 3491 } 3492 return globalLocale; 3493 } 3494 3495 function loadLocale(name) { 3496 var oldLocale = null; 3497 // TODO: Find a better way to register and load all the locales in Node 3498 if (!locales[name] && (typeof module !== 'undefined') && 3499 module && module.exports) { 3500 try { 3501 oldLocale = globalLocale._abbr; 3502 var aliasedRequire = require; 3503 aliasedRequire('./locale/' + name); 3504 getSetGlobalLocale(oldLocale); 3505 } catch (e) {} 3506 } 3507 return locales[name]; 3508 } 3509 3510 // This function will load locale and then set the global locale. If 3511 // no arguments are passed in, it will simply return the current global 3512 // locale key. 3513 function getSetGlobalLocale (key, values) { 3514 var data; 3515 if (key) { 3516 if (isUndefined(values)) { 3517 data = getLocale(key); 3518 } 3519 else { 3520 data = defineLocale(key, values); 3521 } 3522 3523 if (data) { 3524 // moment.duration._locale = moment._locale = data; 3525 globalLocale = data; 3526 } 3527 else { 3528 if ((typeof console !== 'undefined') && console.warn) { 3529 //warn user if arguments are passed but the locale could not be set 3530 console.warn('Locale ' + key + ' not found. Did you forget to load it?'); 3531 } 3532 } 3533 } 3534 3535 return globalLocale._abbr; 3536 } 3537 3538 function defineLocale (name, config) { 3539 if (config !== null) { 3540 var locale, parentConfig = baseConfig; 3541 config.abbr = name; 3542 if (locales[name] != null) { 3543 deprecateSimple('defineLocaleOverride', 3544 'use moment.updateLocale(localeName, config) to change ' + 3545 'an existing locale. moment.defineLocale(localeName, ' + 3546 'config) should only be used for creating a new locale ' + 3547 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); 3548 parentConfig = locales[name]._config; 3549 } else if (config.parentLocale != null) { 3550 if (locales[config.parentLocale] != null) { 3551 parentConfig = locales[config.parentLocale]._config; 3552 } else { 3553 locale = loadLocale(config.parentLocale); 3554 if (locale != null) { 3555 parentConfig = locale._config; 3556 } else { 3557 if (!localeFamilies[config.parentLocale]) { 3558 localeFamilies[config.parentLocale] = []; 3559 } 3560 localeFamilies[config.parentLocale].push({ 3561 name: name, 3562 config: config 3563 }); 3564 return null; 3565 } 3566 } 3567 } 3568 locales[name] = new Locale(mergeConfigs(parentConfig, config)); 3569 3570 if (localeFamilies[name]) { 3571 localeFamilies[name].forEach(function (x) { 3572 defineLocale(x.name, x.config); 3573 }); 3574 } 3575 3576 // backwards compat for now: also set the locale 3577 // make sure we set the locale AFTER all child locales have been 3578 // created, so we won't end up with the child locale set. 3579 getSetGlobalLocale(name); 3580 3581 3582 return locales[name]; 3583 } else { 3584 // useful for testing 3585 delete locales[name]; 3586 return null; 3587 } 3588 } 3589 3590 function updateLocale(name, config) { 3591 if (config != null) { 3592 var locale, tmpLocale, parentConfig = baseConfig; 3593 // MERGE 3594 tmpLocale = loadLocale(name); 3595 if (tmpLocale != null) { 3596 parentConfig = tmpLocale._config; 3597 } 3598 config = mergeConfigs(parentConfig, config); 3599 locale = new Locale(config); 3600 locale.parentLocale = locales[name]; 3601 locales[name] = locale; 3602 3603 // backwards compat for now: also set the locale 3604 getSetGlobalLocale(name); 3605 } else { 3606 // pass null for config to unupdate, useful for tests 3607 if (locales[name] != null) { 3608 if (locales[name].parentLocale != null) { 3609 locales[name] = locales[name].parentLocale; 3610 } else if (locales[name] != null) { 3611 delete locales[name]; 3612 } 3613 } 3614 } 3615 return locales[name]; 3616 } 3617 3618 // returns locale data 3619 function getLocale (key) { 3620 var locale; 3621 3622 if (key && key._locale && key._locale._abbr) { 3623 key = key._locale._abbr; 3624 } 3625 3626 if (!key) { 3627 return globalLocale; 3628 } 3629 3630 if (!isArray(key)) { 3631 //short-circuit everything else 3632 locale = loadLocale(key); 3633 if (locale) { 3634 return locale; 3635 } 3636 key = [key]; 3637 } 3638 3639 return chooseLocale(key); 3640 } 3641 3642 function listLocales() { 3643 return keys(locales); 3644 } 3645 3646 function checkOverflow (m) { 3647 var overflow; 3648 var a = m._a; 3649 3650 if (a && getParsingFlags(m).overflow === -2) { 3651 overflow = 3652 a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : 3653 a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : 3654 a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : 3655 a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : 3656 a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : 3657 a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : 3658 -1; 3659 3660 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { 3661 overflow = DATE; 3662 } 3663 if (getParsingFlags(m)._overflowWeeks && overflow === -1) { 3664 overflow = WEEK; 3665 } 3666 if (getParsingFlags(m)._overflowWeekday && overflow === -1) { 3667 overflow = WEEKDAY; 3668 } 3669 3670 getParsingFlags(m).overflow = overflow; 3671 } 3672 3673 return m; 3674 } 3675 3676 // Pick the first defined of two or three arguments. 3677 function defaults(a, b, c) { 3678 if (a != null) { 3679 return a; 3680 } 3681 if (b != null) { 3682 return b; 3683 } 3684 return c; 3685 } 3686 3687 function currentDateArray(config) { 3688 // hooks is actually the exported moment object 3689 var nowValue = new Date(hooks.now()); 3690 if (config._useUTC) { 3691 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; 3692 } 3693 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; 3694 } 3695 3696 // convert an array to a date. 3697 // the array should mirror the parameters below 3698 // note: all values past the year are optional and will default to the lowest possible value. 3699 // [year, month, day , hour, minute, second, millisecond] 3700 function configFromArray (config) { 3701 var i, date, input = [], currentDate, expectedWeekday, yearToUse; 3702 3703 if (config._d) { 3704 return; 3705 } 3706 3707 currentDate = currentDateArray(config); 3708 3709 //compute day of the year from weeks and weekdays 3710 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { 3711 dayOfYearFromWeekInfo(config); 3712 } 3713 3714 //if the day of the year is set, figure out what it is 3715 if (config._dayOfYear != null) { 3716 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); 3717 3718 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { 3719 getParsingFlags(config)._overflowDayOfYear = true; 3720 } 3721 3722 date = createUTCDate(yearToUse, 0, config._dayOfYear); 3723 config._a[MONTH] = date.getUTCMonth(); 3724 config._a[DATE] = date.getUTCDate(); 3725 } 3726 3727 // Default to current date. 3728 // * if no year, month, day of month are given, default to today 3729 // * if day of month is given, default month and year 3730 // * if month is given, default only year 3731 // * if year is given, don't default anything 3732 for (i = 0; i < 3 && config._a[i] == null; ++i) { 3733 config._a[i] = input[i] = currentDate[i]; 3734 } 3735 3736 // Zero out whatever was not defaulted, including time 3737 for (; i < 7; i++) { 3738 config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 3739 } 3740 3741 // Check for 24:00:00.000 3742 if (config._a[HOUR] === 24 && 3743 config._a[MINUTE] === 0 && 3744 config._a[SECOND] === 0 && 3745 config._a[MILLISECOND] === 0) { 3746 config._nextDay = true; 3747 config._a[HOUR] = 0; 3748 } 3749 3750 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); 3751 expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); 3752 3753 // Apply timezone offset from input. The actual utcOffset can be changed 3754 // with parseZone. 3755 if (config._tzm != null) { 3756 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 3757 } 3758 3759 if (config._nextDay) { 3760 config._a[HOUR] = 24; 3761 } 3762 3763 // check for mismatching day of week 3764 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { 3765 getParsingFlags(config).weekdayMismatch = true; 3766 } 3767 } 3768 3769 function dayOfYearFromWeekInfo(config) { 3770 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; 3771 3772 w = config._w; 3773 if (w.GG != null || w.W != null || w.E != null) { 3774 dow = 1; 3775 doy = 4; 3776 3777 // TODO: We need to take the current isoWeekYear, but that depends on 3778 // how we interpret now (local, utc, fixed offset). So create 3779 // a now version of current config (take local/utc/offset flags, and 3780 // create now). 3781 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); 3782 week = defaults(w.W, 1); 3783 weekday = defaults(w.E, 1); 3784 if (weekday < 1 || weekday > 7) { 3785 weekdayOverflow = true; 3786 } 3787 } else { 3788 dow = config._locale._week.dow; 3789 doy = config._locale._week.doy; 3790 3791 var curWeek = weekOfYear(createLocal(), dow, doy); 3792 3793 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); 3794 3795 // Default to current week. 3796 week = defaults(w.w, curWeek.week); 3797 3798 if (w.d != null) { 3799 // weekday -- low day numbers are considered next week 3800 weekday = w.d; 3801 if (weekday < 0 || weekday > 6) { 3802 weekdayOverflow = true; 3803 } 3804 } else if (w.e != null) { 3805 // local weekday -- counting starts from begining of week 3806 weekday = w.e + dow; 3807 if (w.e < 0 || w.e > 6) { 3808 weekdayOverflow = true; 3809 } 3810 } else { 3811 // default to begi
vendor: 19,665 bytes, lines 3811-4405
3811ning of week 3812 weekday = dow; 3813 } 3814 } 3815 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { 3816 getParsingFlags(config)._overflowWeeks = true; 3817 } else if (weekdayOverflow != null) { 3818 getParsingFlags(config)._overflowWeekday = true; 3819 } else { 3820 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); 3821 config._a[YEAR] = temp.year; 3822 config._dayOfYear = temp.dayOfYear; 3823 } 3824 } 3825 3826 // iso 8601 regex 3827 // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) 3828 var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; 3829 var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; 3830 3831 var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; 3832 3833 var isoDates = [ 3834 ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], 3835 ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], 3836 ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], 3837 ['GGGG-[W]WW', /\d{4}-W\d\d/, false], 3838 ['YYYY-DDD', /\d{4}-\d{3}/], 3839 ['YYYY-MM', /\d{4}-\d\d/, false], 3840 ['YYYYYYMMDD', /[+-]\d{10}/], 3841 ['YYYYMMDD', /\d{8}/], 3842 // YYYYMM is NOT allowed by the standard 3843 ['GGGG[W]WWE', /\d{4}W\d{3}/], 3844 ['GGGG[W]WW', /\d{4}W\d{2}/, false], 3845 ['YYYYDDD', /\d{7}/] 3846 ]; 3847 3848 // iso time formats and regexes 3849 var isoTimes = [ 3850 ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], 3851 ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], 3852 ['HH:mm:ss', /\d\d:\d\d:\d\d/], 3853 ['HH:mm', /\d\d:\d\d/], 3854 ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], 3855 ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], 3856 ['HHmmss', /\d\d\d\d\d\d/], 3857 ['HHmm', /\d\d\d\d/], 3858 ['HH', /\d\d/] 3859 ]; 3860 3861 var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; 3862 3863 // date from iso format 3864 function configFromISO(config) { 3865 var i, l, 3866 string = config._i, 3867 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), 3868 allowTime, dateFormat, timeFormat, tzFormat; 3869 3870 if (match) { 3871 getParsingFlags(config).iso = true; 3872 3873 for (i = 0, l = isoDates.length; i < l; i++) { 3874 if (isoDates[i][1].exec(match[1])) { 3875 dateFormat = isoDates[i][0]; 3876 allowTime = isoDates[i][2] !== false; 3877 break; 3878 } 3879 } 3880 if (dateFormat == null) { 3881 config._isValid = false; 3882 return; 3883 } 3884 if (match[3]) { 3885 for (i = 0, l = isoTimes.length; i < l; i++) { 3886 if (isoTimes[i][1].exec(match[3])) { 3887 // match[2] should be 'T' or space 3888 timeFormat = (match[2] || ' ') + isoTimes[i][0]; 3889 break; 3890 } 3891 } 3892 if (timeFormat == null) { 3893 config._isValid = false; 3894 return; 3895 } 3896 } 3897 if (!allowTime && timeFormat != null) { 3898 config._isValid = false; 3899 return; 3900 } 3901 if (match[4]) { 3902 if (tzRegex.exec(match[4])) { 3903 tzFormat = 'Z'; 3904 } else { 3905 config._isValid = false; 3906 return; 3907 } 3908 } 3909 config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); 3910 configFromStringAndFormat(config); 3911 } else { 3912 config._isValid = false; 3913 } 3914 } 3915 3916 // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 3917 var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; 3918 3919 function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { 3920 var result = [ 3921 untruncateYear(yearStr), 3922 defaultLocaleMonthsShort.indexOf(monthStr), 3923 parseInt(dayStr, 10), 3924 parseInt(hourStr, 10), 3925 parseInt(minuteStr, 10) 3926 ]; 3927 3928 if (secondStr) { 3929 result.push(parseInt(secondStr, 10)); 3930 } 3931 3932 return result; 3933 } 3934 3935 function untruncateYear(yearStr) { 3936 var year = parseInt(yearStr, 10); 3937 if (year <= 49) { 3938 return 2000 + year; 3939 } else if (year <= 999) { 3940 return 1900 + year; 3941 } 3942 return year; 3943 } 3944 3945 function preprocessRFC2822(s) { 3946 // Remove comments and folding whitespace and replace multiple-spaces with a single space 3947 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 3948 } 3949 3950 function checkWeekday(weekdayStr, parsedInput, config) { 3951 if (weekdayStr) { 3952 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. 3953 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), 3954 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); 3955 if (weekdayProvided !== weekdayActual) { 3956 getParsingFlags(config).weekdayMismatch = true; 3957 config._isValid = false; 3958 return false; 3959 } 3960 } 3961 return true; 3962 } 3963 3964 var obsOffsets = { 3965 UT: 0, 3966 GMT: 0, 3967 EDT: -4 * 60, 3968 EST: -5 * 60, 3969 CDT: -5 * 60, 3970 CST: -6 * 60, 3971 MDT: -6 * 60, 3972 MST: -7 * 60, 3973 PDT: -7 * 60, 3974 PST: -8 * 60 3975 }; 3976 3977 function calculateOffset(obsOffset, militaryOffset, numOffset) { 3978 if (obsOffset) { 3979 return obsOffsets[obsOffset]; 3980 } else if (militaryOffset) { 3981 // the only allowed military tz is Z 3982 return 0; 3983 } else { 3984 var hm = parseInt(numOffset, 10); 3985 var m = hm % 100, h = (hm - m) / 100; 3986 return h * 60 + m; 3987 } 3988 } 3989 3990 // date and time from ref 2822 format 3991 function configFromRFC2822(config) { 3992 var match = rfc2822.exec(preprocessRFC2822(config._i)); 3993 if (match) { 3994 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); 3995 if (!checkWeekday(match[1], parsedArray, config)) { 3996 return; 3997 } 3998 3999 config._a = parsedArray; 4000 config._tzm = calculateOffset(match[8], match[9], match[10]); 4001 4002 config._d = createUTCDate.apply(null, config._a); 4003 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 4004 4005 getParsingFlags(config).rfc2822 = true; 4006 } else { 4007 config._isValid = false; 4008 } 4009 } 4010 4011 // date from iso format or fallback 4012 function configFromString(config) { 4013 var matched = aspNetJsonRegex.exec(config._i); 4014 4015 if (matched !== null) { 4016 config._d = new Date(+matched[1]); 4017 return; 4018 } 4019 4020 configFromISO(config); 4021 if (config._isValid === false) { 4022 delete config._isValid; 4023 } else { 4024 return; 4025 } 4026 4027 configFromRFC2822(config); 4028 if (config._isValid === false) { 4029 delete config._isValid; 4030 } else { 4031 return; 4032 } 4033 4034 // Final attempt, use Input Fallback 4035 hooks.createFromInputFallback(config); 4036 } 4037 4038 hooks.createFromInputFallback = deprecate( 4039 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 4040 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 4041 'discouraged and will be removed in an upcoming major release. Please refer to ' + 4042 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', 4043 function (config) { 4044 config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); 4045 } 4046 ); 4047 4048 // constant that refers to the ISO standard 4049 hooks.ISO_8601 = function () {}; 4050 4051 // constant that refers to the RFC 2822 form 4052 hooks.RFC_2822 = function () {}; 4053 4054 // date from string and format string 4055 function configFromStringAndFormat(config) { 4056 // TODO: Move this to another part of the creation flow to prevent circular deps 4057 if (config._f === hooks.ISO_8601) { 4058 configFromISO(config); 4059 return; 4060 } 4061 if (config._f === hooks.RFC_2822) { 4062 configFromRFC2822(config); 4063 return; 4064 } 4065 config._a = []; 4066 getParsingFlags(config).empty = true; 4067 4068 // This array is used to make a Date, either with `new Date` or `Date.UTC` 4069 var string = '' + config._i, 4070 i, parsedInput, tokens, token, skipped, 4071 stringLength = string.length, 4072 totalParsedInputLength = 0; 4073 4074 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; 4075 4076 for (i = 0; i < tokens.length; i++) { 4077 token = tokens[i]; 4078 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; 4079 // console.log('token', token, 'parsedInput', parsedInput, 4080 // 'regex', getParseRegexForToken(token, config)); 4081 if (parsedInput) { 4082 skipped = string.substr(0, string.indexOf(parsedInput)); 4083 if (skipped.length > 0) { 4084 getParsingFlags(config).unusedInput.push(skipped); 4085 } 4086 string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 4087 totalParsedInputLength += parsedInput.length; 4088 } 4089 // don't parse if it's not a known token 4090 if (formatTokenFunctions[token]) { 4091 if (parsedInput) { 4092 getParsingFlags(config).empty = false; 4093 } 4094 else { 4095 getParsingFlags(config).unusedTokens.push(token); 4096 } 4097 addTimeToArrayFromToken(token, parsedInput, config); 4098 } 4099 else if (config._strict && !parsedInput) { 4100 getParsingFlags(config).unusedTokens.push(token); 4101 } 4102 } 4103 4104 // add remaining unparsed input length to the string 4105 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; 4106 if (string.length > 0) { 4107 getParsingFlags(config).unusedInput.push(string); 4108 } 4109 4110 // clear _12h flag if hour is <= 12 4111 if (config._a[HOUR] <= 12 && 4112 getParsingFlags(config).bigHour === true && 4113 config._a[HOUR] > 0) { 4114 getParsingFlags(config).bigHour = undefined; 4115 } 4116 4117 getParsingFlags(config).parsedDateParts = config._a.slice(0); 4118 getParsingFlags(config).meridiem = config._meridiem; 4119 // handle meridiem 4120 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); 4121 4122 configFromArray(config); 4123 checkOverflow(config); 4124 } 4125 4126 4127 function meridiemFixWrap (locale, hour, meridiem) { 4128 var isPm; 4129 4130 if (meridiem == null) { 4131 // nothing to do 4132 return hour; 4133 } 4134 if (locale.meridiemHour != null) { 4135 return locale.meridiemHour(hour, meridiem); 4136 } else if (locale.isPM != null) { 4137 // Fallback 4138 isPm = locale.isPM(meridiem); 4139 if (isPm && hour < 12) { 4140 hour += 12; 4141 } 4142 if (!isPm && hour === 12) { 4143 hour = 0; 4144 } 4145 return hour; 4146 } else { 4147 // this is not supposed to happen 4148 return hour; 4149 } 4150 } 4151 4152 // date from string and array of format strings 4153 function configFromStringAndArray(config) { 4154 var tempConfig, 4155 bestMoment, 4156 4157 scoreToBeat, 4158 i, 4159 currentScore; 4160 4161 if (config._f.length === 0) { 4162 getParsingFlags(config).invalidFormat = true; 4163 config._d = new Date(NaN); 4164 return; 4165 } 4166 4167 for (i = 0; i < config._f.length; i++) { 4168 currentScore = 0; 4169 tempConfig = copyConfig({}, config); 4170 if (config._useUTC != null) { 4171 tempConfig._useUTC = config._useUTC; 4172 } 4173 tempConfig._f = config._f[i]; 4174 configFromStringAndFormat(tempConfig); 4175 4176 if (!isValid(tempConfig)) { 4177 continue; 4178 } 4179 4180 // if there is any input that was not parsed add a penalty for that format 4181 currentScore += getParsingFlags(tempConfig).charsLeftOver; 4182 4183 //or tokens 4184 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; 4185 4186 getParsingFlags(tempConfig).score = currentScore; 4187 4188 if (scoreToBeat == null || currentScore < scoreToBeat) { 4189 scoreToBeat = currentScore; 4190 bestMoment = tempConfig; 4191 } 4192 } 4193 4194 extend(config, bestMoment || tempConfig); 4195 } 4196 4197 function configFromObject(config) { 4198 if (config._d) { 4199 return; 4200 } 4201 4202 var i = normalizeObjectUnits(config._i); 4203 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { 4204 return obj && parseInt(obj, 10); 4205 }); 4206 4207 configFromArray(config); 4208 } 4209 4210 function createFromConfig (config) { 4211 var res = new Moment(checkOverflow(prepareConfig(config))); 4212 if (res._nextDay) { 4213 // Adding is smart enough around DST 4214 res.add(1, 'd'); 4215 res._nextDay = undefined; 4216 } 4217 4218 return res; 4219 } 4220 4221 function prepareConfig (config) { 4222 var input = config._i, 4223 format = config._f; 4224 4225 config._locale = config._locale || getLocale(config._l); 4226 4227 if (input === null || (format === undefined && input === '')) { 4228 return createInvalid({nullInput: true}); 4229 } 4230 4231 if (typeof input === 'string') { 4232 config._i = input = config._locale.preparse(input); 4233 } 4234 4235 if (isMoment(input)) { 4236 return new Moment(checkOverflow(input)); 4237 } else if (isDate(input)) { 4238 config._d = input; 4239 } else if (isArray(format)) { 4240 configFromStringAndArray(config); 4241 } else if (format) { 4242 configFromStringAndFormat(config); 4243 } else { 4244 configFromInput(config); 4245 } 4246 4247 if (!isValid(config)) { 4248 config._d = null; 4249 } 4250 4251 return config; 4252 } 4253 4254 function configFromInput(config) { 4255 var input = config._i; 4256 if (isUndefined(input)) { 4257 config._d = new Date(hooks.now()); 4258 } else if (isDate(input)) { 4259 config._d = new Date(input.valueOf()); 4260 } else if (typeof input === 'string') { 4261 configFromString(config); 4262 } else if (isArray(input)) { 4263 config._a = map(input.slice(0), function (obj) { 4264 return parseInt(obj, 10); 4265 }); 4266 configFromArray(config); 4267 } else if (isObject(input)) { 4268 configFromObject(config); 4269 } else if (isNumber(input)) { 4270 // from milliseconds 4271 config._d = new Date(input); 4272 } else { 4273 hooks.createFromInputFallback(config); 4274 } 4275 } 4276 4277 function createLocalOrUTC (input, format, locale, strict, isUTC) { 4278 var c = {}; 4279 4280 if (locale === true || locale === false) { 4281 strict = locale; 4282 locale = undefined; 4283 } 4284 4285 if ((isObject(input) && isObjectEmpty(input)) || 4286 (isArray(input) && input.length === 0)) { 4287 input = undefined; 4288 } 4289 // object construction must be done this way. 4290 // https://github.com/moment/moment/issues/1423 4291 c._isAMomentObject = true; 4292 c._useUTC = c._isUTC = isUTC; 4293 c._l = locale; 4294 c._i = input; 4295 c._f = format; 4296 c._strict = strict; 4297 4298 return createFromConfig(c); 4299 } 4300 4301 function createLocal (input, format, locale, strict) { 4302 return createLocalOrUTC(input, format, locale, strict, false); 4303 } 4304 4305 var prototypeMin = deprecate( 4306 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', 4307 function () { 4308 var other = createLocal.apply(null, arguments); 4309 if (this.isValid() && other.isValid()) { 4310 return other < this ? this : other; 4311 } else { 4312 return createInvalid(); 4313 } 4314 } 4315 ); 4316 4317 var prototypeMax = deprecate( 4318 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', 4319 function () { 4320 var other = createLocal.apply(null, arguments); 4321 if (this.isValid() && other.isValid()) { 4322 return other > this ? this : other; 4323 } else { 4324 return createInvalid(); 4325 } 4326 } 4327 ); 4328 4329 // Pick a moment m from moments so that m[fn](other) is true for all 4330 // other. This relies on the function fn to be transitive. 4331 // 4332 // moments should either be an array of moment objects or an array, whose 4333 // first element is an array of moment objects. 4334 function pickBy(fn, moments) { 4335 var res, i; 4336 if (moments.length === 1 && isArray(moments[0])) { 4337 moments = moments[0]; 4338 } 4339 if (!moments.length) { 4340 return createLocal(); 4341 } 4342 res = moments[0]; 4343 for (i = 1; i < moments.length; ++i) { 4344 if (!moments[i].isValid() || moments[i][fn](res)) { 4345 res = moments[i]; 4346 } 4347 } 4348 return res; 4349 } 4350 4351 // TODO: Use [].sort instead? 4352 function min () { 4353 var args = [].slice.call(arguments, 0); 4354 4355 return pickBy('isBefore', args); 4356 } 4357 4358 function max () { 4359 var args = [].slice.call(arguments, 0); 4360 4361 return pickBy('isAfter', args); 4362 } 4363 4364 var now = function () { 4365 return Date.now ? Date.now() : +(new Date()); 4366 }; 4367 4368 var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; 4369 4370 function isDurationValid(m) { 4371 for (var key in m) { 4372 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { 4373 return false; 4374 } 4375 } 4376 4377 var unitHasDecimal = false; 4378 for (var i = 0; i < ordering.length; ++i) { 4379 if (m[ordering[i]]) { 4380 if (unitHasDecimal) { 4381 return false; // only allow non-integers for smallest unit 4382 } 4383 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { 4384 unitHasDecimal = true; 4385 } 4386 } 4387 } 4388 4389 return true; 4390 } 4391 4392 function isValid$1() { 4393 return this._isValid; 4394 } 4395 4396 function createInvalid$1() { 4397 return createDuration(NaN); 4398 } 4399 4400 function Duration (duration) { 4401 var normalizedInput = normalizeObjectUnits(duration), 4402 years = normalizedInput.year || 0, 4403 quarters = normalizedInput.quarter || 0, 4404 months = normalizedInput.month || 0, 4405 weeks = normalizedInput.week ||
vendor: 4,158 bytes, lines 4405-4532
44050, 4406 days = normalizedInput.day || 0, 4407 hours = normalizedInput.hour || 0, 4408 minutes = normalizedInput.minute || 0, 4409 seconds = normalizedInput.second || 0, 4410 milliseconds = normalizedInput.millisecond || 0; 4411 4412 this._isValid = isDurationValid(normalizedInput); 4413 4414 // representation for dateAddRemove 4415 this._milliseconds = +milliseconds + 4416 seconds * 1e3 + // 1000 4417 minutes * 6e4 + // 1000 * 60 4418 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 4419 // Because of dateAddRemove treats 24 hours as different from a 4420 // day when working around DST, we need to store them separately 4421 this._days = +days + 4422 weeks * 7; 4423 // It is impossible to translate months into days without knowing 4424 // which months you are are talking about, so we have to store 4425 // it separately. 4426 this._months = +months + 4427 quarters * 3 + 4428 years * 12; 4429 4430 this._data = {}; 4431 4432 this._locale = getLocale(); 4433 4434 this._bubble(); 4435 } 4436 4437 function isDuration (obj) { 4438 return obj instanceof Duration; 4439 } 4440 4441 function absRound (number) { 4442 if (number < 0) { 4443 return Math.round(-1 * number) * -1; 4444 } else { 4445 return Math.round(number); 4446 } 4447 } 4448 4449 // FORMATTING 4450 4451 function offset (token, separator) { 4452 addFormatToken(token, 0, 0, function () { 4453 var offset = this.utcOffset(); 4454 var sign = '+'; 4455 if (offset < 0) { 4456 offset = -offset; 4457 sign = '-'; 4458 } 4459 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); 4460 }); 4461 } 4462 4463 offset('Z', ':'); 4464 offset('ZZ', ''); 4465 4466 // PARSING 4467 4468 addRegexToken('Z', matchShortOffset); 4469 addRegexToken('ZZ', matchShortOffset); 4470 addParseToken(['Z', 'ZZ'], function (input, array, config) { 4471 config._useUTC = true; 4472 config._tzm = offsetFromString(matchShortOffset, input); 4473 }); 4474 4475 // HELPERS 4476 4477 // timezone chunker 4478 // '+10:00' > ['10', '00'] 4479 // '-1530' > ['-15', '30'] 4480 var chunkOffset = /([\+\-]|\d\d)/gi; 4481 4482 function offsetFromString(matcher, string) { 4483 var matches = (string || '').match(matcher); 4484 4485 if (matches === null) { 4486 return null; 4487 } 4488 4489 var chunk = matches[matches.length - 1] || []; 4490 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; 4491 var minutes = +(parts[1] * 60) + toInt(parts[2]); 4492 4493 return minutes === 0 ? 4494 0 : 4495 parts[0] === '+' ? minutes : -minutes; 4496 } 4497 4498 // Return a moment from input, that is local/utc/zone equivalent to model. 4499 function cloneWithOffset(input, model) { 4500 var res, diff; 4501 if (model._isUTC) { 4502 res = model.clone(); 4503 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); 4504 // Use low-level api, because this fn is low-level api. 4505 res._d.setTime(res._d.valueOf() + diff); 4506 hooks.updateOffset(res, false); 4507 return res; 4508 } else { 4509 return createLocal(input).local(); 4510 } 4511 } 4512 4513 function getDateOffset (m) { 4514 // On Firefox.24 Date#getTimezoneOffset returns a floating point. 4515 // https://github.com/moment/moment/pull/1871 4516 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; 4517 } 4518 4519 // HOOKS 4520 4521 // This function will be called whenever a moment is mutated. 4522 // It is intended to keep the offset in sync with the timezone. 4523 hooks.updateOffset = function () {}; 4524 4525 // MOMENTS 4526 4527 // keepLocalTime = true means only change the timezone, without 4528 // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> 4529 // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset 4530 // +0200, so we adjust the time as needed, to be valid. 4531 // 4532 // Keeping the time actually adds/subtracts (one hour)
vendor: 20,735 bytes, lines 4533-5139
4533 // from the actual represented time. That is why we call updateOffset 4534 // a second time. In case it wants us to change the offset again 4535 // _changeInProgress == true case, then we have to adjust, because 4536 // there is no such time in the given timezone. 4537 function getSetOffset (input, keepLocalTime, keepMinutes) { 4538 var offset = this._offset || 0, 4539 localAdjust; 4540 if (!this.isValid()) { 4541 return input != null ? this : NaN; 4542 } 4543 if (input != null) { 4544 if (typeof input === 'string') { 4545 input = offsetFromString(matchShortOffset, input); 4546 if (input === null) { 4547 return this; 4548 } 4549 } else if (Math.abs(input) < 16 && !keepMinutes) { 4550 input = input * 60; 4551 } 4552 if (!this._isUTC && keepLocalTime) { 4553 localAdjust = getDateOffset(this); 4554 } 4555 this._offset = input; 4556 this._isUTC = true; 4557 if (localAdjust != null) { 4558 this.add(localAdjust, 'm'); 4559 } 4560 if (offset !== input) { 4561 if (!keepLocalTime || this._changeInProgress) { 4562 addSubtract(this, createDuration(input - offset, 'm'), 1, false); 4563 } else if (!this._changeInProgress) { 4564 this._changeInProgress = true; 4565 hooks.updateOffset(this, true); 4566 this._changeInProgress = null; 4567 } 4568 } 4569 return this; 4570 } else { 4571 return this._isUTC ? offset : getDateOffset(this); 4572 } 4573 } 4574 4575 function getSetZone (input, keepLocalTime) { 4576 if (input != null) { 4577 if (typeof input !== 'string') { 4578 input = -input; 4579 } 4580 4581 this.utcOffset(input, keepLocalTime); 4582 4583 return this; 4584 } else { 4585 return -this.utcOffset(); 4586 } 4587 } 4588 4589 function setOffsetToUTC (keepLocalTime) { 4590 return this.utcOffset(0, keepLocalTime); 4591 } 4592 4593 function setOffsetToLocal (keepLocalTime) { 4594 if (this._isUTC) { 4595 this.utcOffset(0, keepLocalTime); 4596 this._isUTC = false; 4597 4598 if (keepLocalTime) { 4599 this.subtract(getDateOffset(this), 'm'); 4600 } 4601 } 4602 return this; 4603 } 4604 4605 function setOffsetToParsedOffset () { 4606 if (this._tzm != null) { 4607 this.utcOffset(this._tzm, false, true); 4608 } else if (typeof this._i === 'string') { 4609 var tZone = offsetFromString(matchOffset, this._i); 4610 if (tZone != null) { 4611 this.utcOffset(tZone); 4612 } 4613 else { 4614 this.utcOffset(0, true); 4615 } 4616 } 4617 return this; 4618 } 4619 4620 function hasAlignedHourOffset (input) { 4621 if (!this.isValid()) { 4622 return false; 4623 } 4624 input = input ? createLocal(input).utcOffset() : 0; 4625 4626 return (this.utcOffset() - input) % 60 === 0; 4627 } 4628 4629 function isDaylightSavingTime () { 4630 return ( 4631 this.utcOffset() > this.clone().month(0).utcOffset() || 4632 this.utcOffset() > this.clone().month(5).utcOffset() 4633 ); 4634 } 4635 4636 function isDaylightSavingTimeShifted () { 4637 if (!isUndefined(this._isDSTShifted)) { 4638 return this._isDSTShifted; 4639 } 4640 4641 var c = {}; 4642 4643 copyConfig(c, this); 4644 c = prepareConfig(c); 4645 4646 if (c._a) { 4647 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); 4648 this._isDSTShifted = this.isValid() && 4649 compareArrays(c._a, other.toArray()) > 0; 4650 } else { 4651 this._isDSTShifted = false; 4652 } 4653 4654 return this._isDSTShifted; 4655 } 4656 4657 function isLocal () { 4658 return this.isValid() ? !this._isUTC : false; 4659 } 4660 4661 function isUtcOffset () { 4662 return this.isValid() ? this._isUTC : false; 4663 } 4664 4665 function isUtc () { 4666 return this.isValid() ? this._isUTC && this._offset === 0 : false; 4667 } 4668 4669 // ASP.NET json date format regex 4670 var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; 4671 4672 // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html 4673 // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere 4674 // and further modified to allow for strings containing both week and day 4675 var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; 4676 4677 function createDuration (input, key) { 4678 var duration = input, 4679 // matching against regexp is expensive, do it on demand 4680 match = null, 4681 sign, 4682 ret, 4683 diffRes; 4684 4685 if (isDuration(input)) { 4686 duration = { 4687 ms : input._milliseconds, 4688 d : input._days, 4689 M : input._months 4690 }; 4691 } else if (isNumber(input)) { 4692 duration = {}; 4693 if (key) { 4694 duration[key] = input; 4695 } else { 4696 duration.milliseconds = input; 4697 } 4698 } else if (!!(match = aspNetRegex.exec(input))) { 4699 sign = (match[1] === '-') ? -1 : 1; 4700 duration = { 4701 y : 0, 4702 d : toInt(match[DATE]) * sign, 4703 h : toInt(match[HOUR]) * sign, 4704 m : toInt(match[MINUTE]) * sign, 4705 s : toInt(match[SECOND]) * sign, 4706 ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match 4707 }; 4708 } else if (!!(match = isoRegex.exec(input))) { 4709 sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1; 4710 duration = { 4711 y : parseIso(match[2], sign), 4712 M : parseIso(match[3], sign), 4713 w : parseIso(match[4], sign), 4714 d : parseIso(match[5], sign), 4715 h : parseIso(match[6], sign), 4716 m : parseIso(match[7], sign), 4717 s : parseIso(match[8], sign) 4718 }; 4719 } else if (duration == null) {// checks for null or undefined 4720 duration = {}; 4721 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { 4722 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); 4723 4724 duration = {}; 4725 duration.ms = diffRes.milliseconds; 4726 duration.M = diffRes.months; 4727 } 4728 4729 ret = new Duration(duration); 4730 4731 if (isDuration(input) && hasOwnProp(input, '_locale')) { 4732 ret._locale = input._locale; 4733 } 4734 4735 return ret; 4736 } 4737 4738 createDuration.fn = Duration.prototype; 4739 createDuration.invalid = createInvalid$1; 4740 4741 function parseIso (inp, sign) { 4742 // We'd normally use ~~inp for this, but unfortunately it also 4743 // converts floats to ints. 4744 // inp may be undefined, so careful calling replace on it. 4745 var res = inp && parseFloat(inp.replace(',', '.')); 4746 // apply sign while we're at it 4747 return (isNaN(res) ? 0 : res) * sign; 4748 } 4749 4750 function positiveMomentsDifference(base, other) { 4751 var res = {milliseconds: 0, months: 0}; 4752 4753 res.months = other.month() - base.month() + 4754 (other.year() - base.year()) * 12; 4755 if (base.clone().add(res.months, 'M').isAfter(other)) { 4756 --res.months; 4757 } 4758 4759 res.milliseconds = +other - +(base.clone().add(res.months, 'M')); 4760 4761 return res; 4762 } 4763 4764 function momentsDifference(base, other) { 4765 var res; 4766 if (!(base.isValid() && other.isValid())) { 4767 return {milliseconds: 0, months: 0}; 4768 } 4769 4770 other = cloneWithOffset(other, base); 4771 if (base.isBefore(other)) { 4772 res = positiveMomentsDifference(base, other); 4773 } else { 4774 res = positiveMomentsDifference(other, base); 4775 res.milliseconds = -res.milliseconds; 4776 res.months = -res.months; 4777 } 4778 4779 return res; 4780 } 4781 4782 // TODO: remove 'name' arg after deprecation is removed 4783 function createAdder(direction, name) { 4784 return function (val, period) { 4785 var dur, tmp; 4786 //invert the arguments, but complain about it 4787 if (period !== null && !isNaN(+period)) { 4788 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 4789 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); 4790 tmp = val; val = period; period = tmp; 4791 } 4792 4793 val = typeof val === 'string' ? +val : val; 4794 dur = createDuration(val, period); 4795 addSubtract(this, dur, direction); 4796 return this; 4797 }; 4798 } 4799 4800 function addSubtract (mom, duration, isAdding, updateOffset) { 4801 var milliseconds = duration._milliseconds, 4802 days = absRound(duration._days), 4803 months = absRound(duration._months); 4804 4805 if (!mom.isValid()) { 4806 // No op 4807 return; 4808 } 4809 4810 updateOffset = updateOffset == null ? true : updateOffset; 4811 4812 if (months) { 4813 setMonth(mom, get(mom, 'Month') + months * isAdding); 4814 } 4815 if (days) { 4816 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); 4817 } 4818 if (milliseconds) { 4819 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); 4820 } 4821 if (updateOffset) { 4822 hooks.updateOffset(mom, days || months); 4823 } 4824 } 4825 4826 var add = createAdder(1, 'add'); 4827 var subtract = createAdder(-1, 'subtract'); 4828 4829 function getCalendarFormat(myMoment, now) { 4830 var diff = myMoment.diff(now, 'days', true); 4831 return diff < -6 ? 'sameElse' : 4832 diff < -1 ? 'lastWeek' : 4833 diff < 0 ? 'lastDay' : 4834 diff < 1 ? 'sameDay' : 4835 diff < 2 ? 'nextDay' : 4836 diff < 7 ? 'nextWeek' : 'sameElse'; 4837 } 4838 4839 function calendar$1 (time, formats) { 4840 // We want to compare the start of today, vs this. 4841 // Getting start-of-today depends on whether we're local/utc/offset or not. 4842 var now = time || createLocal(), 4843 sod = cloneWithOffset(now, this).startOf('day'), 4844 format = hooks.calendarFormat(this, sod) || 'sameElse'; 4845 4846 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); 4847 4848 return this.format(output || this.localeData().calendar(format, this, createLocal(now))); 4849 } 4850 4851 function clone () { 4852 return new Moment(this); 4853 } 4854 4855 function isAfter (input, units) { 4856 var localInput = isMoment(input) ? input : createLocal(input); 4857 if (!(this.isValid() && localInput.isValid())) { 4858 return false; 4859 } 4860 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 4861 if (units === 'millisecond') { 4862 return this.valueOf() > localInput.valueOf(); 4863 } else { 4864 return localInput.valueOf() < this.clone().startOf(units).valueOf(); 4865 } 4866 } 4867 4868 function isBefore (input, units) { 4869 var localInput = isMoment(input) ? input : createLocal(input); 4870 if (!(this.isValid() && localInput.isValid())) { 4871 return false; 4872 } 4873 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 4874 if (units === 'millisecond') { 4875 return this.valueOf() < localInput.valueOf(); 4876 } else { 4877 return this.clone().endOf(units).valueOf() < localInput.valueOf(); 4878 } 4879 } 4880 4881 function isBetween (from, to, units, inclusivity) { 4882 inclusivity = inclusivity || '()'; 4883 return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && 4884 (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); 4885 } 4886 4887 function isSame (input, units) { 4888 var localInput = isMoment(input) ? input : createLocal(input), 4889 inputMs; 4890 if (!(this.isValid() && localInput.isValid())) { 4891 return false; 4892 } 4893 units = normalizeUnits(units || 'millisecond'); 4894 if (units === 'millisecond') { 4895 return this.valueOf() === localInput.valueOf(); 4896 } else { 4897 inputMs = localInput.valueOf(); 4898 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); 4899 } 4900 } 4901 4902 function isSameOrAfter (input, units) { 4903 return this.isSame(input, units) || this.isAfter(input,units); 4904 } 4905 4906 function isSameOrBefore (input, units) { 4907 return this.isSame(input, units) || this.isBefore(input,units); 4908 } 4909 4910 function diff (input, units, asFloat) { 4911 var that, 4912 zoneDelta, 4913 output; 4914 4915 if (!this.isValid()) { 4916 return NaN; 4917 } 4918 4919 that = cloneWithOffset(input, this); 4920 4921 if (!that.isValid()) { 4922 return NaN; 4923 } 4924 4925 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; 4926 4927 units = normalizeUnits(units); 4928 4929 switch (units) { 4930 case 'year': output = monthDiff(this, that) / 12; break; 4931 case 'month': output = monthDiff(this, that); break; 4932 case 'quarter': output = monthDiff(this, that) / 3; break; 4933 case 'second': output = (this - that) / 1e3; break; // 1000 4934 case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 4935 case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 4936 case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst 4937 case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst 4938 default: output = this - that; 4939 } 4940 4941 return asFloat ? output : absFloor(output); 4942 } 4943 4944 function monthDiff (a, b) { 4945 // difference in months 4946 var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), 4947 // b is in (anchor - 1 month, anchor + 1 month) 4948 anchor = a.clone().add(wholeMonthDiff, 'months'), 4949 anchor2, adjust; 4950 4951 if (b - anchor < 0) { 4952 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); 4953 // linear across the month 4954 adjust = (b - anchor) / (anchor - anchor2); 4955 } else { 4956 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); 4957 // linear across the month 4958 adjust = (b - anchor) / (anchor2 - anchor); 4959 } 4960 4961 //check for negative zero, return zero if negative zero 4962 return -(wholeMonthDiff + adjust) || 0; 4963 } 4964 4965 hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; 4966 hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; 4967 4968 function toString () { 4969 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); 4970 } 4971 4972 function toISOString(keepOffset) { 4973 if (!this.isValid()) { 4974 return null; 4975 } 4976 var utc = keepOffset !== true; 4977 var m = utc ? this.clone().utc() : this; 4978 if (m.year() < 0 || m.year() > 9999) { 4979 return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); 4980 } 4981 if (isFunction(Date.prototype.toISOString)) { 4982 // native implementation is ~50x faster, use it when we can 4983 if (utc) { 4984 return this.toDate().toISOString(); 4985 } else { 4986 return new Date(this.valueOf() + this.utcOffset() * 60 * 1000).toISOString().replace('Z', formatMoment(m, 'Z')); 4987 } 4988 } 4989 return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); 4990 } 4991 4992 /** 4993 * Return a human readable representation of a moment that can 4994 * also be evaluated to get a new moment which is the same 4995 * 4996 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects 4997 */ 4998 function inspect () { 4999 if (!this.isValid()) { 5000 return 'moment.invalid(/* ' + this._i + ' */)'; 5001 } 5002 var func = 'moment'; 5003 var zone = ''; 5004 if (!this.isLocal()) { 5005 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; 5006 zone = 'Z'; 5007 } 5008 var prefix = '[' + func + '("]'; 5009 var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; 5010 var datetime = '-MM-DD[T]HH:mm:ss.SSS'; 5011 var suffix = zone + '[")]'; 5012 5013 return this.format(prefix + year + datetime + suffix); 5014 } 5015 5016 function format (inputString) { 5017 if (!inputString) { 5018 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; 5019 } 5020 var output = formatMoment(this, inputString); 5021 return this.localeData().postformat(output); 5022 } 5023 5024 function from (time, withoutSuffix) { 5025 if (this.isValid() && 5026 ((isMoment(time) && time.isValid()) || 5027 createLocal(time).isValid())) { 5028 return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); 5029 } else { 5030 return this.localeData().invalidDate(); 5031 } 5032 } 5033 5034 function fromNow (withoutSuffix) { 5035 return this.from(createLocal(), withoutSuffix); 5036 } 5037 5038 function to (time, withoutSuffix) { 5039 if (this.isValid() && 5040 ((isMoment(time) && time.isValid()) || 5041 createLocal(time).isValid())) { 5042 return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); 5043 } else { 5044 return this.localeData().invalidDate(); 5045 } 5046 } 5047 5048 function toNow (withoutSuffix) { 5049 return this.to(createLocal(), withoutSuffix); 5050 } 5051 5052 // If passed a locale key, it will set the locale for this 5053 // instance. Otherwise, it will return the locale configuration 5054 // variables for this instance. 5055 function locale (key) { 5056 var newLocaleData; 5057 5058 if (key === undefined) { 5059 return this._locale._abbr; 5060 } else { 5061 newLocaleData = getLocale(key); 5062 if (newLocaleData != null) { 5063 this._locale = newLocaleData; 5064 } 5065 return this; 5066 } 5067 } 5068 5069 var lang = deprecate( 5070 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', 5071 function (key) { 5072 if (key === undefined) { 5073 return this.localeData(); 5074 } else { 5075 return this.locale(key); 5076 } 5077 } 5078 ); 5079 5080 function localeData () { 5081 return this._locale; 5082 } 5083 5084 function startOf (units) { 5085 units = normalizeUnits(units); 5086 // the following switch intentionally omits break keywords 5087 // to utilize falling through the cases. 5088 switch (units) { 5089 case 'year': 5090 this.month(0); 5091 /* falls through */ 5092 case 'quarter': 5093 case 'month': 5094 this.date(1); 5095 /* falls through */ 5096 case 'week': 5097 case 'isoWeek': 5098 case 'day': 5099 case 'date': 5100 this.hours(0); 5101 /* falls through */ 5102 case 'hour': 5103 this.minutes(0); 5104 /* falls through */ 5105 case 'minute': 5106 this.seconds(0); 5107 /* falls through */ 5108 case 'second': 5109 this.milliseconds(0); 5110 } 5111 5112 // weeks are a special case 5113 if (units === 'week') { 5114 this.weekday(0); 5115 } 5116 if (units === 'isoWeek') { 5117 this.isoWeekday(1); 5118 } 5119 5120 // quarters are also special 5121 if (units === 'quarter') { 5122 this.month(Math.floor(this.month() / 3) * 3); 5123 } 5124 5125 return this; 5126 } 5127 5128 function endOf (units) { 5129 units = normalizeUnits(units); 5130 if (units === undefined || units === 'millisecond') { 5131 return this; 5132 } 5133 5134 // 'date' is an alias for 'day', so it should be considered as such. 5135 if (units === 'date') { 5136 units = 'day'; 5137 } 5138 5139 return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms')
vendor: 20,515 bytes, lines 5139-5845
5139; 5140 } 5141 5142 function valueOf () { 5143 return this._d.valueOf() - ((this._offset || 0) * 60000); 5144 } 5145 5146 function unix () { 5147 return Math.floor(this.valueOf() / 1000); 5148 } 5149 5150 function toDate () { 5151 return new Date(this.valueOf()); 5152 } 5153 5154 function toArray () { 5155 var m = this; 5156 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; 5157 } 5158 5159 function toObject () { 5160 var m = this; 5161 return { 5162 years: m.year(), 5163 months: m.month(), 5164 date: m.date(), 5165 hours: m.hours(), 5166 minutes: m.minutes(), 5167 seconds: m.seconds(), 5168 milliseconds: m.milliseconds() 5169 }; 5170 } 5171 5172 function toJSON () { 5173 // new Date(NaN).toJSON() === null 5174 return this.isValid() ? this.toISOString() : null; 5175 } 5176 5177 function isValid$2 () { 5178 return isValid(this); 5179 } 5180 5181 function parsingFlags () { 5182 return extend({}, getParsingFlags(this)); 5183 } 5184 5185 function invalidAt () { 5186 return getParsingFlags(this).overflow; 5187 } 5188 5189 function creationData() { 5190 return { 5191 input: this._i, 5192 format: this._f, 5193 locale: this._locale, 5194 isUTC: this._isUTC, 5195 strict: this._strict 5196 }; 5197 } 5198 5199 // FORMATTING 5200 5201 addFormatToken(0, ['gg', 2], 0, function () { 5202 return this.weekYear() % 100; 5203 }); 5204 5205 addFormatToken(0, ['GG', 2], 0, function () { 5206 return this.isoWeekYear() % 100; 5207 }); 5208 5209 function addWeekYearFormatToken (token, getter) { 5210 addFormatToken(0, [token, token.length], 0, getter); 5211 } 5212 5213 addWeekYearFormatToken('gggg', 'weekYear'); 5214 addWeekYearFormatToken('ggggg', 'weekYear'); 5215 addWeekYearFormatToken('GGGG', 'isoWeekYear'); 5216 addWeekYearFormatToken('GGGGG', 'isoWeekYear'); 5217 5218 // ALIASES 5219 5220 addUnitAlias('weekYear', 'gg'); 5221 addUnitAlias('isoWeekYear', 'GG'); 5222 5223 // PRIORITY 5224 5225 addUnitPriority('weekYear', 1); 5226 addUnitPriority('isoWeekYear', 1); 5227 5228 5229 // PARSING 5230 5231 addRegexToken('G', matchSigned); 5232 addRegexToken('g', matchSigned); 5233 addRegexToken('GG', match1to2, match2); 5234 addRegexToken('gg', match1to2, match2); 5235 addRegexToken('GGGG', match1to4, match4); 5236 addRegexToken('gggg', match1to4, match4); 5237 addRegexToken('GGGGG', match1to6, match6); 5238 addRegexToken('ggggg', match1to6, match6); 5239 5240 addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { 5241 week[token.substr(0, 2)] = toInt(input); 5242 }); 5243 5244 addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { 5245 week[token] = hooks.parseTwoDigitYear(input); 5246 }); 5247 5248 // MOMENTS 5249 5250 function getSetWeekYear (input) { 5251 return getSetWeekYearHelper.call(this, 5252 input, 5253 this.week(), 5254 this.weekday(), 5255 this.localeData()._week.dow, 5256 this.localeData()._week.doy); 5257 } 5258 5259 function getSetISOWeekYear (input) { 5260 return getSetWeekYearHelper.call(this, 5261 input, this.isoWeek(), this.isoWeekday(), 1, 4); 5262 } 5263 5264 function getISOWeeksInYear () { 5265 return weeksInYear(this.year(), 1, 4); 5266 } 5267 5268 function getWeeksInYear () { 5269 var weekInfo = this.localeData()._week; 5270 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); 5271 } 5272 5273 function getSetWeekYearHelper(input, week, weekday, dow, doy) { 5274 var weeksTarget; 5275 if (input == null) { 5276 return weekOfYear(this, dow, doy).year; 5277 } else { 5278 weeksTarget = weeksInYear(input, dow, doy); 5279 if (week > weeksTarget) { 5280 week = weeksTarget; 5281 } 5282 return setWeekAll.call(this, input, week, weekday, dow, doy); 5283 } 5284 } 5285 5286 function setWeekAll(weekYear, week, weekday, dow, doy) { 5287 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), 5288 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); 5289 5290 this.year(date.getUTCFullYear()); 5291 this.month(date.getUTCMonth()); 5292 this.date(date.getUTCDate()); 5293 return this; 5294 } 5295 5296 // FORMATTING 5297 5298 addFormatToken('Q', 0, 'Qo', 'quarter'); 5299 5300 // ALIASES 5301 5302 addUnitAlias('quarter', 'Q'); 5303 5304 // PRIORITY 5305 5306 addUnitPriority('quarter', 7); 5307 5308 // PARSING 5309 5310 addRegexToken('Q', match1); 5311 addParseToken('Q', function (input, array) { 5312 array[MONTH] = (toInt(input) - 1) * 3; 5313 }); 5314 5315 // MOMENTS 5316 5317 function getSetQuarter (input) { 5318 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); 5319 } 5320 5321 // FORMATTING 5322 5323 addFormatToken('D', ['DD', 2], 'Do', 'date'); 5324 5325 // ALIASES 5326 5327 addUnitAlias('date', 'D'); 5328 5329 // PRIORITY 5330 addUnitPriority('date', 9); 5331 5332 // PARSING 5333 5334 addRegexToken('D', match1to2); 5335 addRegexToken('DD', match1to2, match2); 5336 addRegexToken('Do', function (isStrict, locale) { 5337 // TODO: Remove "ordinalParse" fallback in next major release. 5338 return isStrict ? 5339 (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : 5340 locale._dayOfMonthOrdinalParseLenient; 5341 }); 5342 5343 addParseToken(['D', 'DD'], DATE); 5344 addParseToken('Do', function (input, array) { 5345 array[DATE] = toInt(input.match(match1to2)[0]); 5346 }); 5347 5348 // MOMENTS 5349 5350 var getSetDayOfMonth = makeGetSet('Date', true); 5351 5352 // FORMATTING 5353 5354 addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); 5355 5356 // ALIASES 5357 5358 addUnitAlias('dayOfYear', 'DDD'); 5359 5360 // PRIORITY 5361 addUnitPriority('dayOfYear', 4); 5362 5363 // PARSING 5364 5365 addRegexToken('DDD', match1to3); 5366 addRegexToken('DDDD', match3); 5367 addParseToken(['DDD', 'DDDD'], function (input, array, config) { 5368 config._dayOfYear = toInt(input); 5369 }); 5370 5371 // HELPERS 5372 5373 // MOMENTS 5374 5375 function getSetDayOfYear (input) { 5376 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; 5377 return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); 5378 } 5379 5380 // FORMATTING 5381 5382 addFormatToken('m', ['mm', 2], 0, 'minute'); 5383 5384 // ALIASES 5385 5386 addUnitAlias('minute', 'm'); 5387 5388 // PRIORITY 5389 5390 addUnitPriority('minute', 14); 5391 5392 // PARSING 5393 5394 addRegexToken('m', match1to2); 5395 addRegexToken('mm', match1to2, match2); 5396 addParseToken(['m', 'mm'], MINUTE); 5397 5398 // MOMENTS 5399 5400 var getSetMinute = makeGetSet('Minutes', false); 5401 5402 // FORMATTING 5403 5404 addFormatToken('s', ['ss', 2], 0, 'second'); 5405 5406 // ALIASES 5407 5408 addUnitAlias('second', 's'); 5409 5410 // PRIORITY 5411 5412 addUnitPriority('second', 15); 5413 5414 // PARSING 5415 5416 addRegexToken('s', match1to2); 5417 addRegexToken('ss', match1to2, match2); 5418 addParseToken(['s', 'ss'], SECOND); 5419 5420 // MOMENTS 5421 5422 var getSetSecond = makeGetSet('Seconds', false); 5423 5424 // FORMATTING 5425 5426 addFormatToken('S', 0, 0, function () { 5427 return ~~(this.millisecond() / 100); 5428 }); 5429 5430 addFormatToken(0, ['SS', 2], 0, function () { 5431 return ~~(this.millisecond() / 10); 5432 }); 5433 5434 addFormatToken(0, ['SSS', 3], 0, 'millisecond'); 5435 addFormatToken(0, ['SSSS', 4], 0, function () { 5436 return this.millisecond() * 10; 5437 }); 5438 addFormatToken(0, ['SSSSS', 5], 0, function () { 5439 return this.millisecond() * 100; 5440 }); 5441 addFormatToken(0, ['SSSSSS', 6], 0, function () { 5442 return this.millisecond() * 1000; 5443 }); 5444 addFormatToken(0, ['SSSSSSS', 7], 0, function () { 5445 return this.millisecond() * 10000; 5446 }); 5447 addFormatToken(0, ['SSSSSSSS', 8], 0, function () { 5448 return this.millisecond() * 100000; 5449 }); 5450 addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { 5451 return this.millisecond() * 1000000; 5452 }); 5453 5454 5455 // ALIASES 5456 5457 addUnitAlias('millisecond', 'ms'); 5458 5459 // PRIORITY 5460 5461 addUnitPriority('millisecond', 16); 5462 5463 // PARSING 5464 5465 addRegexToken('S', match1to3, match1); 5466 addRegexToken('SS', match1to3, match2); 5467 addRegexToken('SSS', match1to3, match3); 5468 5469 var token; 5470 for (token = 'SSSS'; token.length <= 9; token += 'S') { 5471 addRegexToken(token, matchUnsigned); 5472 } 5473 5474 function parseMs(input, array) { 5475 array[MILLISECOND] = toInt(('0.' + input) * 1000); 5476 } 5477 5478 for (token = 'S'; token.length <= 9; token += 'S') { 5479 addParseToken(token, parseMs); 5480 } 5481 // MOMENTS 5482 5483 var getSetMillisecond = makeGetSet('Milliseconds', false); 5484 5485 // FORMATTING 5486 5487 addFormatToken('z', 0, 0, 'zoneAbbr'); 5488 addFormatToken('zz', 0, 0, 'zoneName'); 5489 5490 // MOMENTS 5491 5492 function getZoneAbbr () { 5493 return this._isUTC ? 'UTC' : ''; 5494 } 5495 5496 function getZoneName () { 5497 return this._isUTC ? 'Coordinated Universal Time' : ''; 5498 } 5499 5500 var proto = Moment.prototype; 5501 5502 proto.add = add; 5503 proto.calendar = calendar$1; 5504 proto.clone = clone; 5505 proto.diff = diff; 5506 proto.endOf = endOf; 5507 proto.format = format; 5508 proto.from = from; 5509 proto.fromNow = fromNow; 5510 proto.to = to; 5511 proto.toNow = toNow; 5512 proto.get = stringGet; 5513 proto.invalidAt = invalidAt; 5514 proto.isAfter = isAfter; 5515 proto.isBefore = isBefore; 5516 proto.isBetween = isBetween; 5517 proto.isSame = isSame; 5518 proto.isSameOrAfter = isSameOrAfter; 5519 proto.isSameOrBefore = isSameOrBefore; 5520 proto.isValid = isValid$2; 5521 proto.lang = lang; 5522 proto.locale = locale; 5523 proto.localeData = localeData; 5524 proto.max = prototypeMax; 5525 proto.min = prototypeMin; 5526 proto.parsingFlags = parsingFlags; 5527 proto.set = stringSet; 5528 proto.startOf = startOf; 5529 proto.subtract = subtract; 5530 proto.toArray = toArray; 5531 proto.toObject = toObject; 5532 proto.toDate = toDate; 5533 proto.toISOString = toISOString; 5534 proto.inspect = inspect; 5535 proto.toJSON = toJSON; 5536 proto.toString = toString; 5537 proto.unix = unix; 5538 proto.valueOf = valueOf; 5539 proto.creationData = creationData; 5540 proto.year = getSetYear; 5541 proto.isLeapYear = getIsLeapYear; 5542 proto.weekYear = getSetWeekYear; 5543 proto.isoWeekYear = getSetISOWeekYear; 5544 proto.quarter = proto.quarters = getSetQuarter; 5545 proto.month = getSetMonth; 5546 proto.daysInMonth = getDaysInMonth; 5547 proto.week = proto.weeks = getSetWeek; 5548 proto.isoWeek = proto.isoWeeks = getSetISOWeek; 5549 proto.weeksInYear = getWeeksInYear; 5550 proto.isoWeeksInYear = getISOWeeksInYear; 5551 proto.date = getSetDayOfMonth; 5552 proto.day = proto.days = getSetDayOfWeek; 5553 proto.weekday = getSetLocaleDayOfWeek; 5554 proto.isoWeekday = getSetISODayOfWeek; 5555 proto.dayOfYear = getSetDayOfYear; 5556 proto.hour = proto.hours = getSetHour; 5557 proto.minute = proto.minutes = getSetMinute; 5558 proto.second = proto.seconds = getSetSecond; 5559 proto.millisecond = proto.milliseconds = getSetMillisecond; 5560 proto.utcOffset = getSetOffset; 5561 proto.utc = setOffsetToUTC; 5562 proto.local = setOffsetToLocal; 5563 proto.parseZone = setOffsetToParsedOffset; 5564 proto.hasAlignedHourOffset = hasAlignedHourOffset; 5565 proto.isDST = isDaylightSavingTime; 5566 proto.isLocal = isLocal; 5567 proto.isUtcOffset = isUtcOffset; 5568 proto.isUtc = isUtc; 5569 proto.isUTC = isUtc; 5570 proto.zoneAbbr = getZoneAbbr; 5571 proto.zoneName = getZoneName; 5572 proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); 5573 proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); 5574 proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); 5575 proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); 5576 proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); 5577 5578 function createUnix (input) { 5579 return createLocal(input * 1000); 5580 } 5581 5582 function createInZone () { 5583 return createLocal.apply(null, arguments).parseZone(); 5584 } 5585 5586 function preParsePostFormat (string) { 5587 return string; 5588 } 5589 5590 var proto$1 = Locale.prototype; 5591 5592 proto$1.calendar = calendar; 5593 proto$1.longDateFormat = longDateFormat; 5594 proto$1.invalidDate = invalidDate; 5595 proto$1.ordinal = ordinal; 5596 proto$1.preparse = preParsePostFormat; 5597 proto$1.postformat = preParsePostFormat; 5598 proto$1.relativeTime = relativeTime; 5599 proto$1.pastFuture = pastFuture; 5600 proto$1.set = set; 5601 5602 proto$1.months = localeMonths; 5603 proto$1.monthsShort = localeMonthsShort; 5604 proto$1.monthsParse = localeMonthsParse; 5605 proto$1.monthsRegex = monthsRegex; 5606 proto$1.monthsShortRegex = monthsShortRegex; 5607 proto$1.week = localeWeek; 5608 proto$1.firstDayOfYear = localeFirstDayOfYear; 5609 proto$1.firstDayOfWeek = localeFirstDayOfWeek; 5610 5611 proto$1.weekdays = localeWeekdays; 5612 proto$1.weekdaysMin = localeWeekdaysMin; 5613 proto$1.weekdaysShort = localeWeekdaysShort; 5614 proto$1.weekdaysParse = localeWeekdaysParse; 5615 5616 proto$1.weekdaysRegex = weekdaysRegex; 5617 proto$1.weekdaysShortRegex = weekdaysShortRegex; 5618 proto$1.weekdaysMinRegex = weekdaysMinRegex; 5619 5620 proto$1.isPM = localeIsPM; 5621 proto$1.meridiem = localeMeridiem; 5622 5623 function get$1 (format, index, field, setter) { 5624 var locale = getLocale(); 5625 var utc = createUTC().set(setter, index); 5626 return locale[field](utc, format); 5627 } 5628 5629 function listMonthsImpl (format, index, field) { 5630 if (isNumber(format)) { 5631 index = format; 5632 format = undefined; 5633 } 5634 5635 format = format || ''; 5636 5637 if (index != null) { 5638 return get$1(format, index, field, 'month'); 5639 } 5640 5641 var i; 5642 var out = []; 5643 for (i = 0; i < 12; i++) { 5644 out[i] = get$1(format, i, field, 'month'); 5645 } 5646 return out; 5647 } 5648 5649 // () 5650 // (5) 5651 // (fmt, 5) 5652 // (fmt) 5653 // (true) 5654 // (true, 5) 5655 // (true, fmt, 5) 5656 // (true, fmt) 5657 function listWeekdaysImpl (localeSorted, format, index, field) { 5658 if (typeof localeSorted === 'boolean') { 5659 if (isNumber(format)) { 5660 index = format; 5661 format = undefined; 5662 } 5663 5664 format = format || ''; 5665 } else { 5666 format = localeSorted; 5667 index = format; 5668 localeSorted = false; 5669 5670 if (isNumber(format)) { 5671 index = format; 5672 format = undefined; 5673 } 5674 5675 format = format || ''; 5676 } 5677 5678 var locale = getLocale(), 5679 shift = localeSorted ? locale._week.dow : 0; 5680 5681 if (index != null) { 5682 return get$1(format, (index + shift) % 7, field, 'day'); 5683 } 5684 5685 var i; 5686 var out = []; 5687 for (i = 0; i < 7; i++) { 5688 out[i] = get$1(format, (i + shift) % 7, field, 'day'); 5689 } 5690 return out; 5691 } 5692 5693 function listMonths (format, index) { 5694 return listMonthsImpl(format, index, 'months'); 5695 } 5696 5697 function listMonthsShort (format, index) { 5698 return listMonthsImpl(format, index, 'monthsShort'); 5699 } 5700 5701 function listWeekdays (localeSorted, format, index) { 5702 return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); 5703 } 5704 5705 function listWeekdaysShort (localeSorted, format, index) { 5706 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); 5707 } 5708 5709 function listWeekdaysMin (localeSorted, format, index) { 5710 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); 5711 } 5712 5713 getSetGlobalLocale('en', { 5714 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, 5715 ordinal : function (number) { 5716 var b = number % 10, 5717 output = (toInt(number % 100 / 10) === 1) ? 'th' : 5718 (b === 1) ? 'st' : 5719 (b === 2) ? 'nd' : 5720 (b === 3) ? 'rd' : 'th'; 5721 return number + output; 5722 } 5723 }); 5724 5725 // Side effect imports 5726 5727 hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); 5728 hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); 5729 5730 var mathAbs = Math.abs; 5731 5732 function abs () { 5733 var data = this._data; 5734 5735 this._milliseconds = mathAbs(this._milliseconds); 5736 this._days = mathAbs(this._days); 5737 this._months = mathAbs(this._months); 5738 5739 data.milliseconds = mathAbs(data.milliseconds); 5740 data.seconds = mathAbs(data.seconds); 5741 data.minutes = mathAbs(data.minutes); 5742 data.hours = mathAbs(data.hours); 5743 data.months = mathAbs(data.months); 5744 data.years = mathAbs(data.years); 5745 5746 return this; 5747 } 5748 5749 function addSubtract$1 (duration, input, value, direction) { 5750 var other = createDuration(input, value); 5751 5752 duration._milliseconds += direction * other._milliseconds; 5753 duration._days += direction * other._days; 5754 duration._months += direction * other._months; 5755 5756 return duration._bubble(); 5757 } 5758 5759 // supports only 2.0-style add(1, 's') or add(duration) 5760 function add$1 (input, value) { 5761 return addSubtract$1(this, input, value, 1); 5762 } 5763 5764 // supports only 2.0-style subtract(1, 's') or subtract(duration) 5765 function subtract$1 (input, value) { 5766 return addSubtract$1(this, input, value, -1); 5767 } 5768 5769 function absCeil (number) { 5770 if (number < 0) { 5771 return Math.floor(number); 5772 } else { 5773 return Math.ceil(number); 5774 } 5775 } 5776 5777 function bubble () { 5778 var milliseconds = this._milliseconds; 5779 var days = this._days; 5780 var months = this._months; 5781 var data = this._data; 5782 var seconds, minutes, hours, years, monthsFromDays; 5783 5784 // if we have a mix of positive and negative values, bubble down first 5785 // check: https://github.com/moment/moment/issues/2166 5786 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || 5787 (milliseconds <= 0 && days <= 0 && months <= 0))) { 5788 milliseconds += absCeil(monthsToDays(months) + days) * 864e5; 5789 days = 0; 5790 months = 0; 5791 } 5792 5793 // The following code bubbles up values, see the tests for 5794 // examples of what that means. 5795 data.milliseconds = milliseconds % 1000; 5796 5797 seconds = absFloor(milliseconds / 1000); 5798 data.seconds = seconds % 60; 5799 5800 minutes = absFloor(seconds / 60); 5801 data.minutes = minutes % 60; 5802 5803 hours = absFloor(minutes / 60); 5804 data.hours = hours % 24; 5805 5806 days += absFloor(hours / 24); 5807 5808 // convert days to months 5809 monthsFromDays = absFloor(daysToMonths(days)); 5810 months += monthsFromDays; 5811 days -= absCeil(monthsToDays(monthsFromDays)); 5812 5813 // 12 months -> 1 year 5814 years = absFloor(months / 12); 5815 months %= 12; 5816 5817 data.days = days; 5818 data.months = months; 5819 data.years = years; 5820 5821 return this; 5822 } 5823 5824 function daysToMonths (days) { 5825 // 400 years have 146097 days (taking into account leap year rules) 5826 // 400 years have 12 months === 4800 5827 return days * 4800 / 146097; 5828 } 5829 5830 function monthsToDays (months) { 5831 // the reverse of daysToMonths 5832 return months * 146097 / 4800; 5833 } 5834 5835 function as (units) { 5836 if (!this.isValid()) { 5837 return NaN; 5838 } 5839 var days; 5840 var months; 5841 var milliseconds = this._milliseconds; 5842 5843 units = normalizeUnits(units); 5844 5845 if (units === 'month' || units === '
vendor: 1,721 bytes, lines 5845-5890
5845year') { 5846 days = this._days + milliseconds / 864e5; 5847 months = this._months + daysToMonths(days); 5848 return units === 'month' ? months : months / 12; 5849 } else { 5850 // handle milliseconds separately because of floating point math errors (issue #1867) 5851 days = this._days + Math.round(monthsToDays(this._months)); 5852 switch (units) { 5853 case 'week' : return days / 7 + milliseconds / 6048e5; 5854 case 'day' : return days + milliseconds / 864e5; 5855 case 'hour' : return days * 24 + milliseconds / 36e5; 5856 case 'minute' : return days * 1440 + milliseconds / 6e4; 5857 case 'second' : return days * 86400 + milliseconds / 1000; 5858 // Math.floor prevents floating point math errors here 5859 case 'millisecond': return Math.floor(days * 864e5) + milliseconds; 5860 default: throw new Error('Unknown unit ' + units); 5861 } 5862 } 5863 } 5864 5865 // TODO: Use this.as('ms')? 5866 function valueOf$1 () { 5867 if (!this.isValid()) { 5868 return NaN; 5869 } 5870 return ( 5871 this._milliseconds + 5872 this._days * 864e5 + 5873 (this._months % 12) * 2592e6 + 5874 toInt(this._months / 12) * 31536e6 5875 ); 5876 } 5877 5878 function makeAs (alias) { 5879 return function () { 5880 return this.as(alias); 5881 }; 5882 } 5883 5884 var asMilliseconds = makeAs('ms'); 5885 var asSeconds = makeAs('s'); 5886 var asMinutes = makeAs('m'); 5887 var asHours = makeAs('h'); 5888 var asDays = makeAs('d'); 5889 var asWeeks = makeAs('w'); 5890 var asMonths = makeAs('M
vendor: 6,605 bytes, lines 5890-6082
5890'); 5891 var asYears = makeAs('y'); 5892 5893 function clone$1 () { 5894 return createDuration(this); 5895 } 5896 5897 function get$2 (units) { 5898 units = normalizeUnits(units); 5899 return this.isValid() ? this[units + 's']() : NaN; 5900 } 5901 5902 function makeGetter(name) { 5903 return function () { 5904 return this.isValid() ? this._data[name] : NaN; 5905 }; 5906 } 5907 5908 var milliseconds = makeGetter('milliseconds'); 5909 var seconds = makeGetter('seconds'); 5910 var minutes = makeGetter('minutes'); 5911 var hours = makeGetter('hours'); 5912 var days = makeGetter('days'); 5913 var months = makeGetter('months'); 5914 var years = makeGetter('years'); 5915 5916 function weeks () { 5917 return absFloor(this.days() / 7); 5918 } 5919 5920 var round = Math.round; 5921 var thresholds = { 5922 ss: 44, // a few seconds to seconds 5923 s : 45, // seconds to minute 5924 m : 45, // minutes to hour 5925 h : 22, // hours to day 5926 d : 26, // days to month 5927 M : 11 // months to year 5928 }; 5929 5930 // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 5931 function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { 5932 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 5933 } 5934 5935 function relativeTime$1 (posNegDuration, withoutSuffix, locale) { 5936 var duration = createDuration(posNegDuration).abs(); 5937 var seconds = round(duration.as('s')); 5938 var minutes = round(duration.as('m')); 5939 var hours = round(duration.as('h')); 5940 var days = round(duration.as('d')); 5941 var months = round(duration.as('M')); 5942 var years = round(duration.as('y')); 5943 5944 var a = seconds <= thresholds.ss && ['s', seconds] || 5945 seconds < thresholds.s && ['ss', seconds] || 5946 minutes <= 1 && ['m'] || 5947 minutes < thresholds.m && ['mm', minutes] || 5948 hours <= 1 && ['h'] || 5949 hours < thresholds.h && ['hh', hours] || 5950 days <= 1 && ['d'] || 5951 days < thresholds.d && ['dd', days] || 5952 months <= 1 && ['M'] || 5953 months < thresholds.M && ['MM', months] || 5954 years <= 1 && ['y'] || ['yy', years]; 5955 5956 a[2] = withoutSuffix; 5957 a[3] = +posNegDuration > 0; 5958 a[4] = locale; 5959 return substituteTimeAgo.apply(null, a); 5960 } 5961 5962 // This function allows you to set the rounding function for relative time strings 5963 function getSetRelativeTimeRounding (roundingFunction) { 5964 if (roundingFunction === undefined) { 5965 return round; 5966 } 5967 if (typeof(roundingFunction) === 'function') { 5968 round = roundingFunction; 5969 return true; 5970 } 5971 return false; 5972 } 5973 5974 // This function allows you to set a threshold for relative time strings 5975 function getSetRelativeTimeThreshold (threshold, limit) { 5976 if (thresholds[threshold] === undefined) { 5977 return false; 5978 } 5979 if (limit === undefined) { 5980 return thresholds[threshold]; 5981 } 5982 thresholds[threshold] = limit; 5983 if (threshold === 's') { 5984 thresholds.ss = limit - 1; 5985 } 5986 return true; 5987 } 5988 5989 function humanize (withSuffix) { 5990 if (!this.isValid()) { 5991 return this.localeData().invalidDate(); 5992 } 5993 5994 var locale = this.localeData(); 5995 var output = relativeTime$1(this, !withSuffix, locale); 5996 5997 if (withSuffix) { 5998 output = locale.pastFuture(+this, output); 5999 } 6000 6001 return locale.postformat(output); 6002 } 6003 6004 var abs$1 = Math.abs; 6005 6006 function sign(x) { 6007 return ((x > 0) - (x < 0)) || +x; 6008 } 6009 6010 function toISOString$1() { 6011 // for ISO strings we do not use the normal bubbling rules: 6012 // * milliseconds bubble up until they become hours 6013 // * days do not bubble at all 6014 // * months bubble up until they become years 6015 // This is because there is no context-free conversion between hours and days 6016 // (think of clock changes) 6017 // and also not between days and months (28-31 days per month) 6018 if (!this.isValid()) { 6019 return this.localeData().invalidDate(); 6020 } 6021 6022 var seconds = abs$1(this._milliseconds) / 1000; 6023 var days = abs$1(this._days); 6024 var months = abs$1(this._months); 6025 var minutes, hours, years; 6026 6027 // 3600 seconds -> 60 minutes -> 1 hour 6028 minutes = absFloor(seconds / 60); 6029 hours = absFloor(minutes / 60); 6030 seconds %= 60; 6031 minutes %= 60; 6032 6033 // 12 months -> 1 year 6034 years = absFloor(months / 12); 6035 months %= 12; 6036 6037 6038 // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js 6039 var Y = years; 6040 var M = months; 6041 var D = days; 6042 var h = hours; 6043 var m = minutes; 6044 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; 6045 var total = this.asSeconds(); 6046 6047 if (!total) { 6048 // this is the same as C#'s (Noda) and python (isodate)... 6049 // but not other JS (goog.date) 6050 return 'P0D'; 6051 } 6052 6053 var totalSign = total < 0 ? '-' : ''; 6054 var ymSign = sign(this._months) !== sign(total) ? '-' : ''; 6055 var daysSign = sign(this._days) !== sign(total) ? '-' : ''; 6056 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; 6057 6058 return totalSign + 'P' + 6059 (Y ? ymSign + Y + 'Y' : '') + 6060 (M ? ymSign + M + 'M' : '') + 6061 (D ? daysSign + D + 'D' : '') + 6062 ((h || m || s) ? 'T' : '') + 6063 (h ? hmsSign + h + 'H' : '') + 6064 (m ? hmsSign + m + 'M' : '') + 6065 (s ? hmsSign + s + 'S' : ''); 6066 } 6067 6068 var proto$2 = Duration.prototype; 6069 6070 proto$2.isValid = isValid$1; 6071 proto$2.abs = abs; 6072 proto$2.add = add$1; 6073 proto$2.subtract = subtract$1; 6074 proto$2.as = as; 6075 proto$2.asMilliseconds = asMilliseconds; 6076 proto$2.asSeconds = asSeconds; 6077 proto$2.asMinutes = asMinutes; 6078 proto$2.asHours = asHours; 6079 proto$2.asDays = asDays; 6080 proto$2.asWeeks = asWeeks; 6081 proto$2.asMonths = asMonths; 6082 proto$2.as
6082Years = asYears; 6083 proto$2.valueOf = valueOf$1; 6084 proto$2._bubble = bubble; 6085 proto$2.clone = clone$1; 6086 proto$2.get = get$2; 6087 proto$2.milliseconds = milliseconds; 6088 proto$2.seconds = seconds; 6089 proto$2.minutes = minutes; 6090 proto$2.hours = hours; 6091 proto$2.days = days; 6092 proto$2.weeks = weeks; 6093 proto$2.months = months; 6094 proto$2.years = years; 6095 proto$2.humanize = humanize; 6096 proto$2.toISOString = toISOString$1; 6097 proto$2.toString = toISOString$1; 6098 proto$2.toJSON = toISOString$1; 6099 proto$2.locale = locale; 6100 proto$2.localeData = localeData; 6101 6102 proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); 6103 proto$2.lang = lang; 6104 6105 // Side effect imports 6106 6107 // FORMATTING 6108 6109 addFormatToken('X', 0, 0, 'unix'); 6110 addFormatToken('x', 0, 0, 'valueOf'); 6111 6112 // PARSING 6113 6114 addRegexToken('x', matchSigned); 6115 addRegexToken('X', matchTimestamp); 6116 addParseToken('X', function (input, array, config) { 6117 config._d = new Date(parseFloat(input, 10) * 1000); 6118 }); 6119 addParseToken('x', function (input, array, config) { 6120 config._d = new Date(toInt(input)); 6121 }); 6122 6123 // Side effect imports 6124 6125 6126 hooks.version = '2.22.2'; 6127 6128 setHookCallback(createLocal); 6129 6130 hooks.fn = proto; 6131 hooks.min = min; 6132 hooks.max = max; 6133 hooks.now = now; 6134 hooks.utc = createUTC; 6135 hooks.unix = createUnix; 6136 hooks.months = listMonths; 6137 hooks.isDate = isDate; 6138 hooks.locale = getSetGlobalLocale; 6139 hooks.invalid = createInvalid; 6140 hooks.duration = createDuration; 6141 hooks.isMoment = isMoment; 6142 hooks.weekdays = listWeekdays; 6143 hooks.parseZone = createInZone; 6144 hooks.localeData = getLocale; 6145 hooks.isDuration = isDuration; 6146 hooks.monthsShort = listMonthsShort; 6147 hooks.weekdaysMin = listWeekdaysMin; 6148 hooks.defineLocale = defineLocale; 6149 hooks.updateLocale = updateLocale; 6150 hooks.locales = listLocales; 6151 hooks.weekdaysShort = listWeekdaysShort; 6152 hooks.normalizeUnits = normalizeUnits; 6153 hooks.relativeTimeRounding = getSetRelativeTimeRounding; 6154 hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; 6155 hooks.calendarFormat = getCalendarFormat; 6156 hooks.prototype = proto; 6157 6158 // currently HTML5 input type only supports 24-hour formats 6159 hooks.HTML5_FMT = { 6160 DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // <input type="datetime-local" /> 6161 DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // <input type="datetime-local" step="1" /> 6162 DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // <input type="datetime-local" step="0.001" /> 6163 DATE: 'YYYY-MM-DD', // <input type="date" /> 6164 TIME: 'HH:mm', // <input type="time" /> 6165 TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" /> 6166 TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" /> 6167 WEEK: 'YYYY-[W]WW', // <input type="week" /> 6168 MONTH: 'YYYY-MM' // <input type="month" /> 6169 }; 6170 6171 return hooks; 6172 6173}))); 6174 6175},{}],7:[function(require,module,exports){ 6176/** 6177 * @namespace Chart 6178 */ 6179var Chart = require(30)(); 6180 6181Chart.helpers = require(46); 6182 6183// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! 6184require(28)(Chart); 6185 6186Chart.Animation = require(22); 6187Chart.animationService = require(23); 6188Chart.defaults = require(26); 6189Chart.Element = require(27); 6190Chart.elements = require(41); 6191Chart.Interaction = require(29); 6192Chart.layouts = require(31); 6193Chart.platform = require(49); 6194Chart.plugins = require(32); 6195Chart.Scale = require(33); 6196Chart.scaleService = require(34); 6197Chart.Ticks = require(35); 6198Chart.Tooltip = require(36); 6199 6200require(24)(Chart); 6201require(25)(Chart); 6202 6203require(56)(Chart); 6204require(54)(Chart); 6205require(55)(Chart); 6206require(57)(Chart); 6207require(58)(Chart); 6208require(59)(Chart); 6209 6210// Controllers must be loaded after elements 6211// See Chart.core.datasetController.dataElementType 6212require(15)(Chart); 6213require(16)(Chart); 6214require(17)(Chart); 6215require(18)(Chart); 6216require(19)(Chart); 6217require(20)(Chart); 6218require(21)(Chart); 6219 6220require(8)(Chart); 6221require(9)(Chart); 6222require(10)(Chart); 6223require(11)(Chart); 6224require(12)(Chart); 6225require(13)(Chart); 6226require(14)(Chart); 6227 6228// Loading built-in plugins 6229var plugins = require(50); 6230for (var k in plugins) { 6231 if (plugins.hasOwnProperty(k)) { 6232 Chart.plugins.register(plugins[k]); 6233 } 6234} 6235 6236Chart.platform.initialize(); 6237 6238module.exports = Chart; 6239if (typeof window !== 'undefined') { 6240 window.Chart = Chart; 6241} 6242 6243// DEPRECATIONS 6244 6245/** 6246 * Provided for backward compatibility, not available anymore 6247 * @namespace Chart.Legend 6248 * @deprecated since version 2.1.5 6249 * @todo remove at version 3 6250 * @private 6251 */ 6252Chart.Legend = plugins.legend._element; 6253 6254/** 6255 * Provided for backward compatibility, not available anymore 6256 * @namespace Chart.Title 6257 * @deprecated since version 2.1.5 6258 * @todo remove at version 3 6259 * @private 6260 */ 6261Chart.Title = plugins.title._element; 6262 6263/** 6264 * Provided for backward compatibility, use Chart.plugins instead
6265 * @namespace Chart.pluginService 6266 * @deprecated since version 2.1.5 6267 * @todo remove at version 3 6268 * @private 6269 */ 6270Chart.pluginService = Chart.plugins; 6271 6272/** 6273 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no 6274 * effect, instead simply create/register plugins via plain JavaScript objects. 6275 * @interface Chart.PluginBase 6276 * @deprecated since version 2.5.0 6277 * @todo remove at version 3 6278 * @private 6279 */ 6280Chart.PluginBase = Chart.Element.extend({}); 6281 6282/** 6283 * Provided for backward compatibility, use Chart.helpers.canvas instead. 6284 * @namespace Chart.canvasHelpers 6285 * @deprecated since version 2.6.0 6286 * @todo remove at version 3 6287 * @private 6288 */ 6289Chart.canvasHelpers = Chart.helpers.canvas; 6290 6291/** 6292 * Provided for backward compatibility, use Chart.layouts instead. 6293 * @namespace Chart.layoutService 6294 * @deprecated since version 2.8.0 6295 * @todo remove at version 3 6296 * @private 6297 */ 6298Chart.layoutService = Chart.layouts; 6299 6300},{"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,"41":41,"46":46,"49":49,"50":50,"54":54,"55":55,"56":56,"57":57,"58":58,"59":59,"8":8,"9":9}],8:[function(require,module,exports){ 6301'use strict'; 6302 6303module.exports = function(Chart) { 6304 6305 Chart.Bar = function(context, config) { 6306 config.type = 'bar'; 6307 6308 return new Chart(context, config); 6309 }; 6310 6311}; 6312 6313},{}],9:[function(require,module,exports){ 6314'use strict'; 6315 6316module.exports = function(Chart) { 6317 6318 Chart.Bubble = function(context, config) { 6319 config.type = 'bubble'; 6320 return new Chart(context, config); 6321 }; 6322 6323}; 6324 6325},{}],10:[function(require,module,exports){ 6326'use strict'; 6327 6328module.exports = function(Chart) { 6329 6330 Chart.Doughnut = function(context, config) { 6331 config.type = 'doughnut'; 6332 6333 return new Chart(context, config); 6334 }; 6335 6336}; 6337 6338},{}],11:[function(require,module,exports){ 6339'use strict'; 6340 6341module.exports = function(Chart) { 6342 6343 Chart.Line = function(context, config) { 6344 config.type = 'line'; 6345 6346 return new Chart(context, config); 6347 }; 6348 6349}; 6350 6351},{}],12:[function(require,module,exports){ 6352'use strict'; 6353 6354module.exports = function(Chart) { 6355 6356 Chart.PolarArea = function(context, config) { 6357 config.type = 'polarArea'; 6358 6359 return new Chart(context, config); 6360 }; 6361 6362}; 6363 6364},{}],13:[function(require,module,exports){ 6365'use strict'; 6366 6367module.exports = function(Chart) { 6368 6369 Chart.Radar = function(context, config) { 6370 config.type = 'radar'; 6371 6372 return new Chart(context, config); 6373 }; 6374 6375}; 6376 6377},{}],14:[function(require,module,exports){ 6378'use strict'; 6379 6380module.exports = function(Chart) { 6381 Chart.Scatter = function(context, config) { 6382 config.type = 'scatter'; 6383 return new Chart(context, config); 6384 }; 6385}; 6386 6387},{}],15:[function(require,module,exports){ 6388'use strict'; 6389 6390var defaults = require(26); 6391var elements = require(41); 6392var helpers = require(46); 6393 6394defaults._set('bar', { 6395 hover: { 6396 mode: 'label' 6397 }, 6398 6399 scales: { 6400 xAxes: [{ 6401 type: 'category', 6402 6403 // Specific to Bar Controller 6404 categoryPercentage: 0.8, 6405 barPercentage: 0.9, 6406 6407 // offset settings 6408 offset: true, 6409 6410 // grid line settings 6411 gridLines: { 6412 offsetGridLines: true 6413 } 6414 }], 6415 6416 yAxes: [{ 6417 type: 'linear' 6418 }] 6419 } 6420}); 6421 6422defaults._set('horizontalBar', { 6423 hover: { 6424 mode: 'index', 6425 axis: 'y' 6426 }, 6427 6428 scales: { 6429 xAxes: [{ 6430 type: 'linear', 6431 position: 'bottom' 6432 }], 6433 6434 yAxes: [{ 6435 position: 'left', 6436 type: 'category', 6437 6438 // Specific to Horizontal Bar Controller 6439 categoryPercentage: 0.8, 6440 barPercentage: 0.9, 6441 6442 // offset settings 6443 offset: true, 6444 6445 // grid line settings 6446 gridLines: { 6447 offsetGridLines: true 6448 } 6449 }] 6450 }, 6451 6452 elements: { 6453 rectangle: { 6454 borderSkipped: 'left' 6455 } 6456 }, 6457 6458 tooltips: { 6459 callbacks: { 6460 title: function(item, data) { 6461 // Pick first xLabel for now 6462 var title = ''; 6463 6464 if (item.length > 0) { 6465 if (item[0].yLabel) { 6466 title = item[0].yLabel; 6467 } else if (data.labels.length > 0 && item[0].index < data.labels.length) { 6468 title = data.labels[item[0].index]; 6469 } 6470 } 6471 6472 return title; 6473 }, 6474 6475 label: function(item, data) { 6476 var datasetLabel = data.datasets[item.datasetIndex].label || ''; 6477 return datasetLabel + ': ' + item.xLabel; 6478 } 6479 }, 6480 mode: 'index', 6481 axis: 'y' 6482 } 6483}); 6484 6485/** 6486 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. 6487 * @private 6488 */ 6489function computeMinSampleSize(scale, pixels) { 6490 var min = scale.isHorizontal() ? scale.width : scale.height; 6491 var ticks = scale.getTicks(); 6492 var prev, curr, i, ilen; 6493 6494 for (i = 1, ilen = pixels.length; i < ilen; ++i) { 6495 min = Math.min(min, pixels[i] - pixels[i - 1]); 6496 } 6497 6498 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 6499 curr = scale.getPixelForTick(i); 6500 min = i > 0 ? Math.min(min, curr - prev) : min; 6501 prev = curr; 6502 } 6503 6504 return min; 6505} 6506 6507/** 6508 * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, 6509 * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This 6510 * mode currently always generates bars equally sized (until we introduce scriptable options?). 6511 * @private 6512 */ 6513function computeFitCategoryTraits(index, ruler, options) { 6514 var thickness = options.barThickness; 6515 var count = ruler.stackCount; 6516 var curr = ruler.pixels[index]; 6517 var size, ratio; 6518 6519 if (helpers.isNullOrUndef(thickness)) { 6520 size = ruler.min * options.categoryPercentage; 6521 ratio = options.barPercentage; 6522 } else { 6523 // When bar thickness is enforced, category and bar percentages are ignored. 6524 // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') 6525 // and deprecate barPercentage since this value is ignored when thickness is absolute. 6526 size = thickness * count; 6527 ratio = 1; 6528 } 6529 6530 return { 6531 chunk: size / count, 6532 ratio: ratio, 6533 start: curr - (size / 2) 6534 }; 6535} 6536 6537/** 6538 * Computes an "optimal" category that globally arranges bars side by side (no gap when 6539 * percentage options are 1), based on the previous and following categories. This mode 6540 * generates bars with different widths when data are not evenly spaced. 6541 * @private 6542 */ 6543function computeFlexCategoryTraits(index, ruler, options) { 6544 var pixels = ruler.pixels; 6545 var curr = pixels[index]; 6546 var prev = index > 0 ? pixels[index - 1] : null; 6547 var next = index < pixels.length - 1 ? pixels[index + 1] : null; 6548 var percent = options.categoryPercentage; 6549 var start, size; 6550 6551 if (prev === null) { 6552 // first data: its size is double based on the next point or, 6553 // if it's also the last data, we use the scale end extremity. 6554 prev = curr - (next === null ? ruler.end - curr : next - curr); 6555 } 6556 6557 if (next === null) { 6558 // last data: its size is also double based on the previous point. 6559 next = curr + curr - prev; 6560 } 6561 6562 start = curr - ((curr - prev) / 2) * percent; 6563 size = ((next - prev) / 2) * percent; 6564 6565 return { 6566 chunk: size / ruler.stackCount, 6567 ratio: options.barPercentage, 6568 start: start 6569 }; 6570} 6571 6572module.exports = function(Chart) { 6573 6574 Chart.controllers.bar = Chart.DatasetController.extend({ 6575 6576 dataElementType: elements.Rectangle, 6577 6578 initialize: function() { 6579 var me = this; 6580 var meta; 6581 6582 Chart.DatasetController.prototype.initialize.apply(me, arguments); 6583 6584 meta = me.getMeta(); 6585 meta.stack = me.getDataset().stack; 6586 meta.bar = true; 6587 }, 6588 6589 update: function(reset) { 6590 var me = this; 6591 var rects = me.getMeta().data; 6592 var i, ilen; 6593 6594 me._ruler = me.getRuler(); 6595 6596 for (i = 0, ilen = rects.length; i < ilen; ++i) { 6597 me.updateElement(rects[i], i, reset); 6598 } 6599 }, 6600 6601 updateElement: function(rectangle, index, reset) { 6602 var me = this; 6603 var chart = me.chart; 6604 var meta = me.getMeta(); 6605 var dataset = me.getDataset(); 6606 var custom = rectangle.custom || {}; 6607 var rectangleOptions = chart.options.elements.rectangle; 6608 6609 rectangle._xScale = me.getScaleForId(meta.xAxisID); 6610 rectangle._yScale = me.getScaleForId(meta.yAxisID); 6611 rectangle._datasetIndex = me.index; 6612 rectangle._index = index; 6613 6614 rectangle._model = { 6615 datasetLabel: dataset.label, 6616 label: chart.data.labels[index], 6617 borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, 6618 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), 6619 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), 6620 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) 6621 }; 6622 6623 me.updateElementGeometry(rectangle, index, reset); 6624 6625 rectangle.pivot(); 6626 }, 6627 6628 /** 6629 * @private 6630 */ 6631 updateElementGeometry: function(rectangle, index, reset) { 6632 var me = this; 6633 var model = rectangle._model; 6634 var vscale = me.getValueScale(); 6635 var base = vscale.getBasePixel(); 6636 var horizontal = vscale.isHorizontal(); 6637 var ruler = me._ruler || me.getRuler(); 6638 var vpixels = me.calculateBarValuePixels(me.index, index); 6639 var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); 6640 6641 model.horizontal = horizontal; 6642 model.base = reset ? base : vpixels.base; 6643 model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; 6644 model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; 6645 model.height = horizontal ? ipixels.size : undefined; 6646 model.width = horizontal ? undefined : ipixels.size; 6647 }, 6648 6649 /** 6650 * @private 6651 */ 6652 getValueScaleId: function() { 6653 return this.getMeta().yAxisID; 6654 }, 6655 6656 /** 6657 * @private 6658 */ 6659 getIndexScaleId: function() { 6660 return this.getMeta().xAxisID; 6661 }, 6662 6663 /** 6664 * @private 6665 */ 6666 getValueScale: function() {
6667 return this.getScaleForId(this.getValueScaleId()); 6668 }, 6669 6670 /** 6671 * @private 6672 */ 6673 getIndexScale: function() { 6674 return this.getScaleForId(this.getIndexScaleId()); 6675 }, 6676 6677 /** 6678 * Returns the stacks based on groups and bar visibility. 6679 * @param {Number} [last] - The dataset index 6680 * @returns {Array} The stack list 6681 * @private 6682 */ 6683 _getStacks: function(last) { 6684 var me = this; 6685 var chart = me.chart; 6686 var scale = me.getIndexScale(); 6687 var stacked = scale.options.stacked; 6688 var ilen = last === undefined ? chart.data.datasets.length : last + 1; 6689 var stacks = []; 6690 var i, meta; 6691 6692 for (i = 0; i < ilen; ++i) { 6693 meta = chart.getDatasetMeta(i); 6694 if (meta.bar && chart.isDatasetVisible(i) && 6695 (stacked === false || 6696 (stacked === true && stacks.indexOf(meta.stack) === -1) || 6697 (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { 6698 stacks.push(meta.stack); 6699 } 6700 } 6701 6702 return stacks; 6703 }, 6704 6705 /** 6706 * Returns the effective number of stacks based on groups and bar visibility. 6707 * @private 6708 */ 6709 getStackCount: function() { 6710 return this._getStacks().length; 6711 }, 6712 6713 /** 6714 * Returns the stack index for the given dataset based on groups and bar visibility. 6715 * @param {Number} [datasetIndex] - The dataset index 6716 * @param {String} [name] - The stack name to find 6717 * @returns {Number} The stack index 6718 * @private 6719 */ 6720 getStackIndex: function(datasetIndex, name) { 6721 var stacks = this._getStacks(datasetIndex); 6722 var index = (name !== undefined) 6723 ? stacks.indexOf(name) 6724 : -1; // indexOf returns -1 if element is not present 6725 6726 return (index === -1) 6727 ? stacks.length - 1 6728 : index; 6729 }, 6730 6731 /** 6732 * @private 6733 */ 6734 getRuler: function() { 6735 var me = this; 6736 var scale = me.getIndexScale(); 6737 var stackCount = me.getStackCount(); 6738 var datasetIndex = me.index; 6739 var isHorizontal = scale.isHorizontal(); 6740 var start = isHorizontal ? scale.left : scale.top; 6741 var end = start + (isHorizontal ? scale.width : scale.height); 6742 var pixels = []; 6743 var i, ilen, min; 6744 6745 for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { 6746 pixels.push(scale.getPixelForValue(null, i, datasetIndex)); 6747 } 6748 6749 min = helpers.isNullOrUndef(scale.options.barThickness) 6750 ? computeMinSampleSize(scale, pixels) 6751 : -1; 6752 6753 return { 6754 min: min, 6755 pixels: pixels, 6756 start: start, 6757 end: end, 6758 stackCount: stackCount, 6759 scale: scale 6760 }; 6761 }, 6762 6763 /** 6764 * Note: pixel values are not clamped to the scale area. 6765 * @private 6766 */ 6767 calculateBarValuePixels: function(datasetIndex, index) { 6768 var me = this; 6769 var chart = me.chart; 6770 var meta = me.getMeta(); 6771 var scale = me.getValueScale(); 6772 var datasets = chart.data.datasets; 6773 var value = scale.getRightValue(datasets[datasetIndex].data[index]); 6774 var stacked = scale.options.stacked; 6775 var stack = meta.stack; 6776 var start = 0; 6777 var i, imeta, ivalue, base, head, size; 6778 6779 if (stacked || (stacked === undefined && stack !== undefined)) { 6780 for (i = 0; i < datasetIndex; ++i) { 6781 imeta = chart.getDatasetMeta(i); 6782 6783 if (imeta.bar && 6784 imeta.stack === stack && 6785 imeta.controller.getValueScaleId() === scale.id && 6786 chart.isDatasetVisible(i)) { 6787 6788 ivalue = scale.getRightValue(datasets[i].data[index]); 6789 if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { 6790 start += ivalue; 6791 } 6792 } 6793 } 6794 } 6795 6796 base = scale.getPixelForValue(start); 6797 head = scale.getPixelForValue(start + value); 6798 size = (head - base) / 2; 6799 6800 return { 6801 size: size, 6802 base: base, 6803 head: head, 6804 center: head + size / 2 6805 }; 6806 }, 6807 6808 /** 6809 * @private 6810 */ 6811 calculateBarIndexPixels: function(datasetIndex, index, ruler) { 6812 var me = this; 6813 var options = ruler.scale.options; 6814 var range = options.barThickness === 'flex' 6815 ? computeFlexCategoryTraits(index, ruler, options) 6816 : computeFitCategoryTraits(index, ruler, options); 6817 6818 var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); 6819 var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); 6820 var size = Math.min( 6821 helpers.valueOrDefault(options.maxBarThickness, Infinity), 6822 range.chunk * range.ratio); 6823 6824 return { 6825 base: center - size / 2, 6826 head: center + size / 2, 6827 center: center, 6828 size: size 6829 }; 6830 }, 6831 6832 draw: function() { 6833 var me = this; 6834 var chart = me.chart; 6835 var scale = me.getValueScale(); 6836 var rects = me.getMeta().data; 6837 var dataset = me.getDataset(); 6838 var ilen = rects.length; 6839 var i = 0; 6840 6841 helpers.canvas.clipArea(chart.ctx, chart.chartArea); 6842 6843 for (; i < ilen; ++i) { 6844 if (!isNaN(scale.getRightValue(dataset.data[i]))) { 6845 rects[i].draw(); 6846 } 6847 } 6848 6849 helpers.canvas.unclipArea(chart.ctx); 6850 }, 6851 }); 6852 6853 Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ 6854 /** 6855 * @private 6856 */ 6857 getValueScaleId: function() { 6858 return this.getMeta().xAxisID; 6859 }, 6860 6861 /** 6862 * @private 6863 */ 6864 getIndexScaleId: function() { 6865 return this.getMeta().yAxisID; 6866 } 6867 }); 6868}; 6869 6870},{"26":26,"41":41,"46":46}],16:[function(require,module,exports){ 6871'use strict'; 6872 6873var defaults = require(26); 6874var elements = require(41); 6875var helpers = require(46); 6876 6877defaults._set('bubble', { 6878 hover: {
6879 mode: 'single' 6880 }, 6881 6882 scales: { 6883 xAxes: [{ 6884 type: 'linear', // bubble should probably use a linear scale by default 6885 position: 'bottom', 6886 id: 'x-axis-0' // need an ID so datasets can reference the scale 6887 }], 6888 yAxes: [{ 6889 type: 'linear', 6890 position: 'left', 6891 id: 'y-axis-0' 6892 }] 6893 }, 6894 6895 tooltips: { 6896 callbacks: { 6897 title: function() { 6898 // Title doesn't make sense for scatter since we format the data as a point 6899 return ''; 6900 }, 6901 label: function(item, data) { 6902 var datasetLabel = data.datasets[item.datasetIndex].label || ''; 6903 var dataPoint = data.datasets[item.datasetIndex].data[item.index]; 6904 return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; 6905 } 6906 } 6907 } 6908}); 6909 6910 6911module.exports = function(Chart) { 6912 6913 Chart.controllers.bubble = Chart.DatasetController.extend({ 6914 /** 6915 * @protected 6916 */ 6917 dataElementType: elements.Point, 6918 6919 /** 6920 * @protected 6921 */ 6922 update: function(reset) { 6923 var me = this; 6924 var meta = me.getMeta(); 6925 var points = meta.data; 6926 6927 // Update Points 6928 helpers.each(points, function(point, index) { 6929 me.updateElement(point, index, reset); 6930 }); 6931 }, 6932 6933 /** 6934 * @protected 6935 */ 6936 updateElement: function(point, index, reset) { 6937 var me = this; 6938 var meta = me.getMeta(); 6939 var custom = point.custom || {}; 6940 var xScale = me.getScaleForId(meta.xAxisID); 6941 var yScale = me.getScaleForId(meta.yAxisID); 6942 var options = me._resolveElementOptions(point, index); 6943 var data = me.getDataset().data[index]; 6944 var dsIndex = me.index; 6945 6946 var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); 6947 var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); 6948 6949 point._xScale = xScale; 6950 point._yScale = yScale; 6951 point._options = options; 6952 point._datasetIndex = dsIndex; 6953 point._index = index; 6954 point._model = { 6955 backgroundColor: options.backgroundColor, 6956 borderColor: options.borderColor, 6957 borderWidth: options.borderWidth, 6958 hitRadius: options.hitRadius, 6959 pointStyle: options.pointStyle, 6960 rotation: options.rotation, 6961 radius: reset ? 0 : options.radius, 6962 skip: custom.skip || isNaN(x) || isNaN(y), 6963 x: x, 6964 y: y, 6965 }; 6966 6967 point.pivot(); 6968 }, 6969 6970 /** 6971 * @protected 6972 */ 6973 setHoverStyle: function(point) { 6974 var model = point._model; 6975 var options = point._options; 6976 point.$previousStyle = { 6977 backgroundColor: model.backgroundColor, 6978 borderColor: model.borderColor, 6979 borderWidth: model.borderWidth, 6980 radius: model.radius 6981 }; 6982 model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); 6983 model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); 6984 model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); 6985 model.radius = options.radius + options.hoverRadius; 6986 }, 6987 6988 /** 6989 * @private 6990 */ 6991 _resolveElementOptions: function(point, index) { 6992 var me = this; 6993 var chart = me.chart; 6994 var datasets = chart.data.datasets; 6995 var dataset = datasets[me.index]; 6996 var custom = point.custom || {}; 6997 var options = chart.options.elements.point; 6998 var resolve = helpers.options.resolve; 6999 var data = dataset.data[index]; 7000 var values = {}; 7001 var i, ilen, key; 7002 7003 // Scriptable options 7004 var context = { 7005 chart: chart, 7006 dataIndex: index, 7007 dataset: dataset, 7008 datasetIndex: me.index 7009 }; 7010 7011 var keys = [ 7012 'backgroundColor', 7013 'borderColor', 7014 'borderWidth', 7015 'hoverBackgroundColor', 7016 'hoverBorderColor', 7017 'hoverBorderWidth', 7018 'hoverRadius', 7019 'hitRadius', 7020 'pointStyle', 7021 'rotation' 7022 ]; 7023 7024 for (i = 0, ilen = keys.length; i < ilen; ++i) { 7025 key = keys[i]; 7026 values[key] = resolve([ 7027 custom[key], 7028 dataset[key], 7029 options[key] 7030 ], context, index); 7031 } 7032 7033 // Custom radius resolution 7034 values.radius = resolve([ 7035 custom.radius, 7036 data ? data.r : undefined, 7037 dataset.radius, 7038 options.radius 7039 ], context, index); 7040 return values; 7041 } 7042 }); 7043}; 7044 7045},{"26":26,"41":41,"46":46}],17:[function(require,module,exports){ 7046'use strict'; 7047 7048var defaults = require(26); 7049var elements = require(41); 7050var helpers = require(46); 7051 7052defaults._set('doughnut', { 7053 animation: { 7054 // Boolean - Whether we animate the rotation of the Doughnut 7055 animateRotate: true, 7056 // Boolean - Whether we animate scaling the Doughnut from the centre 7057 animateScale: false 7058 }, 7059 hover: {
7060 mode: 'single' 7061 }, 7062 legendCallback: function(chart) { 7063 var text = []; 7064 text.push('<ul class="' + chart.id + '-legend">'); 7065 7066 var data = chart.data; 7067 var datasets = data.datasets; 7068 var labels = data.labels; 7069 7070 if (datasets.length) { 7071 for (var i = 0; i < datasets[0].data.length; ++i) { 7072 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); 7073 if (labels[i]) { 7074 text.push(labels[i]); 7075 } 7076 text.push('</li>'); 7077 } 7078 } 7079 7080 text.push('</ul>'); 7081 return text.join(''); 7082 }, 7083 legend: { 7084 labels: { 7085 generateLabels: function(chart) { 7086 var data = chart.data; 7087 if (data.labels.length && data.datasets.length) { 7088 return data.labels.map(function(label, i) { 7089 var meta = chart.getDatasetMeta(0); 7090 var ds = data.datasets[0]; 7091 var arc = meta.data[i]; 7092 var custom = arc && arc.custom || {}; 7093 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7094 var arcOpts = chart.options.elements.arc; 7095 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); 7096 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); 7097 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); 7098 7099 return { 7100 text: label, 7101 fillStyle: fill, 7102 strokeStyle: stroke, 7103 lineWidth: bw, 7104 hidden: isNaN(ds.data[i]) || meta.data[i].hidden, 7105 7106 // Extra data used for toggling the correct item 7107 index: i 7108 }; 7109 }); 7110 } 7111 return []; 7112 } 7113 }, 7114 7115 onClick: function(e, legendItem) { 7116 var index = legendItem.index; 7117 var chart = this.chart; 7118 var i, ilen, meta; 7119 7120 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 7121 meta = chart.getDatasetMeta(i); 7122 // toggle visibility of index if exists 7123 if (meta.data[index]) { 7124 meta.data[index].hidden = !meta.data[index].hidden; 7125 } 7126 } 7127 7128 chart.update(); 7129 } 7130 }, 7131 7132 // The percentage of the chart that we cut out of the middle. 7133 cutoutPercentage: 50, 7134 7135 // The rotation of the chart, where the first data arc begins. 7136 rotation: Math.PI * -0.5, 7137 7138 // The total circumference of the chart. 7139 circumference: Math.PI * 2.0, 7140 7141 // Need to override these to give a nice default 7142 tooltips: { 7143 callbacks: { 7144 title: function() { 7145 return ''; 7146 }, 7147 label: function(tooltipItem, data) { 7148 var dataLabel = data.labels[tooltipItem.index]; 7149 var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; 7150 7151 if (helpers.isArray(dataLabel)) { 7152 // show value on first line of multiline label 7153 // need to clone because we are changing the value 7154 dataLabel = dataLabel.slice(); 7155 dataLabel[0] += value; 7156 } else { 7157 dataLabel += value; 7158 } 7159 7160 return dataLabel; 7161 } 7162 } 7163 } 7164}); 7165 7166defaults._set('pie', helpers.clone(defaults.doughnut)); 7167defaults._set('pie', { 7168 cutoutPercentage: 0 7169}); 7170 7171module.exports = function(Chart) { 7172 7173 Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ 7174 7175 dataElementType: elements.Arc, 7176 7177 linkScales: helpers.noop, 7178 7179 // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly 7180 getRingIndex: function(datasetIndex) { 7181 var ringIndex = 0; 7182 7183 for (var j = 0; j < datasetIndex; ++j) { 7184 if (this.chart.isDatasetVisible(j)) { 7185 ++ringIndex; 7186 } 7187 } 7188 7189 return ringIndex; 7190 }, 7191 7192 update: function(reset) { 7193 var me = this; 7194 var chart = me.chart; 7195 var chartArea = chart.chartArea; 7196 var opts = chart.options; 7197 var arcOpts = opts.elements.arc; 7198 var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; 7199 var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; 7200 var minSize = Math.min(availableWidth, availableHeight); 7201 var offset = {x: 0, y: 0}; 7202 var meta = me.getMeta(); 7203 var cutoutPercentage = opts.cutoutPercentage; 7204 var circumference = opts.circumference; 7205 7206 // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc 7207 if (circumference < Math.PI * 2.0) { 7208 var startAngle = opts.rotation % (Math.PI * 2.0); 7209 startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); 7210 var endAngle = startAngle + circumference; 7211 var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; 7212 var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; 7213 var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); 7214 var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); 7215 var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); 7216 var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); 7217 var cutout = cutoutPercentage / 100.0; 7218 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))}; 7219 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))}; 7220 var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; 7221 minSize = Math.min(availableWidth / size.width, availableHeight / size.height); 7222 offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; 7223 } 7224 7225 chart.borderWidth = me.getMaxBorderWidth(meta.data); 7226 chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); 7227 chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
7228 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); 7229 chart.offsetX = offset.x * chart.outerRadius; 7230 chart.offsetY = offset.y * chart.outerRadius; 7231 7232 meta.total = me.calculateTotal(); 7233 7234 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); 7235 me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); 7236 7237 helpers.each(meta.data, function(arc, index) { 7238 me.updateElement(arc, index, reset); 7239 }); 7240 }, 7241 7242 updateElement: function(arc, index, reset) { 7243 var me = this; 7244 var chart = me.chart; 7245 var chartArea = chart.chartArea; 7246 var opts = chart.options; 7247 var animationOpts = opts.animation; 7248 var centerX = (chartArea.left + chartArea.right) / 2; 7249 var centerY = (chartArea.top + chartArea.bottom) / 2; 7250 var startAngle = opts.rotation; // non reset case handled later 7251 var endAngle = opts.rotation; // non reset case handled later 7252 var dataset = me.getDataset(); 7253 var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); 7254 var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; 7255 var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; 7256 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7257 7258 helpers.extend(arc, { 7259 // Utility 7260 _datasetIndex: me.index, 7261 _index: index, 7262 7263 // Desired view properties 7264 _model: { 7265 x: centerX + chart.offsetX, 7266 y: centerY + chart.offsetY, 7267 startAngle: startAngle, 7268 endAngle: endAngle, 7269 circumference: circumference, 7270 outerRadius: outerRadius, 7271 innerRadius: innerRadius, 7272 label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) 7273 } 7274 }); 7275 7276 var model = arc._model; 7277 7278 // Resets the visual styles 7279 var custom = arc.custom || {}; 7280 var valueOrDefault = helpers.valueAtIndexOrDefault; 7281 var elementOpts = this.chart.options.elements.arc; 7282 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); 7283 model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); 7284 model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); 7285 7286 // Set correct angles if not resetting 7287 if (!reset || !animationOpts.animateRotate) { 7288 if (index === 0) { 7289 model.startAngle = opts.rotation; 7290 } else { 7291 model.startAngle = me.getMeta().data[index - 1]._model.endAngle; 7292 } 7293 7294 model.endAngle = model.startAngle + model.circumference; 7295 } 7296 7297 arc.pivot(); 7298 }, 7299 7300 calculateTotal: function() { 7301 var dataset = this.getDataset(); 7302 var meta = this.getMeta(); 7303 var total = 0; 7304 var value; 7305 7306 helpers.each(meta.data, function(element, index) { 7307 value = dataset.data[index]; 7308 if (!isNaN(value) && !element.hidden) { 7309 total += Math.abs(value); 7310 } 7311 }); 7312 7313 /* if (total === 0) { 7314 total = NaN; 7315 }*/ 7316 7317 return total; 7318 }, 7319 7320 calculateCircumference: function(value) { 7321 var total = this.getMeta().total; 7322 if (total > 0 && !isNaN(value)) { 7323 return (Math.PI * 2.0) * (Math.abs(value) / total); 7324 } 7325 return 0; 7326 }, 7327 7328 // gets the max border or hover width to properly scale pie charts 7329 getMaxBorderWidth: function(arcs) { 7330 var max = 0; 7331 var index = this.index; 7332 var length = arcs.length; 7333 var borderWidth; 7334 var hoverWidth; 7335 7336 for (var i = 0; i < length; i++) { 7337 borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; 7338 hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; 7339 7340 max = borderWidth > max ? borderWidth : max; 7341 max = hoverWidth > max ? hoverWidth : max; 7342 } 7343 return max; 7344 } 7345 }); 7346}; 7347 7348},{"26":26,"41":41,"46":46}],18:[function(require,module,exports){ 7349'use strict'; 7350 7351var defaults = require(26); 7352var elements = require(41); 7353var helpers = require(46); 7354 7355defaults._set('line', { 7356 showLines: true, 7357 spanGaps: false, 7358 7359 hover: { 7360 mode: 'label' 7361 }, 7362 7363 scales: { 7364 xAxes: [{ 7365 type: 'category', 7366 id: 'x-axis-0' 7367 }], 7368 yAxes: [{ 7369 type: 'linear', 7370 id: 'y-axis-0' 7371 }] 7372 } 7373}); 7374 7375module.exports = function(Chart) { 7376 7377 function lineEnabled(dataset, options) { 7378 return helpers.valueOrDefault(dataset.showLine, options.showLines); 7379 } 7380 7381 Chart.controllers.line = Chart.DatasetController.extend({ 7382 7383 datasetElementType: elements.Line, 7384 7385 dataElementType: elements.Point, 7386 7387 update: function(reset) { 7388 var me = this; 7389 var meta = me.getMeta(); 7390 var line = meta.dataset; 7391 var points = meta.data || []; 7392 var options = me.chart.options; 7393 var lineElementOptions = options.elements.line; 7394 var scale = me.getScaleForId(meta.yAxisID); 7395 var i, ilen, custom; 7396 var dataset = me.getDataset(); 7397 var showLine = lineEnabled(dataset, options); 7398 7399 // Update Line 7400 if (showLine) { 7401 custom = line.custom || {}; 7402 7403 // Compatibility: If the properties are defined with only the old name, use those values 7404 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { 7405 dataset.lineTension = dataset.tension; 7406 } 7407 7408 // Utility 7409 line._scale = scale; 7410 line._datasetIndex = me.index; 7411 // Data 7412 line._children = points; 7413 // Model 7414 line._model = { 7415 // Appearance 7416 // The default behavior of lines is to break at null values, according 7417 // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 7418 // This option gives lines the ability to span gaps 7419 spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, 7420 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), 7421 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), 7422 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), 7423 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), 7424 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), 7425 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), 7426 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), 7427 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
7428 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), 7429 steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), 7430 cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), 7431 }; 7432 7433 line.pivot(); 7434 } 7435 7436 // Update Points 7437 for (i = 0, ilen = points.length; i < ilen; ++i) { 7438 me.updateElement(points[i], i, reset); 7439 } 7440 7441 if (showLine && line._model.tension !== 0) { 7442 me.updateBezierControlPoints(); 7443 } 7444 7445 // Now pivot the point for animation 7446 for (i = 0, ilen = points.length; i < ilen; ++i) { 7447 points[i].pivot(); 7448 } 7449 }, 7450 7451 getPointBackgroundColor: function(point, index) { 7452 var backgroundColor = this.chart.options.elements.point.backgroundColor; 7453 var dataset = this.getDataset(); 7454 var custom = point.custom || {}; 7455 7456 if (custom.backgroundColor) { 7457 backgroundColor = custom.backgroundColor; 7458 }
7458 else if (dataset.pointBackgroundColor) { 7459 backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); 7460 } else if (dataset.backgroundColor) { 7461 backgroundColor = dataset.backgroundColor; 7462 } 7463 7464 return backgroundColor; 7465 }, 7466 7467 getPointBorderColor: function(point, index) { 7468 var borderColor = this.chart.options.elements.point.borderColor; 7469 var dataset = this.getDataset(); 7470 var custom = point.custom || {}; 7471 7472 if (custom.borderColor) { 7473 borderColor = custom.borderColor; 7474 } else if (dataset.pointBorderColor) { 7475 borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); 7476 } else if (dataset.borderColor) { 7477 borderColor = dataset.borderColor; 7478 } 7479 7480 return borderColor; 7481 }, 7482 7483 getPointBorderWidth: function(point, index) { 7484 var borderWidth = this.chart.options.elements.point.borderWidth; 7485 var dataset = this.getDataset(); 7486 var custom = point.custom || {}; 7487 7488 if (!isNaN(custom.borderWidth)) { 7489 borderWidth = custom.borderWidth; 7490 } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { 7491 borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); 7492 } else if (!isNaN(dataset.borderWidth)) { 7493 borderWidth = dataset.borderWidth; 7494 } 7495 7496 return borderWidth; 7497 }, 7498 7499 getPointRotation: function(point, index) { 7500 var pointRotation = this.chart.options.elements.point.rotation; 7501 var dataset = this.getDataset(); 7502 var custom = point.custom || {}; 7503 7504 if (!isNaN(custom.rotation)) { 7505 pointRotation = custom.rotation; 7506 } else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) { 7507 pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation); 7508 } 7509 return pointRotation; 7510 }, 7511 7512 updateElement: function(point, index, reset) { 7513 var me = this; 7514 var meta = me.getMeta(); 7515 var custom = point.custom || {}; 7516 var dataset = me.getDataset(); 7517 var datasetIndex = me.index; 7518 var value = dataset.data[index]; 7519 var yScale = me.getScaleForId(meta.yAxisID); 7520 var xScale = me.getScaleForId(meta.xAxisID); 7521 var pointOptions = me.chart.options.elements.point; 7522 var x, y; 7523 7524 // Compatibility: If the properties are defined with only the old name, use those values 7525 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 7526 dataset.pointRadius = dataset.radius; 7527 } 7528 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { 7529 dataset.pointHitRadius = dataset.hitRadius; 7530 } 7531 7532 x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); 7533 y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); 7534 7535 // Utility 7536 point._xScale = xScale; 7537 point._yScale = yScale; 7538 point._datasetIndex = datasetIndex; 7539 point._index = index; 7540 7541 // Desired view properties 7542 point._model = { 7543 x: x, 7544 y: y, 7545 skip: custom.skip || isNaN(x) || isNaN(y), 7546 // Appearance 7547 radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), 7548 pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), 7549 rotation: me.getPointRotation(point, index),
7550 backgroundColor: me.getPointBackgroundColor(point, index), 7551 borderColor: me.getPointBorderColor(point, index), 7552 borderWidth: me.getPointBorderWidth(point, index), 7553 tension: meta.dataset._model ? meta.dataset._model.tension : 0, 7554 steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, 7555 // Tooltip 7556 hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) 7557 }; 7558 }, 7559 7560 calculatePointY: function(value, index, datasetIndex) { 7561 var me = this; 7562 var chart = me.chart; 7563 var meta = me.getMeta(); 7564 var yScale = me.getScaleForId(meta.yAxisID); 7565 var sumPos = 0; 7566 var sumNeg = 0; 7567 var i, ds, dsMeta; 7568 7569 if (yScale.options.stacked) { 7570 for (i = 0; i < datasetIndex; i++) { 7571 ds = chart.data.datasets[i]; 7572 dsMeta = chart.getDatasetMeta(i); 7573 if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { 7574 var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); 7575 if (stackedRightValue < 0) { 7576 sumNeg += stackedRightValue || 0; 7577 } else { 7578 sumPos += stackedRightValue || 0; 7579 } 7580 } 7581 } 7582 7583 var rightValue = Number(yScale.getRightValue(value)); 7584 if (rightValue < 0) { 7585 return yScale.getPixelForValue(sumNeg + rightValue); 7586 } 7587 return yScale.getPixelForValue(sumPos + rightValue); 7588 } 7589 7590 return yScale.getPixelForValue(value); 7591 }, 7592 7593 updateBezierControlPoints: function() { 7594 var me = this; 7595 var meta = me.getMeta(); 7596 var area = me.chart.chartArea; 7597 var points = (meta.data || []); 7598 var i, ilen, point, model, controlPoints; 7599 7600 // Only consider points that are drawn in case the spanGaps option is used 7601 if (meta.dataset._model.spanGaps) { 7602 points = points.filter(function(pt) { 7603 return !pt._model.skip; 7604 }); 7605 } 7606 7607 function capControlPoint(pt, min, max) { 7608 return Math.max(Math.min(pt, max), min); 7609 } 7610 7611 if (meta.dataset._model.cubicInterpolationMode === 'monotone') { 7612 helpers.splineCurveMonotone(points); 7613 } else { 7614 for (i = 0, ilen = points.length; i < ilen; ++i) { 7615 point = points[i]; 7616 model = point._model; 7617 controlPoints = helpers.splineCurve( 7618 helpers.previousItem(points, i)._model, 7619 model, 7620 helpers.nextItem(points, i)._model, 7621 meta.dataset._model.tension 7622 ); 7623 model.controlPointPreviousX = controlPoints.previous.x; 7624 model.controlPointPreviousY = controlPoints.previous.y; 7625 model.controlPointNextX = controlPoints.next.x; 7626 model.controlPointNextY = controlPoints.next.y; 7627 } 7628 } 7629 7630 if (me.chart.options.elements.line.capBezierPoints) { 7631 for (i = 0, ilen = points.length; i < ilen; ++i) { 7632 model = points[i]._model; 7633 model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); 7634 model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); 7635 model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); 7636 model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); 7637 } 7638 } 7639 }, 7640 7641 draw: function() { 7642 var me = this; 7643 var chart = me.chart; 7644 var meta = me.getMeta(); 7645 var points = meta.data || []; 7646 var area = chart.chartArea; 7647 var ilen = points.length; 7648 var halfBorderWidth; 7649 var i = 0; 7650 7651 if (lineEnabled(me.getDataset(), chart.options)) { 7652 halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2; 7653 7654 helpers.canvas.clipArea(chart.ctx, { 7655 left: area.left, 7656 right: area.right, 7657 top: area.top - halfBorderWidth, 7658 bottom: area.bottom + halfBorderWidth 7659 }); 7660 7661 meta.dataset.draw(); 7662 7663 helpers.canvas.unclipArea(chart.ctx); 7664 } 7665 7666 // Draw the points 7667 for (; i < ilen; ++i) { 7668 points[i].draw(area); 7669 } 7670 }, 7671 7672 setHoverStyle: function(element) { 7673 // Point 7674 var dataset = this.chart.data.datasets[element._datasetIndex]; 7675 var index = element._index; 7676 var custom = element.custom || {}; 7677 var model = element._model; 7678 7679 element.$previousStyle = { 7680 backgroundColor: model.backgroundColor, 7681 borderColor: model.borderColor, 7682 borderWidth: model.borderWidth, 7683 radius: model.radius 7684 }; 7685
7686 model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 7687 model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 7688 model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); 7689 model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); 7690 }, 7691 }); 7692}; 7693 7694},{"26":26,"41":41,"46":46}],19:[function(require,module,exports){ 7695'use strict'; 7696 7697var defaults = require(26); 7698var elements = require(41); 7699var helpers = require(46); 7700 7701defaults._set('polarArea', { 7702 scale: { 7703 type: 'radialLinear', 7704 angleLines: { 7705 display: false 7706 }, 7707 gridLines: { 7708 circular: true 7709 }, 7710 pointLabels: { 7711 display: false 7712 }, 7713 ticks: { 7714 beginAtZero: true 7715 } 7716 }, 7717 7718 // Boolean - Whether to animate the rotation of the chart 7719 animation: { 7720 animateRotate: true, 7721 animateScale: true 7722 }, 7723 7724 startAngle: -0.5 * Math.PI, 7725 legendCallback: function(chart) { 7726 var text = []; 7727 text.push('<ul class="' + chart.id + '-legend">'); 7728 7729 var data = chart.data; 7730 var datasets = data.datasets; 7731 var labels = data.labels; 7732 7733 if (datasets.length) { 7734 for (var i = 0; i < datasets[0].data.length; ++i) { 7735 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); 7736 if (labels[i]) { 7737 text.push(labels[i]); 7738 } 7739 text.push('</li>'); 7740 } 7741 } 7742 7743 text.push('</ul>'); 7744 return text.join(''); 7745 }, 7746 legend: { 7747 labels: { 7748 generateLabels: function(chart) { 7749 var data = chart.data; 7750 if (data.labels.length && data.datasets.length) { 7751 return data.labels.map(function(label, i) { 7752 var meta = chart.getDatasetMeta(0); 7753 var ds = data.datasets[0]; 7754 var arc = meta.data[i]; 7755 var custom = arc.custom || {}; 7756 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7757 var arcOpts = chart.options.elements.arc; 7758 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); 7759 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); 7760 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); 7761 7762 return { 7763 text: label, 7764 fillStyle: fill, 7765 strokeStyle: stroke, 7766 lineWidth: bw, 7767 hidden: isNaN(ds.data[i]) || meta.data[i].hidden, 7768 7769 // Extra data used for toggling the correct item 7770 index: i 7771 }; 7772 }); 7773 } 7774 return []; 7775 } 7776 }, 7777 7778 onClick: function(e, legendItem) { 7779 var index = legendItem.index; 7780 var chart = this.chart; 7781 var i, ilen, meta; 7782 7783 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 7784 meta = chart.getDatasetMeta(i); 7785 meta.data[index].hidden = !meta.data[index].hidden; 7786 } 7787 7788 chart.update(); 7789 } 7790 }, 7791 7792 // Need to override these to give a nice default 7793 tooltips: { 7794 callbacks: { 7795 title: function() { 7796 return ''; 7797 }, 7798 label: function(item, data) { 7799 return data.labels[item.index] + ': ' + item.yLabel; 7800 } 7801 } 7802 } 7803}); 7804 7805module.exports = function(Chart) { 7806 7807 Chart.controllers.polarArea = Chart.DatasetController.extend({ 7808 7809 dataElementType: elements.Arc, 7810 7811 linkScales: helpers.noop, 7812 7813 update: function(reset) { 7814 var me = this; 7815 var dataset = me.getDataset(); 7816 var meta = me.getMeta(); 7817 var start = me.chart.options.startAngle || 0; 7818 var starts = me._starts = []; 7819 var angles = me._angles = []; 7820 var i, ilen, angle; 7821 7822 me._updateRadius(); 7823 7824 meta.count = me.countVisibleElements(); 7825 7826 for (i = 0, ilen = dataset.data.length; i < ilen; i++) { 7827 starts[i] = start; 7828 angle = me._computeAngle(i); 7829 angles[i] = angle; 7830 start += angle; 7831 } 7832 7833 helpers.each(meta.data, function(arc, index) { 7834 me.updateElement(arc, index, reset); 7835 }); 7836 }, 7837 7838 /** 7839 * @private 7840 */ 7841 _updateRadius: function() { 7842 var me = this; 7843 var chart = me.chart; 7844 var chartArea = chart.chartArea; 7845 var opts = chart.options;
7846 var arcOpts = opts.elements.arc; 7847 var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); 7848 7849 chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); 7850 chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); 7851 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); 7852 7853 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); 7854 me.innerRadius = me.outerRadius - chart.radiusLength; 7855 }, 7856 7857 updateElement: function(arc, index, reset) { 7858 var me = this; 7859 var chart = me.chart; 7860 var dataset = me.getDataset(); 7861 var opts = chart.options; 7862 var animationOpts = opts.animation; 7863 var scale = chart.scale; 7864 var labels = chart.data.labels; 7865 7866 var centerX = scale.xCenter; 7867 var centerY = scale.yCenter; 7868 7869 // var negHalfPI = -0.5 * Math.PI; 7870 var datasetStartAngle = opts.startAngle; 7871 var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); 7872 var startAngle = me._starts[index]; 7873 var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]); 7874 7875 var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); 7876 7877 helpers.extend(arc, { 7878 // Utility 7879 _datasetIndex: me.index, 7880 _index: index, 7881 _scale: scale, 7882 7883 // Desired view properties 7884 _model: { 7885 x: centerX, 7886 y: centerY, 7887 innerRadius: 0, 7888 outerRadius: reset ? resetRadius : distance, 7889 startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, 7890 endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, 7891 label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) 7892 } 7893 }); 7894 7895 // Apply border and fill style 7896 var elementOpts = this.chart.options.elements.arc; 7897 var custom = arc.custom || {}; 7898 var valueOrDefault = helpers.valueAtIndexOrDefault; 7899 var model = arc._model; 7900 7901 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); 7902 model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); 7903 model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); 7904 7905 arc.pivot(); 7906 }, 7907 7908 countVisibleElements: function() { 7909 var dataset = this.getDataset(); 7910 var meta = this.getMeta(); 7911 var count = 0; 7912 7913 helpers.each(meta.data, function(element, index) { 7914 if (!isNaN(dataset.data[index]) && !element.hidden) { 7915 count++; 7916 } 7917 }); 7918 7919 return count; 7920 }, 7921 7922 /** 7923 * @private 7924 */ 7925 _computeAngle: function(index) { 7926 var me = this; 7927 var count = this.getMeta().count; 7928 var dataset = me.getDataset(); 7929 var meta = me.getMeta(); 7930 7931 if (isNaN(dataset.data[index]) || meta.data[index].hidden) { 7932 return 0; 7933 } 7934 7935 // Scriptable options 7936 var context = { 7937 chart: me.chart, 7938 dataIndex: index, 7939 dataset: dataset, 7940 datasetIndex: me.index 7941 }; 7942 7943 return helpers.options.resolve([ 7944 me.chart.options.elements.arc.angle, 7945 (2 * Math.PI) / count 7946 ], context, index); 7947 } 7948 }); 7949}; 7950 7951},{"26":26,"41":41,"46":46}],20:[function(require,module,exports){ 7952'use strict'; 7953 7954var defaults = require(26); 7955var elements = require(41); 7956var helpers = require(46); 7957 7958defaults._set('radar', { 7959 scale: { 7960 type: 'radialLinear' 7961 }, 7962 elements: { 7963 line: { 7964 tension: 0 // no bezier in radar 7965 } 7966 } 7967}); 7968 7969module.exports = function(Chart) { 7970 7971 Chart.controllers.radar = Chart.DatasetController.extend({ 7972 7973 datasetElementType: elements.Line, 7974 7975 dataElementType: elements.Point, 7976 7977 linkScales: helpers.noop, 7978 7979 update: function(reset) { 7980 var me = this; 7981 var meta = me.getMeta(); 7982 var line = meta.dataset; 7983 var points = meta.data; 7984 var custom = line.custom || {}; 7985 var dataset = me.getDataset(); 7986 var lineElementOptions = me.chart.options.elements.line; 7987 var scale = me.chart.scale; 7988 7989 // Compatibility: If the properties are defined with only the old name, use those values 7990 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { 7991 dataset.lineTension = dataset.tension; 7992 } 7993 7994 helpers.extend(meta.dataset, { 7995 // Utility 7996 _datasetIndex: me.index, 7997 _scale: scale, 7998 // Data 7999 _children: points, 8000 _loop: true, 8001 // Model 8002 _model: { 8003 // Appearance 8004 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), 8005 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), 8006 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), 8007 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
8008 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), 8009 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), 8010 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), 8011 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), 8012 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), 8013 } 8014 }); 8015 8016 meta.dataset.pivot(); 8017 8018 // Update Points 8019 helpers.each(points, function(point, index) { 8020 me.updateElement(point, index, reset); 8021 }, me); 8022 8023 // Update bezier control points 8024 me.updateBezierControlPoints(); 8025 }, 8026 updateElement: function(point, index, reset) { 8027 var me = this; 8028 var custom = point.custom || {}; 8029 var dataset = me.getDataset(); 8030 var scale = me.chart.scale; 8031 var pointElementOptions = me.chart.options.elements.point; 8032 var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); 8033 8034 // Compatibility: If the properties are defined with only the old name, use those values 8035 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 8036 dataset.pointRadius = dataset.radius; 8037 } 8038 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { 8039 dataset.pointHitRadius = dataset.hitRadius; 8040 } 8041 8042 helpers.extend(point, { 8043 // Utility 8044 _datasetIndex: me.index, 8045 _index: index, 8046 _scale: scale, 8047 8048 // Desired view properties 8049 _model: { 8050 x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales 8051 y: reset ? scale.yCenter : pointPosition.y, 8052 8053 // Appearance 8054 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), 8055 radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), 8056 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
8056dColor, index, pointElementOptions.backgroundColor), 8057 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), 8058 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), 8059 pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), 8060 rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation), 8061 8062 // Tooltip 8063 hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) 8064 } 8065 }); 8066 8067 point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); 8068 }, 8069 updateBezierControlPoints: function() { 8070 var chartArea = this.chart.chartArea; 8071 var meta = this.getMeta(); 8072 8073 helpers.each(meta.data, function(point, index) { 8074 var model = point._model; 8075 var controlPoints = helpers.splineCurve( 8076 helpers.previousItem(meta.data, index, true)._model, 8077 model, 8078 helpers.nextItem(meta.data, index, true)._model, 8079 model.tension 8080 ); 8081 8082 // Prevent the bezier going outside of the bounds of the graph 8083 model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); 8084 model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); 8085 8086 model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); 8087 model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); 8088 8089 // Now pivot the point for animation 8090 point.pivot(); 8091 }); 8092 }, 8093 8094 setHoverStyle: function(point) { 8095 // Point 8096 var dataset = this.chart.data.datasets[point._datasetIndex]; 8097 var custom = point.custom || {}; 8098 var index = point._index; 8099 var model = point._model; 8100 8101 point.$previousStyle = { 8102 backgroundColor: model.backgroundColor, 8103 borderColor: model.borderColor, 8104 borderWidth: model.borderWidth, 8105 radius: model.radius 8106 }; 8107 8108 model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); 8109 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 8110 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 8111 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); 8112 }, 8113 }); 8114}; 8115 8116},{"26":26,"41":41,"46":46}],21:[function(require,module,exports){ 8117'use strict'; 8118 8119var defaults = require(26); 8120 8121defaults._set('scatter', { 8122 hover: { 8123 mode: 'single' 8124 }, 8125 8126 scales: { 8127 xAxes: [{ 8128 id: 'x-axis-1', // need an ID so datasets can reference the scale 8129 type: 'linear', // scatter should not use a category axis 8130 position: 'bottom' 8131 }], 8132 yAxes: [{ 8133 id: 'y-axis-1', 8134 type: 'linear', 8135 position: 'left' 8136 }] 8137 }, 8138 8139 showLines: false, 8140 8141 tooltips: { 8142 callbacks: { 8143 title: function() { 8144 return ''; // doesn't make sense for scatter since data are formatted as a point 8145 }, 8146 label: function(item) { 8147 return '(' + item.xLabel + ', ' + item.yLabel + ')'; 8148 } 8149 } 8150 } 8151}); 8152 8153module.exports = function(Chart) { 8154 8155 // Scatter charts use line controllers 8156 Chart.controllers.scatter = Chart.controllers.line; 8157 8158}; 8159 8160},{"26":26}],22:[function(require,module,exports){ 8161'use strict'; 8162 8163var Element = require(27); 8164 8165var exports = module.exports = Element.extend({ 8166 chart: null, // the animation associated chart instance 8167 currentStep: 0, // the current animation step 8168 numSteps: 60, // default number of steps 8169 easing: '', // the easing to use for this animation 8170 render: null, // render function used by the animation service 8171 8172 onAnimationProgress: null, // user specified callback to fire on each step of the animation 8173 onAnimationComplete: null, // user specified callback to fire when the animation finishes 8174}); 8175 8176// DEPRECATIONS 8177 8178/** 8179 * Provided for backward compatibility, use Chart.Animation instead 8180 * @prop Chart.Animation#animationObject 8181 * @deprecated since version 2.6.0 8182 * @todo remove at version 3 8183 */ 8184Object.defineProperty(exports.prototype, 'animationObject', { 8185 get: function() { 8186 return this; 8187 } 8188}); 8189 8190/** 8191 * Provided for backward compatibility, use Chart.Animation#chart instead 8192 * @prop Chart.Animation#chartInstance 8193 * @deprecated since version 2.6.0 8194 * @todo remove at version 3 8195 */ 8196Object.defineProperty(exports.prototype, 'chartInstance', { 8197 get: function() { 8198 return this.chart; 8199 }, 8200 set: function(value) { 8201 this.chart = value; 8202 } 8203}); 8204 8205},{"27":27}],23:[function(require,module,exports){ 8206/* global window: false */ 8207'use strict'; 8208 8209var defaults = require(26); 8210var helpers = require(46); 8211 8212defaults._set('global', { 8213 animation: { 8214 duration: 1000, 8215 easing: 'easeOutQuart', 8216 onProgress: helpers.noop, 8217 onComplete: helpers.noop 8218 } 8219}); 8220 8221module.exports = { 8222 frameDuration: 17, 8223 animations: [], 8224 dropFrames: 0, 8225 request: null, 8226 8227 /** 8228 * @param {Chart} chart - The chart to animate. 8229 * @param {Chart.Animation} animation - The animation that we will animate. 8230 * @param {Number} duration - The animation duration in ms. 8231 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions 8232 */
8233 addAnimation: function(chart, animation, duration, lazy) { 8234 var animations = this.animations; 8235 var i, ilen; 8236 8237 animation.chart = chart; 8238 8239 if (!lazy) { 8240 chart.animating = true; 8241 } 8242 8243 for (i = 0, ilen = animations.length; i < ilen; ++i) { 8244 if (animations[i].chart === chart) { 8245 animations[i] = animation; 8246 return; 8247 } 8248 } 8249 8250 animations.push(animation); 8251 8252 // If there are no animations queued, manually kickstart a digest, for lack of a better word 8253 if (animations.length === 1) { 8254 this.requestAnimationFrame(); 8255 } 8256 }, 8257 8258 cancelAnimation: function(chart) { 8259 var index = helpers.findIndex(this.animations, function(animation) { 8260 return animation.chart === chart; 8261 }); 8262 8263 if (index !== -1) { 8264 this.animations.splice(index, 1); 8265 chart.animating = false; 8266 } 8267 }, 8268 8269 requestAnimationFrame: function() { 8270 var me = this; 8271 if (me.request === null) { 8272 // Skip animation frame requests until the active one is executed. 8273 // This can happen when processing mouse events, e.g. 'mousemove' 8274 // and 'mouseout' events will trigger multiple renders. 8275 me.request = helpers.requestAnimFrame.call(window, function() { 8276 me.request = null; 8277 me.startDigest(); 8278 }); 8279 } 8280 }, 8281 8282 /** 8283 * @private 8284 */ 8285 startDigest: function() { 8286 var me = this; 8287 var startTime = Date.now(); 8288 var framesToDrop = 0; 8289 8290 if (me.dropFrames > 1) { 8291 framesToDrop = Math.floor(me.dropFrames); 8292 me.dropFrames = me.dropFrames % 1; 8293 } 8294 8295 me.advance(1 + framesToDrop); 8296 8297 var endTime = Date.now(); 8298 8299 me.dropFrames += (endTime - startTime) / me.frameDuration; 8300 8301 // Do we have more stuff to animate? 8302 if (me.animations.length > 0) { 8303 me.requestAnimationFrame(); 8304 } 8305 }, 8306 8307 /** 8308 * @private 8309 */ 8310 advance: function(count) { 8311 var animations = this.animations; 8312 var animation, chart; 8313 var i = 0; 8314 8315 while (i < animations.length) { 8316 animation = animations[i]; 8317 chart = animation.chart; 8318 8319 animation.currentStep = (animation.currentStep || 0) + count; 8320 animation.currentStep = Math.min(animation.currentStep, animation.numSteps); 8321 8322 helpers.callback(animation.render, [chart, animation], chart); 8323 helpers.callback(animation.onAnimationProgress, [animation], chart); 8324 8325 if (animation.currentStep >= animation.numSteps) { 8326 helpers.callback(animation.onAnimationComplete, [animation], chart); 8327 chart.animating = false; 8328 animations.splice(i, 1); 8329 } else { 8330 ++i; 8331 } 8332 } 8333 } 8334}; 8335 8336},{"26":26,"46":46}],24:[function(require,module,exports){ 8337'use strict'; 8338 8339var Animation = require(22); 8340var animations = require(23); 8341var defaults = require(26); 8342var helpers = require(46); 8343var Interaction = require(29); 8344var layouts = require(31); 8345var platform = require(49); 8346var plugins = require(32); 8347var scaleService = require(34); 8348var Tooltip = require(36); 8349 8350module.exports = function(Chart) { 8351 8352 // Create a dictionary of chart types, to allow for extension of existing types 8353 Chart.types = {}; 8354 8355 // Store a reference to each instance - allowing us to globally resize chart instances on window resize. 8356 // Destroy method on the chart will remove the instance of the chart from this reference. 8357 Chart.instances = {}; 8358 8359 // Controllers available for dataset visualization eg. bar, line, slice, etc. 8360 Chart.controllers = {}; 8361 8362 /** 8363 * Initializes the given config with global and chart default values. 8364 */ 8365 function initConfig(config) { 8366 config = config || {}; 8367 8368 // Do NOT use configMerge() for the data object because this method merges arrays 8369 // and so would change references to labels and datasets, preventing data updates. 8370 var data = config.data = config.data || {}; 8371 data.datasets = data.datasets || []; 8372 data.labels = data.labels || []; 8373 8374 config.options = helpers.configMerge( 8375 defaults.global, 8376 defaults[config.type], 8377 config.options || {}); 8378 8379 return config; 8380 } 8381 8382 /** 8383 * Updates the config of the chart 8384 * @param chart {Chart} chart to update the options for 8385 */ 8386 function updateConfig(chart) { 8387 var newOptions = chart.options; 8388 8389 helpers.each(chart.scales, function(scale) { 8390 layouts.removeBox(chart, scale); 8391 }); 8392 8393 newOptions = helpers.configMerge( 8394 Chart.defaults.global, 8395 Chart.defaults[chart.config.type], 8396 newOptions); 8397 8398 chart.options = chart.config.options = newOptions;
vendor: 11,649 bytes, lines 8399-8833
8399 chart.ensureScalesHaveIDs(); 8400 chart.buildOrUpdateScales(); 8401 // Tooltip 8402 chart.tooltip._options = newOptions.tooltips; 8403 chart.tooltip.initialize(); 8404 } 8405 8406 function positionIsHorizontal(position) { 8407 return position === 'top' || position === 'bottom'; 8408 } 8409 8410 helpers.extend(Chart.prototype, /** @lends Chart */ { 8411 /** 8412 * @private 8413 */ 8414 construct: function(item, config) { 8415 var me = this; 8416 8417 config = initConfig(config); 8418 8419 var context = platform.acquireContext(item, config); 8420 var canvas = context && context.canvas; 8421 var height = canvas && canvas.height; 8422 var width = canvas && canvas.width; 8423 8424 me.id = helpers.uid(); 8425 me.ctx = context; 8426 me.canvas = canvas; 8427 me.config = config; 8428 me.width = width; 8429 me.height = height; 8430 me.aspectRatio = height ? width / height : null; 8431 me.options = config.options; 8432 me._bufferedRender = false; 8433 8434 /** 8435 * Provided for backward compatibility, Chart and Chart.Controller have been merged, 8436 * the "instance" still need to be defined since it might be called from plugins. 8437 * @prop Chart#chart 8438 * @deprecated since version 2.6.0 8439 * @todo remove at version 3 8440 * @private 8441 */ 8442 me.chart = me; 8443 me.controller = me; // chart.chart.controller #inception 8444 8445 // Add the chart instance to the global namespace 8446 Chart.instances[me.id] = me; 8447 8448 // Define alias to the config data: `chart.data === chart.config.data` 8449 Object.defineProperty(me, 'data', { 8450 get: function() { 8451 return me.config.data; 8452 }, 8453 set: function(value) { 8454 me.config.data = value; 8455 } 8456 }); 8457 8458 if (!context || !canvas) { 8459 // The given item is not a compatible context2d element, let's return before finalizing 8460 // the chart initialization but after setting basic chart / controller properties that 8461 // can help to figure out that the chart is not valid (e.g chart.canvas !== null); 8462 // https://github.com/chartjs/Chart.js/issues/2807 8463 console.error("Failed to create chart: can't acquire context from the given item"); 8464 return; 8465 } 8466 8467 me.initialize(); 8468 me.update(); 8469 }, 8470 8471 /** 8472 * @private 8473 */ 8474 initialize: function() { 8475 var me = this; 8476 8477 // Before init plugin notification 8478 plugins.notify(me, 'beforeInit'); 8479 8480 helpers.retinaScale(me, me.options.devicePixelRatio); 8481 8482 me.bindEvents(); 8483 8484 if (me.options.responsive) { 8485 // Initial resize before chart draws (must be silent to preserve initial animations). 8486 me.resize(true); 8487 } 8488 8489 // Make sure scales have IDs and are built before we build any controllers. 8490 me.ensureScalesHaveIDs(); 8491 me.buildOrUpdateScales(); 8492 me.initToolTip(); 8493 8494 // After init plugin notification 8495 plugins.notify(me, 'afterInit'); 8496 8497 return me; 8498 }, 8499 8500 clear: function() { 8501 helpers.canvas.clear(this); 8502 return this; 8503 }, 8504 8505 stop: function() { 8506 // Stops any current animation loop occurring 8507 animations.cancelAnimation(this); 8508 return this; 8509 }, 8510 8511 resize: function(silent) { 8512 var me = this; 8513 var options = me.options; 8514 var canvas = me.canvas; 8515 var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; 8516 8517 // the canvas render width and height will be casted to integers so make sure that 8518 // the canvas display style uses the same integer values to avoid blurring effect. 8519 8520 // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed 8521 var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); 8522 var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); 8523 8524 if (me.width === newWidth && me.height === newHeight) { 8525 return; 8526 } 8527 8528 canvas.width = me.width = newWidth; 8529 canvas.height = me.height = newHeight; 8530 canvas.style.width = newWidth + 'px'; 8531 canvas.style.height = newHeight + 'px'; 8532 8533 helpers.retinaScale(me, options.devicePixelRatio); 8534 8535 if (!silent) { 8536 // Notify any plugins about the resize 8537 var newSize = {width: newWidth, height: newHeight}; 8538 plugins.notify(me, 'resize', [newSize]); 8539 8540 // Notify of resize 8541 if (me.options.onResize) { 8542 me.options.onResize(me, newSize); 8543 } 8544 8545 me.stop(); 8546 me.update({ 8547 duration: me.options.responsiveAnimationDuration 8548 }); 8549 } 8550 }, 8551 8552 ensureScalesHaveIDs: function() { 8553 var options = this.options; 8554 var scalesOptions = options.scales || {}; 8555 var scaleOptions = options.scale; 8556 8557 helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { 8558 xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); 8559 }); 8560 8561 helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { 8562 yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); 8563 }); 8564 8565 if (scaleOptions) { 8566 scaleOptions.id = scaleOptions.id || 'scale'; 8567 } 8568 }, 8569 8570 /** 8571 * Builds a map of scale ID to scale object for future lookup. 8572 */ 8573 buildOrUpdateScales: function() { 8574 var me = this; 8575 var options = me.options; 8576 var scales = me.scales || {}; 8577 var items = []; 8578 var updated = Object.keys(scales).reduce(function(obj, id) { 8579 obj[id] = false; 8580 return obj; 8581 }, {}); 8582 8583 if (options.scales) { 8584 items = items.concat( 8585 (options.scales.xAxes || []).map(function(xAxisOptions) { 8586 return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; 8587 }), 8588 (options.scales.yAxes || []).map(function(yAxisOptions) { 8589 return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; 8590 }) 8591 ); 8592 } 8593 8594 if (options.scale) { 8595 items.push({ 8596 options: options.scale, 8597 dtype: 'radialLinear', 8598 isDefault: true, 8599 dposition: 'chartArea' 8600 }); 8601 } 8602 8603 helpers.each(items, function(item) { 8604 var scaleOptions = item.options; 8605 var id = scaleOptions.id; 8606 var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); 8607 8608 if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { 8609 scaleOptions.position = item.dposition; 8610 } 8611 8612 updated[id] = true; 8613 var scale = null; 8614 if (id in scales && scales[id].type === scaleType) { 8615 scale = scales[id]; 8616 scale.options = scaleOptions; 8617 scale.ctx = me.ctx; 8618 scale.chart = me; 8619 } else { 8620 var scaleClass = scaleService.getScaleConstructor(scaleType); 8621 if (!scaleClass) { 8622 return; 8623 } 8624 scale = new scaleClass({ 8625 id: id, 8626 type: scaleType, 8627 options: scaleOptions, 8628 ctx: me.ctx, 8629 chart: me 8630 }); 8631 scales[scale.id] = scale; 8632 } 8633 8634 scale.mergeTicksOptions(); 8635 8636 // TODO(SB): I think we should be able to remove this custom case (options.scale) 8637 // and consider it as a regular scale part of the "scales"" map only! This would 8638 // make the logic easier and remove some useless? custom code. 8639 if (item.isDefault) { 8640 me.scale = scale; 8641 } 8642 }); 8643 // clear up discarded scales 8644 helpers.each(updated, function(hasUpdated, id) { 8645 if (!hasUpdated) { 8646 delete scales[id]; 8647 } 8648 }); 8649 8650 me.scales = scales; 8651 8652 scaleService.addScalesToLayout(this); 8653 }, 8654 8655 buildOrUpdateControllers: function() { 8656 var me = this; 8657 var types = []; 8658 var newControllers = []; 8659 8660 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8661 var meta = me.getDatasetMeta(datasetIndex); 8662 var type = dataset.type || me.config.type; 8663 8664 if (meta.type && meta.type !== type) { 8665 me.destroyDatasetMeta(datasetIndex); 8666 meta = me.getDatasetMeta(datasetIndex); 8667 } 8668 meta.type = type; 8669 8670 types.push(meta.type); 8671 8672 if (meta.controller) { 8673 meta.controller.updateIndex(datasetIndex); 8674 meta.controller.linkScales(); 8675 } else { 8676 var ControllerClass = Chart.controllers[meta.type]; 8677 if (ControllerClass === undefined) { 8678 throw new Error('"' + meta.type + '" is not a chart type.'); 8679 } 8680 8681 meta.controller = new ControllerClass(me, datasetIndex); 8682 newControllers.push(meta.controller); 8683 } 8684 }, me); 8685 8686 return newControllers; 8687 }, 8688 8689 /** 8690 * Reset the elements of all datasets 8691 * @private 8692 */ 8693 resetElements: function() { 8694 var me = this; 8695 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8696 me.getDatasetMeta(datasetIndex).controller.reset(); 8697 }, me); 8698 }, 8699 8700 /** 8701 * Resets the chart back to it's state before the initial animation 8702 */ 8703 reset: function() { 8704 this.resetElements(); 8705 this.tooltip.initialize(); 8706 }, 8707 8708 update: function(config) { 8709 var me = this; 8710 8711 if (!config || typeof config !== 'object') { 8712 // backwards compatibility 8713 config = { 8714 duration: config, 8715 lazy: arguments[1] 8716 }; 8717 } 8718 8719 updateConfig(me); 8720 8721 // plugins options references might have change, let's invalidate the cache 8722 // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 8723 plugins._invalidate(me); 8724 8725 if (plugins.notify(me, 'beforeUpdate') === false) { 8726 return; 8727 } 8728 8729 // In case the entire data object changed 8730 me.tooltip._data = me.data; 8731 8732 // Make sure dataset controllers are updated and new controllers are reset 8733 var newControllers = me.buildOrUpdateControllers(); 8734 8735 // Make sure all dataset controllers have correct meta data counts 8736 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8737 me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); 8738 }, me); 8739 8740 me.updateLayout(); 8741 8742 // Can only reset the new controllers after the scales have been updated 8743 if (me.options.animation && me.options.animation.duration) { 8744 helpers.each(newControllers, function(controller) { 8745 controller.reset(); 8746 }); 8747 } 8748 8749 me.updateDatasets(); 8750 8751 // Need to reset tooltip in case it is displayed with elements that are removed 8752 // after update. 8753 me.tooltip.initialize(); 8754 8755 // Last active contains items that were previously in the tooltip. 8756 // When we reset the tooltip, we need to clear it 8757 me.lastActive = []; 8758 8759 // Do this before render so that any plugins that need final scale updates can use it 8760 plugins.notify(me, 'afterUpdate'); 8761 8762 if (me._bufferedRender) { 8763 me._bufferedRequest = { 8764 duration: config.duration, 8765 easing: config.easing, 8766 lazy: config.lazy 8767 }; 8768 } else { 8769 me.render(config); 8770 } 8771 }, 8772 8773 /** 8774 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` 8775 * hook, in which case, plugins will not be called on `afterLayout`. 8776 * @private 8777 */ 8778 updateLayout: function() { 8779 var me = this; 8780 8781 if (plugins.notify(me, 'beforeLayout') === false) { 8782 return; 8783 } 8784 8785 layouts.update(this, this.width, this.height); 8786 8787 /** 8788 * Provided for backward compatibility, use `afterLayout` instead. 8789 * @method IPlugin#afterScaleUpdate 8790 * @deprecated since version 2.5.0 8791 * @todo remove at version 3 8792 * @private 8793 */ 8794 plugins.notify(me, 'afterScaleUpdate'); 8795 plugins.notify(me, 'afterLayout'); 8796 }, 8797 8798 /** 8799 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` 8800 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. 8801 * @private 8802 */ 8803 updateDatasets: function() { 8804 var me = this; 8805 8806 if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { 8807 return; 8808 } 8809 8810 for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { 8811 me.updateDataset(i); 8812 } 8813 8814 plugins.notify(me, 'afterDatasetsUpdate'); 8815 }, 8816 8817 /** 8818 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` 8819 * hook, in which case, plugins will not be called on `afterDatasetUpdate`. 8820 * @private 8821 */ 8822 updateDataset: function(index) { 8823 var me = this; 8824 var meta = me.getDatasetMeta(index); 8825 var args = { 8826 meta: meta, 8827 index: index 8828 }; 8829 8830 if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { 8831 return; 8832 } 8833
8834 meta.controller.update(); 8835 8836 plugins.notify(me, 'afterDatasetUpdate', [args]); 8837 }, 8838 8839 render: function(config) { 8840 var me = this; 8841 8842 if (!config || typeof config !== 'object') { 8843 // backwards compatibility 8844 config = { 8845 duration: config, 8846 lazy: arguments[1] 8847 }; 8848 } 8849 8850 var duration = config.duration; 8851 var lazy = config.lazy; 8852 8853 if (plugins.notify(me, 'beforeRender') === false) { 8854 return; 8855 } 8856 8857 var animationOptions = me.options.animation; 8858 var onComplete = function(animation) { 8859 plugins.notify(me, 'afterRender'); 8860 helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); 8861 }; 8862 8863 if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { 8864 var animation = new Animation({ 8865 numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps 8866 easing: config.easing || animationOptions.easing, 8867 8868 render: function(chart, animationObject) { 8869 var easingFunction = helpers.easing.effects[animationObject.easing]; 8870 var currentStep = animationObject.currentStep; 8871 var stepDecimal = currentStep / animationObject.numSteps; 8872 8873 chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); 8874 }, 8875 8876 onAnimationProgress: animationOptions.onProgress, 8877 onAnimationComplete: onComplete 8878 }); 8879 8880 animations.addAnimation(me, animation, duration, lazy); 8881 } else { 8882 me.draw(); 8883 8884 // See https://github.com/chartjs/Chart.js/issues/3781 8885 onComplete(new Animation({numSteps: 0, chart: me})); 8886 } 8887 8888 return me; 8889 }, 8890 8891 draw: function(easingValue) { 8892 var me = this; 8893 8894 me.clear(); 8895 8896 if (helpers.isNullOrUndef(easingValue)) { 8897 easingValue = 1; 8898 } 8899 8900 me.transition(easingValue); 8901 8902 if (me.width <= 0 || me.height <= 0) { 8903 return; 8904 } 8905 8906 if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { 8907 return; 8908 } 8909 8910 // Draw all the scales 8911 helpers.each(me.boxes, function(box) { 8912 box.draw(me.chartArea); 8913 }, me); 8914 8915 if (me.scale) { 8916 me.scale.draw(); 8917 } 8918 8919 me.drawDatasets(easingValue); 8920 me._drawTooltip(easingValue); 8921 8922 plugins.notify(me, 'afterDraw', [easingValue]); 8923 }, 8924 8925 /** 8926 * @private 8927 */ 8928 transition: function(easingValue) { 8929 var me = this; 8930 8931 for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { 8932 if (me.isDatasetVisible(i)) { 8933 me.getDatasetMeta(i).controller.transition(easingValue); 8934 } 8935 } 8936 8937 me.tooltip.transition(easingValue); 8938 }, 8939 8940 /** 8941 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` 8942 * hook, in which case, plugins will not be called on `afterDatasetsDraw`. 8943 * @private 8944 */ 8945 drawDatasets: function(easingValue) { 8946 var me = this; 8947 8948 if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { 8949 return; 8950 } 8951 8952 // Draw datasets reversed to support proper line stacking 8953 for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { 8954 if (me.isDatasetVisible(i)) { 8955 me.drawDataset(i, easingValue); 8956 } 8957 } 8958 8959 plugins.notify(me, 'afterDatasetsDraw', [easingValue]); 8960 }, 8961 8962 /** 8963 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` 8964 * hook, in which case, plugins will not be called on `afterDatasetDraw`. 8965 * @private 8966 */ 8967 drawDataset: function(index, easingValue) { 8968 var me = this; 8969 var meta = me.getDatasetMeta(index); 8970 var args = { 8971 meta: meta, 8972 index: index, 8973 easingValue: easingValue 8974 }; 8975 8976 if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { 8977 return; 8978 } 8979 8980 meta.controller.draw(easingValue); 8981 8982 plugins.notify(me, 'afterDatasetDraw', [args]); 8983 }, 8984 8985 /** 8986 * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` 8987 * hook, in which case, plugins will not be called on `afterTooltipDraw`. 8988 * @private 8989 */ 8990 _drawTooltip: function(easingValue) { 8991 var me = this; 8992 var tooltip = me.tooltip; 8993 var args = { 8994 tooltip: tooltip, 8995 easingValue: easingValue 8996 }; 8997 8998 if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { 8999 return; 9000 } 9001 9002 tooltip.draw(); 9003 9004 plugins.notify(me, 'afterTooltipDraw', [args]); 9005 }, 9006 9007 // Get the single element that was clicked on
vendor: 9,696 bytes, lines 9008-9394
9008 // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw 9009 getElementAtEvent: function(e) { 9010 return Interaction.modes.single(this, e); 9011 }, 9012 9013 getElementsAtEvent: function(e) { 9014 return Interaction.modes.label(this, e, {intersect: true}); 9015 }, 9016 9017 getElementsAtXAxis: function(e) { 9018 return Interaction.modes['x-axis'](this, e, {intersect: true}); 9019 }, 9020 9021 getElementsAtEventForMode: function(e, mode, options) { 9022 var method = Interaction.modes[mode]; 9023 if (typeof method === 'function') { 9024 return method(this, e, options); 9025 } 9026 9027 return []; 9028 }, 9029 9030 getDatasetAtEvent: function(e) { 9031 return Interaction.modes.dataset(this, e, {intersect: true}); 9032 }, 9033 9034 getDatasetMeta: function(datasetIndex) { 9035 var me = this; 9036 var dataset = me.data.datasets[datasetIndex]; 9037 if (!dataset._meta) { 9038 dataset._meta = {}; 9039 } 9040 9041 var meta = dataset._meta[me.id]; 9042 if (!meta) { 9043 meta = dataset._meta[me.id] = { 9044 type: null, 9045 data: [], 9046 dataset: null, 9047 controller: null, 9048 hidden: null, // See isDatasetVisible() comment 9049 xAxisID: null, 9050 yAxisID: null 9051 }; 9052 } 9053 9054 return meta; 9055 }, 9056 9057 getVisibleDatasetCount: function() { 9058 var count = 0; 9059 for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { 9060 if (this.isDatasetVisible(i)) { 9061 count++; 9062 } 9063 } 9064 return count; 9065 }, 9066 9067 isDatasetVisible: function(datasetIndex) { 9068 var meta = this.getDatasetMeta(datasetIndex); 9069 9070 // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, 9071 // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. 9072 return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; 9073 }, 9074 9075 generateLegend: function() { 9076 return this.options.legendCallback(this); 9077 }, 9078 9079 /** 9080 * @private 9081 */ 9082 destroyDatasetMeta: function(datasetIndex) { 9083 var id = this.id; 9084 var dataset = this.data.datasets[datasetIndex]; 9085 var meta = dataset._meta && dataset._meta[id]; 9086 9087 if (meta) { 9088 meta.controller.destroy(); 9089 delete dataset._meta[id]; 9090 } 9091 }, 9092 9093 destroy: function() { 9094 var me = this; 9095 var canvas = me.canvas; 9096 var i, ilen; 9097 9098 me.stop(); 9099 9100 // dataset controllers need to cleanup associated data 9101 for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { 9102 me.destroyDatasetMeta(i); 9103 } 9104 9105 if (canvas) { 9106 me.unbindEvents(); 9107 helpers.canvas.clear(me); 9108 platform.releaseContext(me.ctx); 9109 me.canvas = null; 9110 me.ctx = null; 9111 } 9112 9113 plugins.notify(me, 'destroy'); 9114 9115 delete Chart.instances[me.id]; 9116 }, 9117 9118 toBase64Image: function() { 9119 return this.canvas.toDataURL.apply(this.canvas, arguments); 9120 }, 9121 9122 initToolTip: function() { 9123 var me = this; 9124 me.tooltip = new Tooltip({ 9125 _chart: me, 9126 _chartInstance: me, // deprecated, backward compatibility 9127 _data: me.data, 9128 _options: me.options.tooltips 9129 }, me); 9130 }, 9131 9132 /** 9133 * @private 9134 */ 9135 bindEvents: function() { 9136 var me = this; 9137 var listeners = me._listeners = {}; 9138 var listener = function() { 9139 me.eventHandler.apply(me, arguments); 9140 }; 9141 9142 helpers.each(me.options.events, function(type) { 9143 platform.addEventListener(me, type, listener); 9144 listeners[type] = listener; 9145 }); 9146 9147 // Elements used to detect size change should not be injected for non responsive charts. 9148 // See https://github.com/chartjs/Chart.js/issues/2210 9149 if (me.options.responsive) { 9150 listener = function() { 9151 me.resize(); 9152 }; 9153 9154 platform.addEventListener(me, 'resize', listener); 9155 listeners.resize = listener; 9156 } 9157 }, 9158 9159 /** 9160 * @private 9161 */ 9162 unbindEvents: function() { 9163 var me = this; 9164 var listeners = me._listeners; 9165 if (!listeners) { 9166 return; 9167 } 9168 9169 delete me._listeners; 9170 helpers.each(listeners, function(listener, type) { 9171 platform.removeEventListener(me, type, listener); 9172 }); 9173 }, 9174 9175 updateHoverStyle: function(elements, mode, enabled) { 9176 var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; 9177 var element, i, ilen; 9178 9179 for (i = 0, ilen = elements.length; i < ilen; ++i) { 9180 element = elements[i]; 9181 if (element) { 9182 this.getDatasetMeta(element._datasetIndex).controller[method](element); 9183 } 9184 } 9185 }, 9186 9187 /** 9188 * @private 9189 */ 9190 eventHandler: function(e) { 9191 var me = this; 9192 var tooltip = me.tooltip; 9193 9194 if (plugins.notify(me, 'beforeEvent', [e]) === false) { 9195 return; 9196 } 9197 9198 // Buffer any update calls so that renders do not occur 9199 me._bufferedRender = true; 9200 me._bufferedRequest = null; 9201 9202 var changed = me.handleEvent(e); 9203 // for smooth tooltip animations issue #4989 9204 // the tooltip should be the source of change 9205 // Animation check workaround: 9206 // tooltip._start will be null when tooltip isn't animating 9207 if (tooltip) { 9208 changed = tooltip._start 9209 ? tooltip.handleEvent(e) 9210 : changed | tooltip.handleEvent(e); 9211 } 9212 9213 plugins.notify(me, 'afterEvent', [e]); 9214 9215 var bufferedRequest = me._bufferedRequest; 9216 if (bufferedRequest) { 9217 // If we have an update that was triggered, we need to do a normal render 9218 me.render(bufferedRequest); 9219 } else if (changed && !me.animating) { 9220 // If entering, leaving, or changing elements, animate the change via pivot 9221 me.stop(); 9222 9223 // We only need to render at this point. Updating will cause scales to be 9224 // recomputed generating flicker & using more memory than necessary. 9225 me.render({ 9226 duration: me.options.hover.animationDuration, 9227 lazy: true 9228 }); 9229 } 9230 9231 me._bufferedRender = false; 9232 me._bufferedRequest = null; 9233 9234 return me; 9235 }, 9236 9237 /** 9238 * Handle an event 9239 * @private 9240 * @param {IEvent} event the event to handle 9241 * @return {Boolean} true if the chart needs to re-render 9242 */ 9243 handleEvent: function(e) { 9244 var me = this; 9245 var options = me.options || {}; 9246 var hoverOptions = options.hover; 9247 var changed = false; 9248 9249 me.lastActive = me.lastActive || []; 9250 9251 // Find Active Elements for hover and tooltips 9252 if (e.type === 'mouseout') { 9253 me.active = []; 9254 } else { 9255 me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); 9256 } 9257 9258 // Invoke onHover hook 9259 // Need to call with native event here to not break backwards compatibility 9260 helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); 9261 9262 if (e.type === 'mouseup' || e.type === 'click') { 9263 if (options.onClick) { 9264 // Use e.native here for backwards compatibility 9265 options.onClick.call(me, e.native, me.active); 9266 } 9267 } 9268 9269 // Remove styling for last active (even if it may still be active) 9270 if (me.lastActive.length) { 9271 me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); 9272 } 9273 9274 // Built in hover styling 9275 if (me.active.length && hoverOptions.mode) { 9276 me.updateHoverStyle(me.active, hoverOptions.mode, true); 9277 } 9278 9279 changed = !helpers.arrayEquals(me.active, me.lastActive); 9280 9281 // Remember Last Actives 9282 me.lastActive = me.active; 9283 9284 return changed; 9285 } 9286 }); 9287 9288 /** 9289 * Provided for backward compatibility, use Chart instead. 9290 * @class Chart.Controller 9291 * @deprecated since version 2.6.0 9292 * @todo remove at version 3 9293 * @private 9294 */ 9295 Chart.Controller = Chart; 9296}; 9297 9298},{"22":22,"23":23,"26":26,"29":29,"31":31,"32":32,"34":34,"36":36,"46":46,"49":49}],25:[function(require,module,exports){ 9299'use strict'; 9300 9301var helpers = require(46); 9302 9303module.exports = function(Chart) { 9304 9305 var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; 9306 9307 /** 9308 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', 9309 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are 9310 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. 9311 */ 9312 function listenArrayEvents(array, listener) { 9313 if (array._chartjs) { 9314 array._chartjs.listeners.push(listener); 9315 return; 9316 } 9317 9318 Object.defineProperty(array, '_chartjs', { 9319 configurable: true, 9320 enumerable: false, 9321 value: { 9322 listeners: [listener] 9323 } 9324 }); 9325 9326 arrayEvents.forEach(function(key) { 9327 var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); 9328 var base = array[key]; 9329 9330 Object.defineProperty(array, key, { 9331 configurable: true, 9332 enumerable: false, 9333 value: function() { 9334 var args = Array.prototype.slice.call(arguments); 9335 var res = base.apply(this, args); 9336 9337 helpers.each(array._chartjs.listeners, function(object) { 9338 if (typeof object[method] === 'function') { 9339 object[method].apply(object, args); 9340 } 9341 }); 9342 9343 return res; 9344 } 9345 }); 9346 }); 9347 } 9348 9349 /** 9350 * Removes the given array event listener and cleanup extra attached properties (such as 9351 * the _chartjs stub and overridden methods) if array doesn't have any more listeners. 9352 */ 9353 function unlistenArrayEvents(array, listener) { 9354 var stub = array._chartjs; 9355 if (!stub) { 9356 return; 9357 } 9358 9359 var listeners = stub.listeners; 9360 var index = listeners.indexOf(listener); 9361 if (index !== -1) { 9362 listeners.splice(index, 1); 9363 } 9364 9365 if (listeners.length > 0) { 9366 return; 9367 } 9368 9369 arrayEvents.forEach(function(key) { 9370 delete array[key]; 9371 }); 9372 9373 delete array._chartjs; 9374 } 9375 9376 // Base class for all dataset controllers (line, bar, etc) 9377 Chart.DatasetController = function(chart, datasetIndex) { 9378 this.initialize(chart, datasetIndex); 9379 }; 9380 9381 helpers.extend(Chart.DatasetController.prototype, { 9382 9383 /** 9384 * Element type used to generate a meta dataset (e.g. Chart.element.Line). 9385 * @type {Chart.core.element} 9386 */ 9387 datasetElementType: null, 9388 9389 /** 9390 * Element type used to generate a meta data (e.g. Chart.element.Point). 9391 * @type {Chart.core.element} 9392 */ 9393 dataElementType: null, 9394
9395 initialize: function(chart, datasetIndex) { 9396 var me = this; 9397 me.chart = chart; 9398 me.index = datasetIndex; 9399 me.linkScales(); 9400 me.addElements(); 9401 }, 9402 9403 updateIndex: function(datasetIndex) { 9404 this.index = datasetIndex; 9405 }, 9406 9407 linkScales: function() { 9408 var me = this; 9409 var meta = me.getMeta(); 9410 var dataset = me.getDataset(); 9411 9412 if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { 9413 meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; 9414 } 9415 if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { 9416 meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; 9417 } 9418 }, 9419 9420 getDataset: function() { 9421 return this.chart.data.datasets[this.index]; 9422 }, 9423 9424 getMeta: function() { 9425 return this.chart.getDatasetMeta(this.index); 9426 }, 9427 9428 getScaleForId: function(scaleID) { 9429 return this.chart.scales[scaleID]; 9430 }, 9431 9432 reset: function() { 9433 this.update(true); 9434 }, 9435 9436 /** 9437 * @private 9438 */ 9439 destroy: function() { 9440 if (this._data) { 9441 unlistenArrayEvents(this._data, this); 9442 } 9443 }, 9444 9445 createMetaDataset: function() { 9446 var me = this; 9447 var type = me.datasetElementType; 9448 return type && new type({ 9449 _chart: me.chart, 9450 _datasetIndex: me.index 9451 }); 9452 }, 9453 9454 createMetaData: function(index) { 9455 var me = this; 9456 var type = me.dataElementType; 9457 return type && new type({ 9458 _chart: me.chart, 9459 _datasetIndex: me.index, 9460 _index: index 9461 }); 9462 }, 9463 9464 addElements: function() { 9465 var me = this; 9466 var meta = me.getMeta(); 9467 var data = me.getDataset().data || []; 9468 var metaData = meta.data; 9469 var i, ilen; 9470 9471 for (i = 0, ilen = data.length; i < ilen; ++i) { 9472 metaData[i] = metaData[i] || me.createMetaData(i); 9473 } 9474 9475 meta.dataset = meta.dataset || me.createMetaDataset(); 9476 }, 9477 9478 addElementAndReset: function(index) { 9479 var element = this.createMetaData(index); 9480 this.getMeta().data.splice(index, 0, element); 9481 this.updateElement(element, index, true); 9482 }, 9483 9484 buildOrUpdateElements: function() { 9485 var me = this; 9486 var dataset = me.getDataset(); 9487 var data = dataset.data || (dataset.data = []); 9488 9489 // In order to correctly handle data addition/deletion animation (an thus simulate 9490 // real-time charts), we need to monitor these data modifications and synchronize 9491 // the internal meta data accordingly. 9492 if (me._data !== data) { 9493 if (me._data) { 9494 // This case happens when the user replaced the data array instance. 9495 unlistenArrayEvents(me._data, me); 9496 } 9497 9498 listenArrayEvents(data, me); 9499 me._data = data; 9500 } 9501 9502 // Re-sync meta data in case the user replaced the data array or if we missed 9503 // any updates and so make sure that we handle number of datapoints changing. 9504 me.resyncElements(); 9505 }, 9506 9507 update: helpers.noop, 9508 9509 transition: function(easingValue) { 9510 var meta = this.getMeta(); 9511 var elements = meta.data || []; 9512 var ilen = elements.length; 9513 var i = 0; 9514 9515 for (; i < ilen; ++i) { 9516 elements[i].transition(easingValue); 9517 } 9518 9519 if (meta.dataset) { 9520 meta.dataset.transition(easingValue); 9521 } 9522 }, 9523 9524 draw: function() { 9525 var meta = this.getMeta(); 9526 var elements = meta.data || []; 9527 var ilen = elements.length; 9528 var i = 0; 9529 9530 if (meta.dataset) { 9531 meta.dataset.draw(); 9532 } 9533 9534 for (; i < ilen; ++i) { 9535 elements[i].draw(); 9536 } 9537 }, 9538 9539 removeHoverStyle: function(element) { 9540 helpers.merge(element._model, element.$previousStyle || {}); 9541 delete element.$previousStyle; 9542 }, 9543 9544 setHoverStyle: function(element) { 9545 var dataset = this.chart.data.datasets[element._datasetIndex]; 9546 var index = element._index; 9547 var custom = element.custom || {}; 9548 var valueOrDefault = helpers.valueAtIndexOrDefault; 9549 var getHoverColor = helpers.getHoverColor; 9550 var model = element._model; 9551 9552 element.$previousStyle = { 9553 backgroundColor: model.backgroundColor, 9554 borderColor: model.borderColor, 9555 borderWidth: model.borderWidth 9556 }; 9557 9558 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); 9559 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); 9560 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); 9561 }, 9562 9563 /** 9564 * @private 9565 */ 9566 resyncElements: function() { 9567 var me = this; 9568 var meta = me.getMeta(); 9569 var data = me.getDataset().data; 9570 var numMeta = meta.data.length; 9571 var numData = data.length; 9572 9573 if (numData < numMeta) { 9574 meta.data.splice(numData, numMeta - numData); 9575 } else if (numData > numMeta) { 9576 me.insertElements(numMeta, numData - numMeta); 9577 } 9578 }, 9579 9580 /** 9581 * @private 9582 */ 9583 insertElements: function(start, count) { 9584 for (var i = 0; i < count; ++i) { 9585 this.addElementAndReset(start + i); 9586 } 9587 }, 9588 9589 /** 9590 * @private 9591 */ 9592 onDataPush: function() { 9593 this.insertElements(this.getDataset().data.length - 1, arguments.length); 9594 }, 9595 9596 /** 9597 * @private 9598 */ 9599 onDataPop: function() { 9600 this.getMeta().data.pop(); 9601 }, 9602 9603 /** 9604 * @private 9605 */ 9606 onDataShift: function() { 9607 this.getMeta().data.shift(); 9608 }, 9609 9610 /** 9611 * @private 9612 */ 9613 onDataSplice: function(start, count) { 9614 this.getMeta().data.splice(start, count); 9615 this.insertElements(start, arguments.length - 2); 9616 }, 9617 9618 /** 9619 * @private 9620 */ 9621 onDataUnshift: function() { 9622 this.insertElements(0, arguments.length); 9623 } 9624 }); 9625 9626 Chart.DatasetController.extend = helpers.inherits; 9627}; 9628 9629},{"46":46}],26:[function(require,module,exports){ 9630'use strict'; 9631 9632var helpers = require(46); 9633 9634module.exports = { 9635 /** 9636 * @private 9637 */ 9638 _set: function(scope, values) { 9639 return helpers.merge(this[scope] || (this[scope] = {}), values); 9640 } 9641}; 9642 9643},{"46":46}],27:[function(require,module,exports){ 9644'use strict'; 9645 9646var color = require(2); 9647var helpers = require(46); 9648 9649function interpolate(start, view, model, ease) { 9650 var keys = Object.keys(model); 9651 var i, ilen, key, actual, origin, target, type, c0, c1; 9652 9653 for (i = 0, ilen = keys.length; i < ilen; ++i) { 9654 key = keys[i]; 9655 9656 target = model[key]; 9657 9658 // if a value is added to the model after pivot() has been called, the view 9659 // doesn't contain it, so let's initialize the view to the target value. 9660 if (!view.hasOwnProperty(key)) { 9661 view[key] = target; 9662 } 9663 9664 actual = view[key]; 9665
vendor: 30,038 bytes, lines 9666-10637
9666 if (actual === target || key[0] === '_') { 9667 continue; 9668 } 9669 9670 if (!start.hasOwnProperty(key)) { 9671 start[key] = actual; 9672 } 9673 9674 origin = start[key]; 9675 9676 type = typeof target; 9677 9678 if (type === typeof origin) { 9679 if (type === 'string') { 9680 c0 = color(origin); 9681 if (c0.valid) { 9682 c1 = color(target); 9683 if (c1.valid) { 9684 view[key] = c1.mix(c0, ease).rgbString(); 9685 continue; 9686 } 9687 } 9688 } else if (type === 'number' && isFinite(origin) && isFinite(target)) { 9689 view[key] = origin + (target - origin) * ease; 9690 continue; 9691 } 9692 } 9693 9694 view[key] = target; 9695 } 9696} 9697 9698var Element = function(configuration) { 9699 helpers.extend(this, configuration); 9700 this.initialize.apply(this, arguments); 9701}; 9702 9703helpers.extend(Element.prototype, { 9704 9705 initialize: function() { 9706 this.hidden = false; 9707 }, 9708 9709 pivot: function() { 9710 var me = this; 9711 if (!me._view) { 9712 me._view = helpers.clone(me._model); 9713 } 9714 me._start = {}; 9715 return me; 9716 }, 9717 9718 transition: function(ease) { 9719 var me = this; 9720 var model = me._model; 9721 var start = me._start; 9722 var view = me._view; 9723 9724 // No animation -> No Transition 9725 if (!model || ease === 1) { 9726 me._view = model; 9727 me._start = null; 9728 return me; 9729 } 9730 9731 if (!view) { 9732 view = me._view = {}; 9733 } 9734 9735 if (!start) { 9736 start = me._start = {}; 9737 } 9738 9739 interpolate(start, view, model, ease); 9740 9741 return me; 9742 }, 9743 9744 tooltipPosition: function() { 9745 return { 9746 x: this._model.x, 9747 y: this._model.y 9748 }; 9749 }, 9750 9751 hasValue: function() { 9752 return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); 9753 } 9754}); 9755 9756Element.extend = helpers.inherits; 9757 9758module.exports = Element; 9759 9760},{"2":2,"46":46}],28:[function(require,module,exports){ 9761/* global window: false */ 9762/* global document: false */ 9763'use strict'; 9764 9765var color = require(2); 9766var defaults = require(26); 9767var helpers = require(46); 9768var scaleService = require(34); 9769 9770module.exports = function() { 9771 9772 // -- Basic js utility methods 9773 9774 helpers.configMerge = function(/* objects ... */) { 9775 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { 9776 merger: function(key, target, source, options) { 9777 var tval = target[key] || {}; 9778 var sval = source[key]; 9779 9780 if (key === 'scales') { 9781 // scale config merging is complex. Add our own function here for that 9782 target[key] = helpers.scaleMerge(tval, sval); 9783 } else if (key === 'scale') { 9784 // used in polar area & radar charts since there is only one scale 9785 target[key] = helpers.merge(tval, [scaleService.getScaleDefaults(sval.type), sval]); 9786 } else { 9787 helpers._merger(key, target, source, options); 9788 } 9789 } 9790 }); 9791 }; 9792 9793 helpers.scaleMerge = function(/* objects ... */) { 9794 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { 9795 merger: function(key, target, source, options) { 9796 if (key === 'xAxes' || key === 'yAxes') { 9797 var slen = source[key].length; 9798 var i, type, scale; 9799 9800 if (!target[key]) { 9801 target[key] = []; 9802 } 9803 9804 for (i = 0; i < slen; ++i) { 9805 scale = source[key][i]; 9806 type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); 9807 9808 if (i >= target[key].length) { 9809 target[key].push({}); 9810 } 9811 9812 if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { 9813 // new/untyped scale or type changed: let's apply the new defaults 9814 // then merge source scale to correctly overwrite the defaults. 9815 helpers.merge(target[key][i], [scaleService.getScaleDefaults(type), scale]); 9816 } else { 9817 // scales type are the same 9818 helpers.merge(target[key][i], scale); 9819 } 9820 } 9821 } else { 9822 helpers._merger(key, target, source, options); 9823 } 9824 } 9825 }); 9826 }; 9827 9828 helpers.where = function(collection, filterCallback) { 9829 if (helpers.isArray(collection) && Array.prototype.filter) { 9830 return collection.filter(filterCallback); 9831 } 9832 var filtered = []; 9833 9834 helpers.each(collection, function(item) { 9835 if (filterCallback(item)) { 9836 filtered.push(item); 9837 } 9838 }); 9839 9840 return filtered; 9841 }; 9842 helpers.findIndex = Array.prototype.findIndex ? 9843 function(array, callback, scope) { 9844 return array.findIndex(callback, scope); 9845 } : 9846 function(array, callback, scope) { 9847 scope = scope === undefined ? array : scope; 9848 for (var i = 0, ilen = array.length; i < ilen; ++i) { 9849 if (callback.call(scope, array[i], i, array)) { 9850 return i; 9851 } 9852 } 9853 return -1; 9854 }; 9855 helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { 9856 // Default to start of the array 9857 if (helpers.isNullOrUndef(startIndex)) { 9858 startIndex = -1; 9859 } 9860 for (var i = startIndex + 1; i < arrayToSearch.length; i++) { 9861 var currentItem = arrayToSearch[i]; 9862 if (filterCallback(currentItem)) { 9863 return currentItem; 9864 } 9865 } 9866 }; 9867 helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { 9868 // Default to end of the array 9869 if (helpers.isNullOrUndef(startIndex)) { 9870 startIndex = arrayToSearch.length; 9871 } 9872 for (var i = startIndex - 1; i >= 0; i--) { 9873 var currentItem = arrayToSearch[i]; 9874 if (filterCallback(currentItem)) { 9875 return currentItem; 9876 } 9877 } 9878 }; 9879 9880 // -- Math methods 9881 helpers.isNumber = function(n) { 9882 return !isNaN(parseFloat(n)) && isFinite(n); 9883 }; 9884 helpers.almostEquals = function(x, y, epsilon) { 9885 return Math.abs(x - y) < epsilon; 9886 }; 9887 helpers.almostWhole = function(x, epsilon) { 9888 var rounded = Math.round(x); 9889 return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); 9890 }; 9891 helpers.max = function(array) { 9892 return array.reduce(function(max, value) { 9893 if (!isNaN(value)) { 9894 return Math.max(max, value); 9895 } 9896 return max; 9897 }, Number.NEGATIVE_INFINITY); 9898 }; 9899 helpers.min = function(array) { 9900 return array.reduce(function(min, value) { 9901 if (!isNaN(value)) { 9902 return Math.min(min, value); 9903 } 9904 return min; 9905 }, Number.POSITIVE_INFINITY); 9906 }; 9907 helpers.sign = Math.sign ? 9908 function(x) { 9909 return Math.sign(x); 9910 } : 9911 function(x) { 9912 x = +x; // convert to a number 9913 if (x === 0 || isNaN(x)) { 9914 return x; 9915 } 9916 return x > 0 ? 1 : -1; 9917 }; 9918 helpers.log10 = Math.log10 ? 9919 function(x) { 9920 return Math.log10(x); 9921 } : 9922 function(x) { 9923 var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. 9924 // Check for whole powers of 10, 9925 // which due to floating point rounding error should be corrected. 9926 var powerOf10 = Math.round(exponent); 9927 var isPowerOf10 = x === Math.pow(10, powerOf10); 9928 9929 return isPowerOf10 ? powerOf10 : exponent; 9930 }; 9931 helpers.toRadians = function(degrees) { 9932 return degrees * (Math.PI / 180); 9933 }; 9934 helpers.toDegrees = function(radians) { 9935 return radians * (180 / Math.PI); 9936 }; 9937 // Gets the angle from vertical upright to the point about a centre. 9938 helpers.getAngleFromPoint = function(centrePoint, anglePoint) { 9939 var distanceFromXCenter = anglePoint.x - centrePoint.x; 9940 var distanceFromYCenter = anglePoint.y - centrePoint.y; 9941 var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); 9942 9943 var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); 9944 9945 if (angle < (-0.5 * Math.PI)) { 9946 angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] 9947 } 9948 9949 return { 9950 angle: angle, 9951 distance: radialDistanceFromCenter 9952 }; 9953 }; 9954 helpers.distanceBetweenPoints = function(pt1, pt2) { 9955 return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); 9956 }; 9957 helpers.aliasPixel = function(pixelWidth) { 9958 return (pixelWidth % 2 === 0) ? 0 : 0.5; 9959 }; 9960 helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { 9961 // Props to Rob Spencer at scaled innovation for his post on splining between points 9962 // http://scaledinnovation.com/analytics/splines/aboutSplines.html 9963 9964 // This function must also respect "skipped" points 9965 9966 var previous = firstPoint.skip ? middlePoint : firstPoint; 9967 var current = middlePoint; 9968 var next = afterPoint.skip ? middlePoint : afterPoint; 9969 9970 var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); 9971 var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); 9972 9973 var s01 = d01 / (d01 + d12); 9974 var s12 = d12 / (d01 + d12); 9975 9976 // If all points are the same, s01 & s02 will be inf 9977 s01 = isNaN(s01) ? 0 : s01; 9978 s12 = isNaN(s12) ? 0 : s12; 9979 9980 var fa = t * s01; // scaling factor for triangle Ta 9981 var fb = t * s12; 9982 9983 return { 9984 previous: { 9985 x: current.x - fa * (next.x - previous.x), 9986 y: current.y - fa * (next.y - previous.y) 9987 }, 9988 next: { 9989 x: current.x + fb * (next.x - previous.x), 9990 y: current.y + fb * (next.y - previous.y) 9991 } 9992 }; 9993 }; 9994 helpers.EPSILON = Number.EPSILON || 1e-14; 9995 helpers.splineCurveMonotone = function(points) { 9996 // This function calculates Bézier control points in a similar way than |splineCurve|, 9997 // but preserves monotonicity of the provided data and ensures no local extremums are added 9998 // between the dataset discrete points due to the interpolation. 9999 // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation 10000 10001 var pointsWithTangents = (points || []).map(function(point) { 10002 return { 10003 model: point._model, 10004 deltaK: 0, 10005 mK: 0 10006 }; 10007 }); 10008 10009 // Calculate slopes (deltaK) and initialize tangents (mK) 10010 var pointsLen = pointsWithTangents.length; 10011 var i, pointBefore, pointCurrent, pointAfter; 10012 for (i = 0; i < pointsLen; ++i) { 10013 pointCurrent = pointsWithTangents[i]; 10014 if (pointCurrent.model.skip) { 10015 continue; 10016 } 10017 10018 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; 10019 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; 10020 if (pointAfter && !pointAfter.model.skip) { 10021 var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); 10022 10023 // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 10024 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; 10025 } 10026 10027 if (!pointBefore || pointBefore.model.skip) { 10028 pointCurrent.mK = pointCurrent.deltaK; 10029 } else if (!pointAfter || pointAfter.model.skip) { 10030 pointCurrent.mK = pointBefore.deltaK; 10031 } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { 10032 pointCurrent.mK = 0; 10033 } else { 10034 pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; 10035 } 10036 } 10037 10038 // Adjust tangents to ensure monotonic properties 10039 var alphaK, betaK, tauK, squaredMagnitude; 10040 for (i = 0; i < pointsLen - 1; ++i) { 10041 pointCurrent = pointsWithTangents[i]; 10042 pointAfter = pointsWithTangents[i + 1]; 10043 if (pointCurrent.model.skip || pointAfter.model.skip) { 10044 continue; 10045 } 10046 10047 if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { 10048 pointCurrent.mK = pointAfter.mK = 0; 10049 continue; 10050 } 10051 10052 alphaK = pointCurrent.mK / pointCurrent.deltaK; 10053 betaK = pointAfter.mK / pointCurrent.deltaK; 10054 squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); 10055 if (squaredMagnitude <= 9) { 10056 continue; 10057 } 10058 10059 tauK = 3 / Math.sqrt(squaredMagnitude); 10060 pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; 10061 pointAfter.mK = betaK * tauK * pointCurrent.deltaK; 10062 } 10063 10064 // Compute control points 10065 var deltaX; 10066 for (i = 0; i < pointsLen; ++i) { 10067 pointCurrent = pointsWithTangents[i]; 10068 if (pointCurrent.model.skip) { 10069 continue; 10070 } 10071 10072 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; 10073 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; 10074 if (pointBefore && !pointBefore.model.skip) { 10075 deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; 10076 pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; 10077 pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; 10078 } 10079 if (pointAfter && !pointAfter.model.skip) { 10080 deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; 10081 pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; 10082 pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; 10083 } 10084 } 10085 }; 10086 helpers.nextItem = function(collection, index, loop) { 10087 if (loop) { 10088 return index >= collection.length - 1 ? collection[0] : collection[index + 1]; 10089 } 10090 return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; 10091 }; 10092 helpers.previousItem = function(collection, index, loop) { 10093 if (loop) { 10094 return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; 10095 } 10096 return index <= 0 ? collection[0] : collection[index - 1]; 10097 }; 10098 // Implementation of the nice number algorithm used in determining where axis labels will go 10099 helpers.niceNum = function(range, round) { 10100 var exponent = Math.floor(helpers.log10(range)); 10101 var fraction = range / Math.pow(10, exponent); 10102 var niceFraction; 10103 10104 if (round) { 10105 if (fraction < 1.5) { 10106 niceFraction = 1; 10107 } else if (fraction < 3) { 10108 niceFraction = 2; 10109 } else if (fraction < 7) { 10110 niceFraction = 5; 10111 } else { 10112 niceFraction = 10; 10113 } 10114 } else if (fraction <= 1.0) { 10115 niceFraction = 1; 10116 } else if (fraction <= 2) { 10117 niceFraction = 2; 10118 } else if (fraction <= 5) { 10119 niceFraction = 5; 10120 } else { 10121 niceFraction = 10; 10122 } 10123 10124 return niceFraction * Math.pow(10, exponent); 10125 }; 10126 // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ 10127 helpers.requestAnimFrame = (function() { 10128 if (typeof window === 'undefined') { 10129 return function(callback) { 10130 callback(); 10131 }; 10132 } 10133 return window.requestAnimationFrame || 10134 window.webkitRequestAnimationFrame || 10135 window.mozRequestAnimationFrame || 10136 window.oRequestAnimationFrame || 10137 window.msRequestAnimationFrame || 10138 function(callback) { 10139 return window.setTimeout(callback, 1000 / 60); 10140 }; 10141 }()); 10142 // -- DOM methods 10143 helpers.getRelativePosition = function(evt, chart) { 10144 var mouseX, mouseY; 10145 var e = evt.originalEvent || evt; 10146 var canvas = evt.target || evt.srcElement; 10147 var boundingRect = canvas.getBoundingClientRect(); 10148 10149 var touches = e.touches; 10150 if (touches && touches.length > 0) { 10151 mouseX = touches[0].clientX; 10152 mouseY = touches[0].clientY; 10153 10154 } else { 10155 mouseX = e.clientX; 10156 mouseY = e.clientY; 10157 } 10158 10159 // Scale mouse coordinates into canvas coordinates 10160 // by following the pattern laid out by 'jerryj' in the comments of 10161 // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ 10162 var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); 10163 var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); 10164 var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); 10165 var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); 10166 var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; 10167 var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; 10168 10169 // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However 10170 // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here 10171 mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); 10172 mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); 10173 10174 return { 10175 x: mouseX, 10176 y: mouseY 10177 }; 10178 10179 }; 10180 10181 // Private helper function to convert max-width/max-height values that may be percentages into a number 10182 function parseMaxStyle(styleValue, node, parentProperty) { 10183 var valueInPixels; 10184 if (typeof styleValue === 'string') { 10185 valueInPixels = parseInt(styleValue, 10); 10186 10187 if (styleValue.indexOf('%') !== -1) { 10188 // percentage * size in dimension 10189 valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; 10190 } 10191 } else { 10192 valueInPixels = styleValue; 10193 } 10194 10195 return valueInPixels; 10196 } 10197 10198 /** 10199 * Returns if the given value contains an effective constraint. 10200 * @private 10201 */ 10202 function isConstrainedValue(value) { 10203 return value !== undefined && value !== null && value !== 'none'; 10204 } 10205 10206 // Private helper to get a constraint dimension 10207 // @param domNode : the node to check the constraint on 10208 // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) 10209 // @param percentageProperty : property of parent to use when calculating width as a percentage 10210 // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser 10211 function getConstraintDimension(domNode, maxStyle, percentageProperty) { 10212 var view = document.defaultView; 10213 var parentNode = helpers._getParentNode(domNode); 10214 var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; 10215 var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; 10216 var hasCNode = isConstrainedValue(constrainedNode); 10217 var hasCContainer = isConstrainedValue(constrainedContainer); 10218 var infinity = Number.POSITIVE_INFINITY; 10219 10220 if (hasCNode || hasCContainer) { 10221 return Math.min( 10222 hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, 10223 hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); 10224 } 10225 10226 return 'none'; 10227 } 10228 // returns Number or undefined if no constraint 10229 helpers.getConstraintWidth = function(domNode) { 10230 return getConstraintDimension(domNode, 'max-width', 'clientWidth'); 10231 }; 10232 // returns Number or undefined if no constraint 10233 helpers.getConstraintHeight = function(domNode) { 10234 return getConstraintDimension(domNode, 'max-height', 'clientHeight'); 10235 }; 10236 /** 10237 * @private 10238 */ 10239 helpers._calculatePadding = function(container, padding, parentDimension) { 10240 padding = helpers.getStyle(container, padding); 10241 10242 return padding.indexOf('%') > -1 ? parentDimension / parseInt(padding, 10) : parseInt(padding, 10); 10243 }; 10244 /** 10245 * @private 10246 */ 10247 helpers._getParentNode = function(domNode) { 10248 var parent = domNode.parentNode; 10249 if (parent && parent.host) { 10250 parent = parent.host; 10251 } 10252 return parent; 10253 }; 10254 helpers.getMaximumWidth = function(domNode) { 10255 var container = helpers._getParentNode(domNode); 10256 if (!container) { 10257 return domNode.clientWidth; 10258 } 10259 10260 var clientWidth = container.clientWidth; 10261 var paddingLeft = helpers._calculatePadding(container, 'padding-left', clientWidth); 10262 var paddingRight = helpers._calculatePadding(container, 'padding-right', clientWidth); 10263 10264 var w = clientWidth - paddingLeft - paddingRight; 10265 var cw = helpers.getConstraintWidth(domNode); 10266 return isNaN(cw) ? w : Math.min(w, cw); 10267 }; 10268 helpers.getMaximumHeight = function(domNode) { 10269 var container = helpers._getParentNode(domNode); 10270 if (!container) { 10271 return domNode.clientHeight; 10272 } 10273 10274 var clientHeight = container.clientHeight; 10275 var paddingTop = helpers._calculatePadding(container, 'padding-top', clientHeight); 10276 var paddingBottom = helpers._calculatePadding(container, 'padding-bottom', clientHeight); 10277 10278 var h = clientHeight - paddingTop - paddingBottom; 10279 var ch = helpers.getConstraintHeight(domNode); 10280 return isNaN(ch) ? h : Math.min(h, ch); 10281 }; 10282 helpers.getStyle = function(el, property) { 10283 return el.currentStyle ? 10284 el.currentStyle[property] : 10285 document.defaultView.getComputedStyle(el, null).getPropertyValue(property); 10286 }; 10287 helpers.retinaScale = function(chart, forceRatio) { 10288 var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1; 10289 if (pixelRatio === 1) { 10290 return; 10291 } 10292 10293 var canvas = chart.canvas; 10294 var height = chart.height; 10295 var width = chart.width; 10296 10297 canvas.height = height * pixelRatio; 10298 canvas.width = width * pixelRatio; 10299 chart.ctx.scale(pixelRatio, pixelRatio); 10300 10301 // If no style has been set on the canvas, the render size is used as display size, 10302 // making the chart visually bigger, so let's enforce it to the "correct" values. 10303 // See https://github.com/chartjs/Chart.js/issues/3575 10304 if (!canvas.style.height && !canvas.style.width) { 10305 canvas.style.height = height + 'px'; 10306 canvas.style.width = width + 'px'; 10307 } 10308 }; 10309 // -- Canvas methods 10310 helpers.fontString = function(pixelSize, fontStyle, fontFamily) { 10311 return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; 10312 }; 10313 helpers.longestText = function(ctx, font, arrayOfThings, cache) { 10314 cache = cache || {}; 10315 var data = cache.data = cache.data || {}; 10316 var gc = cache.garbageCollect = cache.garbageCollect || []; 10317 10318 if (cache.font !== font) { 10319 data = cache.data = {}; 10320 gc = cache.garbageCollect = []; 10321 cache.font = font; 10322 } 10323 10324 ctx.font = font; 10325 var longest = 0; 10326 helpers.each(arrayOfThings, function(thing) { 10327 // Undefined strings and arrays should not be measured 10328 if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { 10329 longest = helpers.measureText(ctx, data, gc, longest, thing); 10330 } else if (helpers.isArray(thing)) { 10331 // if it is an array lets measure each element 10332 // to do maybe simplify this function a bit so we can do this more recursively? 10333 helpers.each(thing, function(nestedThing) { 10334 // Undefined strings and arrays should not be measured 10335 if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { 10336 longest = helpers.measureText(ctx, data, gc, longest, nestedThing); 10337 } 10338 }); 10339 } 10340 }); 10341 10342 var gcLen = gc.length / 2; 10343 if (gcLen > arrayOfThings.length) { 10344 for (var i = 0; i < gcLen; i++) { 10345 delete data[gc[i]]; 10346 } 10347 gc.splice(0, gcLen); 10348 } 10349 return longest; 10350 }; 10351 helpers.measureText = function(ctx, data, gc, longest, string) { 10352 var textWidth = data[string]; 10353 if (!textWidth) { 10354 textWidth = data[string] = ctx.measureText(string).width; 10355 gc.push(string); 10356 } 10357 if (textWidth > longest) { 10358 longest = textWidth; 10359 } 10360 return longest; 10361 }; 10362 helpers.numberOfLabelLines = function(arrayOfThings) { 10363 var numberOfLines = 1; 10364 helpers.each(arrayOfThings, function(thing) { 10365 if (helpers.isArray(thing)) { 10366 if (thing.length > numberOfLines) { 10367 numberOfLines = thing.length; 10368 } 10369 } 10370 }); 10371 return numberOfLines; 10372 }; 10373 10374 helpers.color = !color ? 10375 function(value) { 10376 console.error('Color.js not found!'); 10377 return value; 10378 } : 10379 function(value) { 10380 /* global CanvasGradient */ 10381 if (value instanceof CanvasGradient) { 10382 value = defaults.global.defaultColor; 10383 } 10384 10385 return color(value); 10386 }; 10387 10388 helpers.getHoverColor = function(colorValue) { 10389 /* global CanvasPattern */ 10390 return (colorValue instanceof CanvasPattern) ? 10391 colorValue : 10392 helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); 10393 }; 10394}; 10395 10396},{"2":2,"26":26,"34":34,"46":46}],29:[function(require,module,exports){ 10397'use strict'; 10398 10399var helpers = require(46); 10400 10401/** 10402 * Helper function to get relative position for an event 10403 * @param {Event|IEvent} event - The event to get the position for 10404 * @param {Chart} chart - The chart 10405 * @returns {Point} the event position 10406 */ 10407function getRelativePosition(e, chart) { 10408 if (e.native) { 10409 return { 10410 x: e.x, 10411 y: e.y 10412 }; 10413 } 10414 10415 return helpers.getRelativePosition(e, chart); 10416} 10417 10418/** 10419 * Helper function to traverse all of the visible elements in the chart 10420 * @param chart {chart} the chart 10421 * @param handler {Function} the callback to execute for each visible item 10422 */ 10423function parseVisibleItems(chart, handler) { 10424 var datasets = chart.data.datasets; 10425 var meta, i, j, ilen, jlen; 10426 10427 for (i = 0, ilen = datasets.length; i < ilen; ++i) { 10428 if (!chart.isDatasetVisible(i)) { 10429 continue; 10430 } 10431 10432 meta = chart.getDatasetMeta(i); 10433 for (j = 0, jlen = meta.data.length; j < jlen; ++j) { 10434 var element = meta.data[j]; 10435 if (!element._view.skip) { 10436 handler(element); 10437 } 10438 } 10439 } 10440} 10441 10442/** 10443 * Helper function to get the items that intersect the event position 10444 * @param items {ChartElement[]} elements to filter 10445 * @param position {Point} the point to be nearest to 10446 * @return {ChartElement[]} the nearest items 10447 */ 10448function getIntersectItems(chart, position) { 10449 var elements = []; 10450 10451 parseVisibleItems(chart, function(element) { 10452 if (element.inRange(position.x, position.y)) { 10453 elements.push(element); 10454 } 10455 }); 10456 10457 return elements; 10458} 10459 10460/** 10461 * Helper function to get the items nearest to the event position considering all visible items in teh chart 10462 * @param chart {Chart} the chart to look at elements from 10463 * @param position {Point} the point to be nearest to 10464 * @param intersect {Boolean} if true, only consider items that intersect the position 10465 * @param distanceMetric {Function} function to provide the distance between points 10466 * @return {ChartElement[]} the nearest items 10467 */ 10468function getNearestItems(chart, position, intersect, distanceMetric) { 10469 var minDistance = Number.POSITIVE_INFINITY; 10470 var nearestItems = []; 10471 10472 parseVisibleItems(chart, function(element) { 10473 if (intersect && !element.inRange(position.x, position.y)) { 10474 return; 10475 } 10476 10477 var center = element.getCenterPoint(); 10478 var distance = distanceMetric(position, center); 10479 10480 if (distance < minDistance) { 10481 nearestItems = [element]; 10482 minDistance = distance; 10483 } else if (distance === minDistance) { 10484 // Can have multiple items at the same distance in which case we sort by size 10485 nearestItems.push(element); 10486 } 10487 }); 10488 10489 return nearestItems; 10490} 10491 10492/** 10493 * Get a distance metric function for two points based on the 10494 * axis mode setting 10495 * @param {String} axis the axis mode. x|y|xy 10496 */ 10497function getDistanceMetricForAxis(axis) { 10498 var useX = axis.indexOf('x') !== -1; 10499 var useY = axis.indexOf('y') !== -1; 10500 10501 return function(pt1, pt2) { 10502 var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; 10503 var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; 10504 return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); 10505 }; 10506} 10507 10508function indexMode(chart, e, options) { 10509 var position = getRelativePosition(e, chart); 10510 // Default axis for index mode is 'x' to match old behaviour 10511 options.axis = options.axis || 'x'; 10512 var distanceMetric = getDistanceMetricForAxis(options.axis); 10513 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); 10514 var elements = []; 10515 10516 if (!items.length) { 10517 return []; 10518 } 10519 10520 chart.data.datasets.forEach(function(dataset, datasetIndex) { 10521 if (chart.isDatasetVisible(datasetIndex)) { 10522 var meta = chart.getDatasetMeta(datasetIndex); 10523 var element = meta.data[items[0]._index]; 10524 10525 // don't count items that are skipped (null data) 10526 if (element && !element._view.skip) { 10527 elements.push(element); 10528 } 10529 } 10530 }); 10531 10532 return elements; 10533} 10534 10535/** 10536 * @interface IInteractionOptions 10537 */ 10538/** 10539 * If true, only consider items that intersect the point 10540 * @name IInterfaceOptions#boolean 10541 * @type Boolean 10542 */ 10543 10544/** 10545 * Contains interaction related functions 10546 * @namespace Chart.Interaction 10547 */ 10548module.exports = { 10549 // Helper function for different modes 10550 modes: { 10551 single: function(chart, e) { 10552 var position = getRelativePosition(e, chart); 10553 var elements = []; 10554 10555 parseVisibleItems(chart, function(element) { 10556 if (element.inRange(position.x, position.y)) { 10557 elements.push(element); 10558 return elements; 10559 } 10560 }); 10561 10562 return elements.slice(0, 1); 10563 }, 10564 10565 /** 10566 * @function Chart.Interaction.modes.label 10567 * @deprecated since version 2.4.0 10568 * @todo remove at version 3 10569 * @private 10570 */ 10571 label: indexMode, 10572 10573 /** 10574 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something 10575 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item 10576 * @function Chart.Interaction.modes.index 10577 * @since v2.4.0 10578 * @param chart {chart} the chart we are returning items from 10579 * @param e {Event} the event we are find things at 10580 * @param options {IInteractionOptions} options to use during interaction 10581 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10582 */ 10583 index: indexMode, 10584 10585 /** 10586 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something 10587 * If the options.intersect is false, we find the nearest item and return the items in that dataset 10588 * @function Chart.Interaction.modes.dataset 10589 * @param chart {chart} the chart we are returning items from 10590 * @param e {Event} the event we are find things at 10591 * @param options {IInteractionOptions} options to use during interaction 10592 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10593 */ 10594 dataset: function(chart, e, options) { 10595 var position = getRelativePosition(e, chart); 10596 options.axis = options.axis || 'xy'; 10597 var distanceMetric = getDistanceMetricForAxis(options.axis); 10598 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); 10599 10600 if (items.length > 0) { 10601 items = chart.getDatasetMeta(items[0]._datasetIndex).data; 10602 } 10603 10604 return items; 10605 }, 10606 10607 /** 10608 * @function Chart.Interaction.modes.x-axis 10609 * @deprecated since version 2.4.0. Use index mode and intersect == true 10610 * @todo remove at version 3 10611 * @private 10612 */ 10613 'x-axis': function(chart, e) { 10614 return indexMode(chart, e, {intersect: false}); 10615 }, 10616 10617 /** 10618 * Point mode returns all elements that hit test based on the event position 10619 * of the event 10620 * @function Chart.Interaction.modes.intersect 10621 * @param chart {chart} the chart we are returning items from 10622 * @param e {Event} the event we are find things at 10623 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10624 */ 10625 point: function(chart, e) { 10626 var position = getRelativePosition(e, chart); 10627 return getIntersectItems(chart, position); 10628 }, 10629 10630 /** 10631 * nearest mode returns the element closest to the point 10632 * @function Chart.Interaction.modes.intersect 10633 * @param chart {chart} the chart we are returning items from 10634 * @param e {Event} the event we are find things at 10635 * @param options {IInteractionOptions} options to use 10636 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10637 */
10638 nearest: function(chart, e, options) { 10639 var position = getRelativePosition(e, chart); 10640 options.axis = options.axis || 'xy'; 10641 var distanceMetric = getDistanceMetricForAxis(options.axis); 10642 var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); 10643 10644 // We have multiple items at the same distance from the event. Now sort by smallest 10645 if (nearestItems.length > 1) { 10646 nearestItems.sort(function(a, b) { 10647 var sizeA = a.getArea(); 10648 var sizeB = b.getArea(); 10649 var ret = sizeA - sizeB; 10650 10651 if (ret === 0) { 10652 // if equal sort by dataset index 10653 ret = a._datasetIndex - b._datasetIndex; 10654 } 10655 10656 return ret; 10657 }); 10658 } 10659 10660 // Return only 1 item 10661 return nearestItems.slice(0, 1); 10662 }, 10663 10664 /** 10665 * x mode returns the elements that hit-test at the current x coordinate 10666 * @function Chart.Interaction.modes.x 10667 * @param chart {chart} the chart we are returning items from 10668 * @param e {Event} the event we are find things at 10669 * @param options {IInteractionOptions} options to use 10670 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10671 */ 10672 x: function(chart, e, options) { 10673 var position = getRelativePosition(e, chart); 10674 var items = []; 10675 var intersectsItem = false; 10676 10677 parseVisibleItems(chart, function(element) { 10678 if (element.inXRange(position.x)) { 10679 items.push(element); 10680 } 10681 10682 if (element.inRange(position.x, position.y)) { 10683 intersectsItem = true; 10684 } 10685 }); 10686 10687 // If we want to trigger on an intersect and we don't have any items 10688 // that intersect the position, return nothing 10689 if (options.intersect && !intersectsItem) { 10690 items = []; 10691 } 10692 return items; 10693 }, 10694 10695 /** 10696 * y mode returns the elements that hit-test at the current y coordinate 10697 * @function Chart.Interaction.modes.y 10698 * @param chart {chart} the chart we are returning items from 10699 * @param e {Event} the event we are find things at 10700 * @param options {IInteractionOptions} options to use 10701 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10702 */ 10703 y: function(chart, e, options) { 10704 var position = getRelativePosition(e, chart); 10705 var items = []; 10706 var intersectsItem = false; 10707 10708 parseVisibleItems(chart, function(element) { 10709 if (element.inYRange(position.y)) { 10710 items.push(element); 10711 } 10712 10713 if (element.inRange(position.x, position.y)) { 10714 intersectsItem = true; 10715 } 10716 }); 10717 10718 // If we want to trigger on an intersect and we don't have any items 10719 // that intersect the position, return nothing 10720 if (options.intersect && !intersectsItem) { 10721 items = []; 10722 } 10723 return items; 10724 } 10725 } 10726}; 10727 10728},{"46":46}],30:[function(require,module,exports){ 10729'use strict'; 10730 10731var defaults = require(26); 10732 10733defaults._set('global', { 10734 responsive: true, 10735 responsiveAnimationDuration: 0, 10736 maintainAspectRatio: true, 10737 events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], 10738 hover: { 10739 onHover: null, 10740 mode: 'nearest', 10741 intersect: true, 10742 animationDuration: 400 10743 }, 10744 onClick: null, 10745 defaultColor: 'rgba(0,0,0,0.1)', 10746 defaultFontColor: '#666', 10747 defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", 10748 defaultFontSize: 12, 10749 defaultFontStyle: 'normal', 10750 showLines: true, 10751 10752 // Element defaults defined in element extensions 10753 elements: {}, 10754 10755 // Layout options such as padding 10756 layout: { 10757 padding: { 10758 top: 0, 10759 right: 0, 10760 bottom: 0, 10761 left: 0 10762 } 10763 } 10764}); 10765 10766module.exports = function() { 10767 10768 // Occupy the global variable of Chart, and create a simple base class 10769 var Chart = function(item, config) { 10770 this.construct(item, config); 10771 return this; 10772 }; 10773 10774 Chart.Chart = Chart; 10775 10776 return Chart; 10777}; 10778 10779},{"26":26}],31:[function(require,module,exports){ 10780'use strict'; 10781 10782var helpers = require(46); 10783 10784function filterByPosition(array, position) { 10785 return helpers.where(array, function(v) { 10786 return v.position === position; 10787 }); 10788} 10789 10790function sortByWeight(array, reverse) { 10791 array.forEach(function(v, i) { 10792 v._tmpIndex_ = i; 10793 return v; 10794 }); 10795 array.sort(function(a, b) { 10796 var v0 = reverse ? b : a; 10797 var v1 = reverse ? a : b; 10798 return v0.weight === v1.weight ? 10799 v0._tmpIndex_ - v1._tmpIndex_ : 10800 v0.weight - v1.weight; 10801 }); 10802 array.forEach(function(v) { 10803 delete v._tmpIndex_; 10804 }); 10805} 10806 10807/** 10808 * @interface ILayoutItem 10809 * @prop {String} position - The position of the item in the chart layout. Possible values are 10810 * 'left', 'top', 'right', 'bottom', and 'chartArea' 10811 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area 10812 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down 10813 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) 10814 * @prop {Function} update - Takes two parameters: width and height. Returns size of item 10815 * @prop {Function} getPadding - Returns an object with padding on the edges 10816 * @prop {Number} width - Width of item. Must be valid after update() 10817 * @prop {Number} height - Height of item. Must be valid after update() 10818 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update 10819 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update 10820 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update 10821 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update 10822 */ 10823 10824// The layout service is very self explanatory. It's responsible for the layout within a chart. 10825// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need 10826// It is this service's responsibility of carrying out that layout. 10827module.exports = { 10828 defaults: {}, 10829 10830 /** 10831 * Register a box to a chart. 10832 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. 10833 * @param {Chart} chart - the chart to use 10834 * @param {ILayoutItem} item - the item to add to be layed out 10835 */
10836 addBox: function(chart, item) { 10837 if (!chart.boxes) { 10838 chart.boxes = []; 10839 } 10840 10841 // initialize item with default values 10842 item.fullWidth = item.fullWidth || false; 10843 item.position = item.position || 'top'; 10844 item.weight = item.weight || 0; 10845 10846 chart.boxes.push(item); 10847 }, 10848 10849 /** 10850 * Remove a layoutItem from a chart 10851 * @param {Chart} chart - the chart to remove the box from 10852 * @param {Object} layoutItem - the item to remove from the layout 10853 */ 10854 removeBox: function(chart, layoutItem) { 10855 var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; 10856 if (index !== -1) { 10857 chart.boxes.splice(index, 1); 10858 } 10859 }, 10860 10861 /** 10862 * Sets (or updates) options on the given `item`. 10863 * @param {Chart} chart - the chart in which the item lives (or will be added to) 10864 * @param {Object} item - the item to configure with the given options 10865 * @param {Object} options - the new item options. 10866 */ 10867 configure: function(chart, item, options) { 10868 var props = ['fullWidth', 'position', 'weight']; 10869 var ilen = props.length; 10870 var i = 0; 10871 var prop; 10872 10873 for (; i < ilen; ++i) { 10874 prop = props[i]; 10875 if (options.hasOwnProperty(prop)) { 10876 item[prop] = options[prop]; 10877 } 10878 } 10879 }, 10880 10881 /** 10882 * Fits boxes of the given chart into the given size by having each box measure itself 10883 * then running a fitting algorithm 10884 * @param {Chart} chart - the chart 10885 * @param {Number} width - the width to fit into 10886 * @param {Number} height - the height to fit into 10887 */ 10888 update: function(chart, width, height) { 10889 if (!chart) { 10890 return; 10891 } 10892 10893 var layoutOptions = chart.options.layout || {}; 10894 var padding = helpers.options.toPadding(layoutOptions.padding); 10895 var leftPadding = padding.left; 10896 var rightPadding = padding.right; 10897 var topPadding = padding.top; 10898 var bottomPadding = padding.bottom; 10899 10900 var leftBoxes = filterByPosition(chart.boxes, 'left'); 10901 var rightBoxes = filterByPosition(chart.boxes, 'right'); 10902 var topBoxes = filterByPosition(chart.boxes, 'top'); 10903 var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); 10904 var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); 10905 10906 // Sort boxes by weight. A higher weight is further away from the chart area 10907 sortByWeight(leftBoxes, true); 10908 sortByWeight(rightBoxes, false); 10909 sortByWeight(topBoxes, true); 10910 sortByWeight(bottomBoxes, false); 10911 10912 // Essentially we now have any number of boxes on each of the 4 sides. 10913 // Our canvas looks like the following. 10914 // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and 10915 // B1 is the bottom axis 10916 // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays 10917 // These locations are single-box locations only, when trying to register a chartArea location that is already taken, 10918 // an error will be thrown. 10919 // 10920 // |----------------------------------------------------| 10921 // | T1 (Full Width) | 10922 // |----------------------------------------------------| 10923 // | | | T2 | | 10924 // | |----|-------------------------------------|----| 10925 // | | | C1 | | C2 | | 10926 // | | |----| |----| | 10927 // | | | | | 10928 // | L1 | L2 | ChartArea (C0) | R1 | 10929 // | | | | | 10930 // | | |----| |----| | 10931 // | | | C3 | | C4 | | 10932 // | |----|-------------------------------------|----| 10933 // | | | B1 | | 10934 // |----------------------------------------------------| 10935 // | B2 (Full Width) | 10936 // |----------------------------------------------------| 10937 // 10938 // What we do to find the best sizing, we do the following 10939 // 1. Determine the minimum size of the chart area. 10940 // 2. Split the remaining width equally between each vertical axis 10941 // 3. Split the remaining height equally between each horizontal axis 10942 // 4. Give each layout the maximum size it can be. The layout will return it's minimum size 10943 // 5. Adjust the sizes of each axis based on it's minimum reported size. 10944 // 6. Refit each axis 10945 // 7. Position each axis in the final location 10946 // 8. Tell the chart the final location of the chart area 10947 // 9. Tell any axes that overlay the chart area the positions of the chart area 10948 10949 // Step 1 10950 var chartWidth = width - leftPadding - rightPadding;
10951 var chartHeight = height - topPadding - bottomPadding; 10952 var chartAreaWidth = chartWidth / 2; // min 50% 10953 var chartAreaHeight = chartHeight / 2; // min 50% 10954 10955 // Step 2 10956 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); 10957 10958 // Step 3 10959 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); 10960 10961 // Step 4 10962 var maxChartAreaWidth = chartWidth; 10963 var maxChartAreaHeight = chartHeight; 10964 var minBoxSizes = []; 10965 10966 function getMinimumBoxSize(box) { 10967 var minSize; 10968 var isHorizontal = box.isHorizontal(); 10969 10970 if (isHorizontal) { 10971 minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); 10972 maxChartAreaHeight -= minSize.height; 10973 } else { 10974 minSize = box.update(verticalBoxWidth, maxChartAreaHeight); 10975 maxChartAreaWidth -= minSize.width; 10976 } 10977 10978 minBoxSizes.push({ 10979 horizontal: isHorizontal, 10980 minSize: minSize, 10981 box: box, 10982 }); 10983 } 10984 10985 helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); 10986 10987 // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) 10988 var maxHorizontalLeftPadding = 0; 10989 var maxHorizontalRightPadding = 0; 10990 var maxVerticalTopPadding = 0; 10991 var maxVerticalBottomPadding = 0; 10992 10993 helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { 10994 if (horizontalBox.getPadding) { 10995 var boxPadding = horizontalBox.getPadding(); 10996 maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); 10997 maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); 10998 } 10999 }); 11000 11001 helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { 11002 if (verticalBox.getPadding) { 11003 var boxPadding = verticalBox.getPadding(); 11004 maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); 11005 maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); 11006 } 11007 }); 11008 11009 // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could 11010 // be if the axes are drawn at their minimum sizes. 11011 // Steps 5 & 6 11012 var totalLeftBoxesWidth = leftPadding; 11013 var totalRightBoxesWidth = rightPadding; 11014 var totalTopBoxesHeight = topPadding; 11015 var totalBottomBoxesHeight = bottomPadding; 11016 11017 // Function to fit a box 11018 function fitBox(box) { 11019 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { 11020 return minBox.box === box; 11021 }); 11022 11023 if (minBoxSize) { 11024 if (box.isHorizontal()) { 11025 var scaleMargin = { 11026 left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), 11027 right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), 11028 top: 0, 11029 bottom: 0 11030 }; 11031 11032 // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends 11033 // on the margin. Sometimes they need to increase in size slightly 11034 box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); 11035 } else { 11036 box.update(minBoxSize.minSize.width, maxChartAreaHeight); 11037 } 11038 } 11039 } 11040 11041 // Update, and calculate the left and right margins for the horizontal boxes 11042 helpers.each(leftBoxes.concat(rightBoxes), fitBox); 11043 11044 helpers.each(leftBoxes, function(box) { 11045 totalLeftBoxesWidth += box.width; 11046 }); 11047 11048 helpers.each(rightBoxes, function(box) { 11049 totalRightBoxesWidth += box.width; 11050 }); 11051 11052 // Set the Left and Right margins for the horizontal boxes 11053 helpers.each(topBoxes.concat(bottomBoxes), fitBox); 11054 11055 // Figure out how much margin is on the top and bottom of the vertical boxes 11056 helpers.each(topBoxes, function(box) { 11057 totalTopBoxesHeight += box.height; 11058 }); 11059 11060 helpers.each(bottomBoxes, function(box) { 11061 totalBottomBoxesHeight += box.height; 11062 }); 11063 11064 function finalFitVerticalBox(box) { 11065 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { 11066 return minSize.box === box; 11067 }); 11068 11069 var scaleMargin = { 11070 left: 0, 11071 right: 0, 11072 top: totalTopBoxesHeight, 11073 bottom: totalBottomBoxesHeight 11074 }; 11075 11076 if (minBoxSize) { 11077 box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); 11078 } 11079 } 11080 11081 // Let the left layout know the final margin 11082 helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); 11083 11084 // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) 11085 totalLeftBoxesWidth = leftPadding; 11086 totalRightBoxesWidth = rightPadding;
11087 totalTopBoxesHeight = topPadding; 11088 totalBottomBoxesHeight = bottomPadding; 11089 11090 helpers.each(leftBoxes, function(box) { 11091 totalLeftBoxesWidth += box.width; 11092 }); 11093 11094 helpers.each(rightBoxes, function(box) { 11095 totalRightBoxesWidth += box.width; 11096 }); 11097 11098 helpers.each(topBoxes, function(box) { 11099 totalTopBoxesHeight += box.height; 11100 }); 11101 helpers.each(bottomBoxes, function(box) { 11102 totalBottomBoxesHeight += box.height; 11103 }); 11104 11105 // We may be adding some padding to account for rotated x axis labels 11106 var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); 11107 totalLeftBoxesWidth += leftPaddingAddition; 11108 totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); 11109 11110 var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); 11111 totalTopBoxesHeight += topPaddingAddition; 11112 totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); 11113 11114 // Figure out if our chart area changed. This would occur if the dataset layout label rotation 11115 // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do 11116 // without calling `fit` again 11117 var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; 11118 var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; 11119 11120 if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { 11121 helpers.each(leftBoxes, function(box) { 11122 box.height = newMaxChartAreaHeight; 11123 }); 11124 11125 helpers.each(rightBoxes, function(box) { 11126 box.height = newMaxChartAreaHeight; 11127 }); 11128 11129 helpers.each(topBoxes, function(box) { 11130 if (!box.fullWidth) { 11131 box.width = newMaxChartAreaWidth; 11132 } 11133 }); 11134 11135 helpers.each(bottomBoxes, function(box) { 11136 if (!box.fullWidth) { 11137 box.width = newMaxChartAreaWidth; 11138 } 11139 }); 11140 11141 maxChartAreaHeight = newMaxChartAreaHeight; 11142 maxChartAreaWidth = newMaxChartAreaWidth; 11143 } 11144 11145 // Step 7 - Position the boxes 11146 var left = leftPadding + leftPaddingAddition; 11147 var top = topPadding + topPaddingAddition; 11148 11149 function placeBox(box) { 11150 if (box.isHorizontal()) { 11151 box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; 11152 box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; 11153 box.top = top; 11154 box.bottom = top + box.height; 11155 11156 // Move to next point 11157 top = box.bottom; 11158 11159 } else { 11160 11161 box.left = left; 11162 box.right = left + box.width;
11163 box.top = totalTopBoxesHeight; 11164 box.bottom = totalTopBoxesHeight + maxChartAreaHeight; 11165 11166 // Move to next point 11167 left = box.right; 11168 } 11169 } 11170 11171 helpers.each(leftBoxes.concat(topBoxes), placeBox); 11172 11173 // Account for chart width and height 11174 left += maxChartAreaWidth; 11175 top += maxChartAreaHeight; 11176 11177 helpers.each(rightBoxes, placeBox); 11178 helpers.each(bottomBoxes, placeBox); 11179 11180 // Step 8 11181 chart.chartArea = { 11182 left: totalLeftBoxesWidth, 11183 top: totalTopBoxesHeight, 11184 right: totalLeftBoxesWidth + maxChartAreaWidth, 11185 bottom: totalTopBoxesHeight + maxChartAreaHeight 11186 }; 11187 11188 // Step 9 11189 helpers.each(chartAreaBoxes, function(box) { 11190 box.left = chart.chartArea.left; 11191 box.top = chart.chartArea.top; 11192 box.right = chart.chartArea.right; 11193 box.bottom = chart.chartArea.bottom; 11194 11195 box.update(maxChartAreaWidth, maxChartAreaHeight); 11196 }); 11197 } 11198}; 11199 11200},{"46":46}],32:[function(require,module,exports){ 11201'use strict'; 11202 11203var defaults = require(26); 11204var helpers = require(46); 11205 11206defaults._set('global', { 11207 plugins: {} 11208}); 11209 11210/** 11211 * The plugin service singleton 11212 * @namespace Chart.plugins 11213 * @since 2.1.0 11214 */ 11215module.exports = { 11216 /** 11217 * Globally registered plugins. 11218 * @private 11219 */ 11220 _plugins: [], 11221 11222 /** 11223 * This identifier is used to invalidate the descriptors cache attached to each chart 11224 * when a global plugin is registered or unregistered. In this case, the cache ID is 11225 * incremented and descriptors are regenerated during following API calls. 11226 * @private 11227 */ 11228 _cacheId: 0, 11229 11230 /** 11231 * Registers the given plugin(s) if not already registered. 11232 * @param {Array|Object} plugins plugin instance(s). 11233 */ 11234 register: function(plugins) { 11235 var p = this._plugins; 11236 ([]).concat(plugins).forEach(function(plugin) { 11237 if (p.indexOf(plugin) === -1) { 11238 p.push(plugin); 11239 } 11240 }); 11241 11242 this._cacheId++; 11243 }, 11244 11245 /** 11246 * Unregisters the given plugin(s) only if registered. 11247 * @param {Array|Object} plugins plugin instance(s). 11248 */ 11249 unregister: function(plugins) { 11250 var p = this._plugins; 11251 ([]).concat(plugins).forEach(function(plugin) { 11252 var idx = p.indexOf(plugin); 11253 if (idx !== -1) { 11254 p.splice(idx, 1); 11255 } 11256 }); 11257 11258 this._cacheId++; 11259 }, 11260 11261 /** 11262 * Remove all registered plugins. 11263 * @since 2.1.5 11264 */ 11265 clear: function() { 11266 this._plugins = []; 11267 this._cacheId++; 11268 }, 11269 11270 /** 11271 * Returns the number of registered plugins? 11272 * @returns {Number} 11273 * @since 2.1.5 11274 */ 11275 count: function() { 11276 return this._plugins.length; 11277 }, 11278 11279 /** 11280 * Returns all registered plugin instances. 11281 * @returns {Array} array of plugin objects. 11282 * @since 2.1.5 11283 */ 11284 getAll: function() { 11285 return this._plugins; 11286 }, 11287 11288 /** 11289 * Calls enabled plugins for `chart` on the specified hook and with the given args. 11290 * This method immediately returns as soon as a plugin explicitly returns false. The 11291 * returned value can be used, for instance, to interrupt the current action. 11292 * @param {Object} chart - The chart instance for which plugins should be called. 11293 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). 11294 * @param {Array} [args] - Extra arguments to apply to the hook call. 11295 * @returns {Boolean} false if any of the plugins return false, else returns true. 11296 */ 11297 notify: function(chart, hook, args) { 11298 var descriptors = this.descriptors(chart); 11299 var ilen = descriptors.length; 11300 var i, descriptor, plugin, params, method; 11301 11302 for (i = 0; i < ilen; ++i) { 11303 descriptor = descriptors[i]; 11304 plugin = descriptor.plugin; 11305 method = plugin[hook]; 11306 if (typeof method === 'function') { 11307 params = [chart].concat(args || []); 11308 params.push(descriptor.options); 11309 if (method.apply(plugin, params) === false) { 11310 return false; 11311 } 11312 } 11313 } 11314 11315 return true; 11316 }, 11317 11318 /** 11319 * Returns descriptors of enabled plugins for the given chart. 11320 * @returns {Array} [{ plugin, options }] 11321 * @private 11322 */ 11323 descriptors: function(chart) { 11324 var cache = chart.$plugins || (chart.$plugins = {}); 11325 if (cache.id === this._cacheId) { 11326 return cache.descriptors; 11327 } 11328 11329 var plugins = []; 11330 var descriptors = []; 11331 var config = (chart && chart.config) || {}; 11332 var options = (config.options && config.options.plugins) || {}; 11333
vendor: 9,411 bytes, lines 11334-11562
11334 this._plugins.concat(config.plugins || []).forEach(function(plugin) { 11335 var idx = plugins.indexOf(plugin); 11336 if (idx !== -1) { 11337 return; 11338 } 11339 11340 var id = plugin.id; 11341 var opts = options[id]; 11342 if (opts === false) { 11343 return; 11344 } 11345 11346 if (opts === true) { 11347 opts = helpers.clone(defaults.global.plugins[id]); 11348 } 11349 11350 plugins.push(plugin); 11351 descriptors.push({ 11352 plugin: plugin, 11353 options: opts || {} 11354 }); 11355 }); 11356 11357 cache.descriptors = descriptors; 11358 cache.id = this._cacheId; 11359 return descriptors; 11360 }, 11361 11362 /** 11363 * Invalidates cache for the given chart: descriptors hold a reference on plugin option, 11364 * but in some cases, this reference can be changed by the user when updating options. 11365 * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 11366 * @private 11367 */ 11368 _invalidate: function(chart) { 11369 delete chart.$plugins; 11370 } 11371}; 11372 11373/** 11374 * Plugin extension hooks. 11375 * @interface IPlugin 11376 * @since 2.1.0 11377 */ 11378/** 11379 * @method IPlugin#beforeInit 11380 * @desc Called before initializing `chart`. 11381 * @param {Chart.Controller} chart - The chart instance. 11382 * @param {Object} options - The plugin options. 11383 */ 11384/** 11385 * @method IPlugin#afterInit 11386 * @desc Called after `chart` has been initialized and before the first update. 11387 * @param {Chart.Controller} chart - The chart instance. 11388 * @param {Object} options - The plugin options. 11389 */ 11390/** 11391 * @method IPlugin#beforeUpdate 11392 * @desc Called before updating `chart`. If any plugin returns `false`, the update 11393 * is cancelled (and thus subsequent render(s)) until another `update` is triggered. 11394 * @param {Chart.Controller} chart - The chart instance. 11395 * @param {Object} options - The plugin options. 11396 * @returns {Boolean} `false` to cancel the chart update. 11397 */ 11398/** 11399 * @method IPlugin#afterUpdate 11400 * @desc Called after `chart` has been updated and before rendering. Note that this 11401 * hook will not be called if the chart update has been previously cancelled. 11402 * @param {Chart.Controller} chart - The chart instance. 11403 * @param {Object} options - The plugin options. 11404 */ 11405/** 11406 * @method IPlugin#beforeDatasetsUpdate 11407 * @desc Called before updating the `chart` datasets. If any plugin returns `false`, 11408 * the datasets update is cancelled until another `update` is triggered. 11409 * @param {Chart.Controller} chart - The chart instance. 11410 * @param {Object} options - The plugin options. 11411 * @returns {Boolean} false to cancel the datasets update. 11412 * @since version 2.1.5 11413*/ 11414/** 11415 * @method IPlugin#afterDatasetsUpdate 11416 * @desc Called after the `chart` datasets have been updated. Note that this hook 11417 * will not be called if the datasets update has been previously cancelled. 11418 * @param {Chart.Controller} chart - The chart instance. 11419 * @param {Object} options - The plugin options. 11420 * @since version 2.1.5 11421 */ 11422/** 11423 * @method IPlugin#beforeDatasetUpdate 11424 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin 11425 * returns `false`, the datasets update is cancelled until another `update` is triggered. 11426 * @param {Chart} chart - The chart instance. 11427 * @param {Object} args - The call arguments. 11428 * @param {Number} args.index - The dataset index. 11429 * @param {Object} args.meta - The dataset metadata. 11430 * @param {Object} options - The plugin options. 11431 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11432 */ 11433/** 11434 * @method IPlugin#afterDatasetUpdate 11435 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note 11436 * that this hook will not be called if the datasets update has been previously cancelled. 11437 * @param {Chart} chart - The chart instance. 11438 * @param {Object} args - The call arguments. 11439 * @param {Number} args.index - The dataset index. 11440 * @param {Object} args.meta - The dataset metadata. 11441 * @param {Object} options - The plugin options. 11442 */ 11443/** 11444 * @method IPlugin#beforeLayout 11445 * @desc Called before laying out `chart`. If any plugin returns `false`, 11446 * the layout update is cancelled until another `update` is triggered. 11447 * @param {Chart.Controller} chart - The chart instance. 11448 * @param {Object} options - The plugin options. 11449 * @returns {Boolean} `false` to cancel the chart layout. 11450 */ 11451/** 11452 * @method IPlugin#afterLayout 11453 * @desc Called after the `chart` has been layed out. Note that this hook will not 11454 * be called if the layout update has been previously cancelled. 11455 * @param {Chart.Controller} chart - The chart instance. 11456 * @param {Object} options - The plugin options. 11457 */ 11458/** 11459 * @method IPlugin#beforeRender 11460 * @desc Called before rendering `chart`. If any plugin returns `false`, 11461 * the rendering is cancelled until another `render` is triggered. 11462 * @param {Chart.Controller} chart - The chart instance. 11463 * @param {Object} options - The plugin options. 11464 * @returns {Boolean} `false` to cancel the chart rendering. 11465 */ 11466/** 11467 * @method IPlugin#afterRender 11468 * @desc Called after the `chart` has been fully rendered (and animation completed). Note 11469 * that this hook will not be called if the rendering has been previously cancelled. 11470 * @param {Chart.Controller} chart - The chart instance. 11471 * @param {Object} options - The plugin options. 11472 */ 11473/** 11474 * @method IPlugin#beforeDraw 11475 * @desc Called before drawing `chart` at every animation frame specified by the given 11476 * easing value. If any plugin returns `false`, the frame drawing is cancelled until 11477 * another `render` is triggered. 11478 * @param {Chart.Controller} chart - The chart instance. 11479 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11480 * @param {Object} options - The plugin options. 11481 * @returns {Boolean} `false` to cancel the chart drawing. 11482 */ 11483/** 11484 * @method IPlugin#afterDraw 11485 * @desc Called after the `chart` has been drawn for the specific easing value. Note 11486 * that this hook will not be called if the drawing has been previously cancelled. 11487 * @param {Chart.Controller} chart - The chart instance. 11488 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11489 * @param {Object} options - The plugin options. 11490 */ 11491/** 11492 * @method IPlugin#beforeDatasetsDraw 11493 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, 11494 * the datasets drawing is cancelled until another `render` is triggered. 11495 * @param {Chart.Controller} chart - The chart instance. 11496 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11497 * @param {Object} options - The plugin options. 11498 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11499 */ 11500/** 11501 * @method IPlugin#afterDatasetsDraw 11502 * @desc Called after the `chart` datasets have been drawn. Note that this hook 11503 * will not be called if the datasets drawing has been previously cancelled. 11504 * @param {Chart.Controller} chart - The chart instance. 11505 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11506 * @param {Object} options - The plugin options. 11507 */ 11508/** 11509 * @method IPlugin#beforeDatasetDraw 11510 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets 11511 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing 11512 * is cancelled until another `render` is triggered. 11513 * @param {Chart} chart - The chart instance. 11514 * @param {Object} args - The call arguments. 11515 * @param {Number} args.index - The dataset index. 11516 * @param {Object} args.meta - The dataset metadata. 11517 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11518 * @param {Object} options - The plugin options. 11519 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11520 */ 11521/** 11522 * @method IPlugin#afterDatasetDraw 11523 * @desc Called after the `chart` datasets at the given `args.index` have been drawn 11524 * (datasets are drawn in the reverse order). Note that this hook will not be called 11525 * if the datasets drawing has been previously cancelled. 11526 * @param {Chart} chart - The chart instance. 11527 * @param {Object} args - The call arguments. 11528 * @param {Number} args.index - The dataset index. 11529 * @param {Object} args.meta - The dataset metadata. 11530 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11531 * @param {Object} options - The plugin options. 11532 */ 11533/** 11534 * @method IPlugin#beforeTooltipDraw 11535 * @desc Called before drawing the `tooltip`. If any plugin returns `false`, 11536 * the tooltip drawing is cancelled until another `render` is triggered. 11537 * @param {Chart} chart - The chart instance. 11538 * @param {Object} args - The call arguments. 11539 * @param {Object} args.tooltip - The tooltip. 11540 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11541 * @param {Object} options - The plugin options. 11542 * @returns {Boolean} `false` to cancel the chart tooltip drawing. 11543 */ 11544/** 11545 * @method IPlugin#afterTooltipDraw 11546 * @desc Called after drawing the `tooltip`. Note that this hook will not 11547 * be called if the tooltip drawing has been previously cancelled. 11548 * @param {Chart} chart - The chart instance. 11549 * @param {Object} args - The call arguments. 11550 * @param {Object} args.tooltip - The tooltip. 11551 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11552 * @param {Object} options - The plugin options. 11553 */ 11554/** 11555 * @method IPlugin#beforeEvent 11556 * @desc Called before processing the specified `event`. If any plugin returns `false`, 11557 * the event will be discarded. 11558 * @param {Chart.Controller} chart - The chart instance. 11559 * @param {IEvent} event - The event object. 11560 * @param {Object} options - The plugin options. 11561 */ 11562/**
11563 * @method IPlugin#afterEvent 11564 * @desc Called after the `event` has been consumed. Note that this hook 11565 * will not be called if the `event` has been previously discarded. 11566 * @param {Chart.Controller} chart - The chart instance. 11567 * @param {IEvent} event - The event object. 11568 * @param {Object} options - The plugin options. 11569 */ 11570/** 11571 * @method IPlugin#resize 11572 * @desc Called after the chart as been resized. 11573 * @param {Chart.Controller} chart - The chart instance. 11574 * @param {Number} size - The new canvas display size (eq. canvas.style width & height). 11575 * @param {Object} options - The plugin options. 11576 */ 11577/** 11578 * @method IPlugin#destroy 11579 * @desc Called after the chart as been destroyed. 11580 * @param {Chart.Controller} chart - The chart instance. 11581 * @param {Object} options - The plugin options. 11582 */ 11583 11584},{"26":26,"46":46}],33:[function(require,module,exports){ 11585'use strict'; 11586 11587var defaults = require(26); 11588var Element = require(27); 11589var helpers = require(46); 11590var Ticks = require(35); 11591 11592defaults._set('scale', { 11593 display: true, 11594 position: 'left', 11595 offset: false, 11596 11597 // grid line settings 11598 gridLines: { 11599 display: true, 11600 color: 'rgba(0, 0, 0, 0.1)', 11601 lineWidth: 1, 11602 drawBorder: true, 11603 drawOnChartArea: true, 11604 drawTicks: true, 11605 tickMarkLength: 10, 11606 zeroLineWidth: 1, 11607 zeroLineColor: 'rgba(0,0,0,0.25)', 11608 zeroLineBorderDash: [], 11609 zeroLineBorderDashOffset: 0.0, 11610 offsetGridLines: false, 11611 borderDash: [], 11612 borderDashOffset: 0.0 11613 }, 11614 11615 // scale label 11616 scaleLabel: { 11617 // display property 11618 display: false, 11619 11620 // actual label 11621 labelString: '', 11622 11623 // line height 11624 lineHeight: 1.2, 11625 11626 // top/bottom padding 11627 padding: { 11628 top: 4, 11629 bottom: 4 11630 } 11631 }, 11632 11633 // label settings 11634 ticks: { 11635 beginAtZero: false, 11636 minRotation: 0, 11637 maxRotation: 50, 11638 mirror: false, 11639 padding: 0, 11640 reverse: false, 11641 display: true, 11642 autoSkip: true, 11643 autoSkipPadding: 0, 11644 labelOffset: 0, 11645 // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. 11646 callback: Ticks.formatters.values, 11647 minor: {}, 11648 major: {} 11649 } 11650}); 11651 11652function labelsFromTicks(ticks) { 11653 var labels = []; 11654 var i, ilen; 11655 11656 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 11657 labels.push(ticks[i].label); 11658 } 11659 11660 return labels; 11661} 11662 11663function getLineValue(scale, index, offsetGridLines) { 11664 var lineValue = scale.getPixelForTick(index); 11665 11666 if (offsetGridLines) { 11667 if (index === 0) { 11668 lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; 11669 } else { 11670 lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; 11671 } 11672 } 11673 return lineValue; 11674} 11675 11676function computeTextSize(context, tick, font) { 11677 return helpers.isArray(tick) ? 11678 helpers.longestText(context, font, tick) : 11679 context.measureText(tick).width; 11680} 11681 11682function parseFontOptions(options) { 11683 var valueOrDefault = helpers.valueOrDefault; 11684 var globalDefaults = defaults.global; 11685 var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); 11686 var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); 11687 var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); 11688 11689 return { 11690 size: size, 11691 style: style, 11692 family: family, 11693 font: helpers.fontString(size, style, family) 11694 }; 11695} 11696 11697function parseLineHeight(options) { 11698 return helpers.options.toLineHeight( 11699 helpers.valueOrDefault(options.lineHeight, 1.2), 11700 helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); 11701} 11702 11703module.exports = Element.extend({ 11704 /** 11705 * Get the padding needed for the scale 11706 * @method getPadding 11707 * @private 11708 * @returns {Padding} the necessary padding 11709 */ 11710 getPadding: function() { 11711 var me = this; 11712 return { 11713 left: me.paddingLeft || 0, 11714 top: me.paddingTop || 0, 11715 right: me.paddingRight || 0, 11716 bottom: me.paddingBottom || 0 11717 }; 11718 }, 11719 11720 /** 11721 * Returns the scale tick objects ({label, major}) 11722 * @since 2.7 11723 */ 11724 getTicks: function() { 11725 return this._ticks; 11726 }, 11727 11728 // These methods are ordered by lifecyle. Utilities then follow. 11729 // Any function defined here is inherited by all scale types. 11730 // Any function can be extended by the scale type 11731 11732 mergeTicksOptions: function() { 11733 var ticks = this.options.ticks; 11734 if (ticks.minor === false) { 11735 ticks.minor = { 11736 display: false 11737 }; 11738 } 11739 if (ticks.major === false) { 11740 ticks.major = { 11741 display: false 11742 }; 11743 } 11744 for (var key in ticks) { 11745 if (key !== 'major' && key !== 'minor') { 11746 if (typeof ticks.minor[key] === 'undefined') { 11747 ticks.minor[key] = ticks[key]; 11748 } 11749 if (typeof ticks.major[key] === 'undefined') { 11750 ticks.major[key] = ticks[key]; 11751 } 11752 } 11753 } 11754 }, 11755 beforeUpdate: function() { 11756 helpers.callback(this.options.beforeUpdate, [this]); 11757 }, 11758 11759 update: function(maxWidth, maxHeight, margins) { 11760 var me = this; 11761 var i, ilen, labels, label, ticks, tick; 11762 11763 // Update Lifecycle - Probably don't want to ever extend or over
11763write this function ;) 11764 me.beforeUpdate(); 11765 11766 // Absorb the master measurements 11767 me.maxWidth = maxWidth; 11768 me.maxHeight = maxHeight; 11769 me.margins = helpers.extend({ 11770 left: 0, 11771 right: 0, 11772 top: 0, 11773 bottom: 0 11774 }, margins); 11775 me.longestTextCache = me.longestTextCache || {}; 11776 11777 // Dimensions 11778 me.beforeSetDimensions(); 11779 me.setDimensions(); 11780 me.afterSetDimensions(); 11781 11782 // Data min/max 11783 me.beforeDataLimits(); 11784 me.determineDataLimits(); 11785 me.afterDataLimits(); 11786 11787 // Ticks - `this.ticks` is now DEPRECATED! 11788 // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member 11789 // and must not be accessed directly from outside this class. `this.ticks` being 11790 // around for long time and not marked as private, we can't change its structure 11791 // without unexpected breaking changes. If you need to access the scale ticks, 11792 // use scale.getTicks() instead. 11793 11794 me.beforeBuildTicks(); 11795 11796 // New implementations should return an array of objects but for BACKWARD COMPAT, 11797 // we still support no return (`this.ticks` internally set by calling this method). 11798 ticks = me.buildTicks() || []; 11799 11800 me.afterBuildTicks(); 11801 11802 me.beforeTickToLabelConversion(); 11803 11804 // New implementations should return the formatted tick labels but for BACKWARD 11805 // COMPAT, we still support no return (`this.ticks` internally changed by calling 11806 // this method and supposed to contain only string values). 11807 labels = me.convertTicksToLabels(ticks) || me.ticks; 11808 11809 me.afterTickToLabelConversion(); 11810 11811 me.ticks = labels; // BACKWARD COMPATIBILITY 11812 11813 // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! 11814 11815 // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) 11816 for (i = 0, ilen = labels.length; i < ilen; ++i) { 11817 label = labels[i]; 11818 tick = ticks[i]; 11819 if (!tick) { 11820 ticks.push(tick = { 11821 label: label, 11822 major: false 11823 }); 11824 } else { 11825 tick.label = label; 11826 } 11827 } 11828 11829 me._ticks = ticks; 11830 11831 // Tick Rotation 11832 me.beforeCalculateTickRotation(); 11833 me.calculateTickRotation(); 11834 me.afterCalculateTickRotation(); 11835 // Fit 11836 me.beforeFit(); 11837 me.fit(); 11838 me.afterFit(); 11839 // 11840 me.afterUpdate(); 11841 11842 return me.minSize; 11843 11844 }, 11845 afterUpdate: function() { 11846 helpers.callback(this.options.afterUpdate, [this]); 11847 }, 11848 11849 // 11850 11851 beforeSetDimensions: function() { 11852 helpers.callback(this.options.beforeSetDimensions, [this]); 11853 }, 11854 setDimensions: function() { 11855 var me = this; 11856 // Set the unconstrained dimension before label rotation 11857 if (me.isHorizontal()) { 11858 // Reset position before calculating rotation 11859 me.width = me.maxWidth; 11860 me.left = 0; 11861 me.right = me.width; 11862 } else { 11863 me.height = me.maxHeight; 11864 11865 // Reset position before calculating rotation 11866 me.top = 0; 11867 me.bottom = me.height; 11868 } 11869 11870 // Reset padding 11871 me.paddingLeft = 0; 11872 me.paddingTop = 0; 11873 me.paddingRight = 0; 11874 me.paddingBottom = 0; 11875 }, 11876 afterSetDimensions: function() { 11877 helpers.callback(this.options.afterSetDimensions, [this]); 11878 }, 11879 11880 // Data limits 11881 beforeDataLimits: function() { 11882 helpers.callback(this.options.beforeDataLimits, [this]); 11883 }, 11884 determineDataLimits: helpers.noop, 11885 afterDataLimits: function() { 11886 helpers.callback(this.options.afterDataLimits, [this]); 11887 }, 11888 11889 // 11890 beforeBuildTicks: function() { 11891 helpers.callback(this.options.beforeBuildTicks, [this]); 11892 }, 11893 buildTicks: helpers.noop, 11894 afterBuildTicks: function() { 11895 helpers.callback(this.options.afterBuildTicks, [this]); 11896 }, 11897 11898 beforeTickToLabelConversion: function() { 11899 helpers.callback(this.options.beforeTickToLabelConversion, [this]); 11900 }, 11901 convertTicksToLabels: function() { 11902 var me = this; 11903 // Convert ticks to strings 11904 var tickOpts = me.options.ticks; 11905 me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); 11906 }, 11907 afterTickToLabelConversion: function() { 11908 helpers.callback(this.options.afterTickToLabelConversion, [this]); 11909 }, 11910 11911 // 11912 11913 beforeCalculateTickRotation: function() { 11914 helpers.callback(this.options.beforeCalculateTickRotation, [this]); 11915 }, 11916 calculateTickRotation: function() { 11917 var me = this; 11918 var context = me.ctx; 11919 var tickOpts = me.options.ticks; 11920 var labels = labelsFromTicks(me._ticks); 11921 11922 // Get the width of each grid by calculating the difference 11923 // between x offsets between 0 and 1.
11924 var tickFont = parseFontOptions(tickOpts); 11925 context.font = tickFont.font; 11926 11927 var labelRotation = tickOpts.minRotation || 0; 11928 11929 if (labels.length && me.options.display && me.isHorizontal()) { 11930 var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); 11931 var labelWidth = originalLabelWidth; 11932 var cosRotation, sinRotation; 11933 11934 // Allow 3 pixels x2 padding either side for label readability 11935 var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; 11936 11937 // Max label rotation can be set or default to 90 - also act as a loop counter 11938 while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { 11939 var angleRadians = helpers.toRadians(labelRotation); 11940 cosRotation = Math.cos(angleRadians); 11941 sinRotation = Math.sin(angleRadians); 11942 11943 if (sinRotation * originalLabelWidth > me.maxHeight) { 11944 // go back one step 11945 labelRotation--; 11946 break; 11947 } 11948 11949 labelRotation++; 11950 labelWidth = cosRotation * originalLabelWidth; 11951 } 11952 } 11953 11954 me.labelRotation = labelRotation; 11955 }, 11956 afterCalculateTickRotation: function() { 11957 helpers.callback(this.options.afterCalculateTickRotation, [this]); 11958 }, 11959 11960 // 11961 11962 beforeFit: function() { 11963 helpers.callback(this.options.beforeFit, [this]); 11964 }, 11965 fit: function() { 11966 var me = this; 11967 // Reset 11968 var minSize = me.minSize = { 11969 width: 0, 11970 height: 0 11971 }; 11972 11973 var labels = labelsFromTicks(me._ticks); 11974 11975 var opts = me.options; 11976 var tickOpts = opts.ticks; 11977 var scaleLabelOpts = opts.scaleLabel; 11978 var gridLineOpts = opts.gridLines; 11979 var display = opts.display; 11980 var isHorizontal = me.isHorizontal(); 11981 11982 var tickFont = parseFontOptions(tickOpts); 11983 var tickMarkLength = opts.gridLines.tickMarkLength; 11984 11985 // Width 11986 if (isHorizontal) { 11987 // subtract the margins to line up with the chartArea if we are a full width scale 11988 minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; 11989 } else { 11990 minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; 11991 } 11992 11993 // height 11994 if (isHorizontal) { 11995 minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; 11996 } else { 11997 minSize.height = me.maxHeight; // fill all the height 11998 } 11999 12000 // Are we showing a title for the scale? 12001 if (scaleLabelOpts.display && display) { 12002 var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); 12003 var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); 12004 var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; 12005 12006 if (isHorizontal) { 12007 minSize.height += deltaHeight; 12008 } else { 12009 minSize.width += deltaHeight; 12010 } 12011 } 12012 12013 // Don't bother fitting the ticks if we are not showing them 12014 if (tickOpts.display && display) { 12015 var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); 12016 var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); 12017 var lineSpace = tickFont.size * 0.5; 12018 var tickPadding = me.options.ticks.padding; 12019 12020 if (isHorizontal) { 12021 // A horizontal axis is more constrained by the height. 12022 me.longestLabelWidth = largestTextWidth; 12023 12024 var angleRadians = helpers.toRadians(me.labelRotation); 12025 var cosRotation = Math.cos(angleRadians); 12026 var sinRotation = Math.sin(angleRadians); 12027 12028 // TODO - improve this calculation 12029 var labelHeight = (sinRotation * largestTextWidth) 12030 + (tickFont.size * tallestLabelHeightInLines) 12031 + (lineSpace * (tallestLabelHeightInLines - 1)) 12032 + lineSpace; // padding 12033 12034 minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); 12035 12036 me.ctx.font = tickFont.font; 12037 var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); 12038 var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); 12039 12040 // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned 12041 // which means that the right padding is dominated by the font height 12042 if (me.labelRotation !== 0) { 12043 me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges 12044 me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; 12045 } else {
12046 me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges 12047 me.paddingRight = lastLabelWidth / 2 + 3; 12048 } 12049 } else { 12050 // A vertical axis is more constrained by the width. Labels are the 12051 // dominant factor here, so get that length first and account for padding 12052 if (tickOpts.mirror) { 12053 largestTextWidth = 0; 12054 } else { 12055 // use lineSpace for consistency with horizontal axis 12056 // tickPadding is not implemented for horizontal 12057 largestTextWidth += tickPadding + lineSpace; 12058 } 12059 12060 minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); 12061 12062 me.paddingTop = tickFont.size / 2; 12063 me.paddingBottom = tickFont.size / 2; 12064 } 12065 } 12066 12067 me.handleMargins(); 12068 12069 me.width = minSize.width; 12070 me.height = minSize.height; 12071 }, 12072 12073 /** 12074 * Handle margins and padding interactions 12075 * @private 12076 */ 12077 handleMargins: function() { 12078 var me = this; 12079 if (me.margins) { 12080 me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); 12081 me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); 12082 me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); 12083 me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); 12084 } 12085 }, 12086 12087 afterFit: function() { 12088 helpers.callback(this.options.afterFit, [this]); 12089 }, 12090 12091 // Shared Methods 12092 isHorizontal: function() { 12093 return this.options.position === 'top' || this.options.position === 'bottom'; 12094 }, 12095 isFullWidth: function() { 12096 return (this.options.fullWidth); 12097 }, 12098 12099 // 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 12100 getRightValue: function(rawValue) { 12101 // Null and undefined values first 12102 if (helpers.isNullOrUndef(rawValue)) { 12103 return NaN; 12104 } 12105 // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values 12106 if (typeof rawValue === 'number' && !isFinite(rawValue)) { 12107 return NaN; 12108 } 12109 // If it is in fact an object, dive in one more level 12110 if (rawValue) { 12111 if (this.isHorizontal()) { 12112 if (rawValue.x !== undefined) { 12113 return this.getRightValue(rawValue.x); 12114 } 12115 } else if (rawValue.y !== undefined) { 12116 return this.getRightValue(rawValue.y); 12117 } 12118 } 12119 12120 // Value is good, return it 12121 return rawValue; 12122 }, 12123 12124 /** 12125 * Used to get the value to display in the tooltip for the data at the given index 12126 * @param index 12127 * @param datasetIndex 12128 */ 12129 getLabelForIndex: helpers.noop, 12130 12131 /** 12132 * Returns the location of the given data point. Value can either be an index or a numerical value 12133 * The coordinate (0, 0) is at the upper-left corner of the canvas 12134 * @param value 12135 * @param index 12136 * @param datasetIndex 12137 */ 12138 getPixelForValue: helpers.noop, 12139 12140 /** 12141 * Used to get the data value from a given pixel. This is the inverse of getPixelForValue 12142 * The coordinate (0, 0) is at the upper-left corner of the canvas 12143 * @param pixel 12144 */ 12145 getValueForPixel: helpers.noop, 12146 12147 /** 12148 * Returns the location of the tick at the given index 12149 * The coordinate (0, 0) is at the upper-left corner of the canvas 12150 */ 12151 getPixelForTick: function(index) { 12152 var me = this; 12153 var offset = me.options.offset; 12154 if (me.isHorizontal()) { 12155 var innerWidth = me.width - (me.paddingLeft + me.paddingRight); 12156 var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); 12157 var pixel = (tickWidth * index) + me.paddingLeft; 12158 12159 if (offset) { 12160 pixel += tickWidth / 2; 12161 } 12162 12163 var finalVal = me.left + Math.round(pixel); 12164 finalVal += me.isFullWidth() ? me.margins.left : 0; 12165 return finalVal; 12166 } 12167 var innerHeight = me.height - (me.paddingTop + me.paddingBottom); 12168 return me.top + (index * (innerHeight / (me._ticks.length - 1))); 12169 }, 12170 12171 /** 12172 * Utility for getting the pixel location of a percentage of scale 12173 * The coordinate (0, 0) is at the upper-left corner of the canvas 12174 */ 12175 getPixelForDecimal: function(decimal) { 12176 var me = this; 12177 if (me.isHorizontal()) { 12178 var innerWidth = me.width - (me.paddingLeft + me.paddingRight); 12179 var valueOffset = (innerWidth * decimal) + me.paddingLeft; 12180 12181 var finalVal = me.left + Math.round(valueOffset); 12182 finalVal += me.isFullWidth() ? me.margins.left : 0; 12183 return finalVal; 12184 } 12185 return me.top + (decimal * me.height); 12186 }, 12187 12188 /** 12189 * Returns the pixel for the minimum chart value 12190 * The coordinate (0, 0) is at the upper-left corner of the canvas 12191 */ 12192 getBasePixel: function() { 12193 return this.getPixelForValue(this.getBaseValue()); 12194 }, 12195 12196 getBaseValue: function() { 12197 var me = this; 12198 var min = me.min; 12199 var max = me.max; 12200 12201 return me.beginAtZero ? 0 : 12202 min < 0 && max < 0 ? max : 12203 min > 0 && max > 0 ? min : 12204 0; 12205 }, 12206 12207 /** 12208 * Returns a subset of ticks to be plotted to avoid overlapping labels. 12209 * @private 12210 */ 12211 _autoSkip: function(ticks) { 12212 var skipRatio; 12213 var me = this; 12214 var isHorizontal = me.isHorizontal(); 12215 var optionTicks = me.options.ticks.minor; 12216 var tickCount = ticks.length; 12217 var labelRotationRadians = helpers.toRadians(me.labelRotation); 12218 var cosRotation = Math.cos(labelRotationRadians); 12219 var longestRotatedLabel = me.longestLabelWidth * cosRotation; 12220 var result = []; 12221 var i, tick, shouldSkip; 12222
12223 // figure out the maximum number of gridlines to show 12224 var maxTicks; 12225 if (optionTicks.maxTicksLimit) { 12226 maxTicks = optionTicks.maxTicksLimit; 12227 } 12228 12229 if (isHorizontal) { 12230 skipRatio = false; 12231 12232 if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { 12233 skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); 12234 } 12235 12236 // if they defined a max number of optionTicks, 12237 // increase skipRatio until that number is met 12238 if (maxTicks && tickCount > maxTicks) { 12239 skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); 12240 } 12241 } 12242 12243 for (i = 0; i < tickCount; i++) { 12244 tick = ticks[i]; 12245 12246 // Since we always show the last tick,we need may need to hide the last shown one before 12247 shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); 12248 if (shouldSkip && i !== tickCount - 1) { 12249 // leave tick in place but make sure it's not displayed (#4635) 12250 delete tick.label; 12251 } 12252 result.push(tick); 12253 } 12254 return result; 12255 }, 12256 12257 // Actually draw the scale on the canvas 12258 // @param {rectangle} chartArea : the area of the chart to draw full grid lines on 12259 draw: function(chartArea) { 12260 var me = this; 12261 var options = me.options; 12262 if (!options.display) { 12263 return; 12264 } 12265 12266 var context = me.ctx; 12267 var globalDefaults = defaults.global; 12268 var optionTicks = options.ticks.minor; 12269 var optionMajorTicks = options.ticks.major || optionTicks; 12270 var gridLines = options.gridLines; 12271 var scaleLabel = options.scaleLabel; 12272 12273 var isRotated = me.labelRotation !== 0; 12274 var isHorizontal = me.isHorizontal(); 12275 12276 var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); 12277 var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); 12278 var tickFont = parseFontOptions(optionTicks); 12279 var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); 12280 var majorTickFont = parseFontOptions(optionMajorTicks); 12281 12282 var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; 12283 12284 var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); 12285 var scaleLabelFont = parseFontOptions(scaleLabel); 12286 var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); 12287 var labelRotationRadians = helpers.toRadians(me.labelRotation); 12288 12289 var itemsToDraw = []; 12290 12291 var axisWidth = me.options.gridLines.lineWidth; 12292 var xTickStart = options.position === 'right' ? me.left : me.right - axisWidth - tl; 12293 var xTickEnd = options.position === 'right' ? me.left + tl : me.right; 12294 var yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth; 12295 var yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth; 12296 12297 helpers.each(ticks, function(tick, index) { 12298 // autoskipper skipped this tick (#4635) 12299 if (helpers.isNullOrUndef(tick.label)) { 12300 return; 12301 } 12302 12303 var label = tick.label; 12304 var lineWidth, lineColor, borderDash, borderDashOffset; 12305 if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { 12306 // Draw the first index specially 12307 lineWidth = gridLines.zeroLineWidth; 12308 lineColor = gridLines.zeroLineColor; 12309 borderDash = gridLines.zeroLineBorderDash; 12310 borderDashOffset = gridLines.zeroLineBorderDashOffset; 12311 } else { 12312 lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); 12313 lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); 12314 borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); 12315 borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); 12316 } 12317 12318 // Common properties 12319 var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; 12320 var textAlign = 'middle'; 12321 var textBaseline = 'middle'; 12322 var tickPadding = optionTicks.padding; 12323 12324 if (isHorizontal) { 12325 var labelYOffset = tl + tickPadding; 12326 12327 if (options.position === 'bottom') { 12328 // bottom 12329 textBaseline = !isRotated ? 'top' : 'middle'; 12330 textAlign = !isRotated ? 'center' : 'right'; 12331 labelY = me.top + labelYOffset; 12332 } else { 12333 // top 12334 textBaseline = !isRotated ? 'bottom' : 'middle'; 12335 textAlign = !isRotated ? 'center' : 'left'; 12336 labelY = me.bottom - labelYOffset; 12337 } 12338 12339 var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); 12340 if (xLineValue < me.left) { 12341 lineColor = 'rgba(0,0,0,0)'; 12342 } 12343 xLineValue += helpers.aliasPixel(lineWidth); 12344 12345 labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) 12346 12347 tx1 = tx2 = x1 = x2 = xLineValue; 12348 ty1 = yTickStart; 12349 ty2 = yTickEnd; 12350 y1 = chartArea.top; 12351 y2 = chartArea.bottom + axisWidth; 12352 } else { 12353 var isLeft = options.position === 'left'; 12354 var labelXOffset; 12355 12356 if (optionTicks.mirror) { 12357 textAlign = isLeft ? 'left' : 'right'; 12358 labelXOffset = tickPadding; 12359 } else { 12360 textAlign = isLeft ? 'right' : 'left'; 12361 labelXOffset = tl + tickPadding; 12362 } 12363 12364 labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; 12365 12366 var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); 12367 if (yLineValue < me.top) { 12368 lineColor = 'rgba(0,0,0,0)'; 12369 } 12370 yLineValue += helpers.aliasPixel(lineWidth); 12371 12372 labelY = me.getPixelForTick(index) + optionTicks.labelOffset; 12373 12374 tx1 = xTickStart; 12375 tx2 = xTickEnd; 12376 x1 = chartArea.left; 12377 x2 = chartArea.right + axisWidth; 12378 ty1 = ty2 = y1 = y2 = yLineValue; 12379 } 12380 12381 itemsToDraw.push({ 12382 tx1: tx1, 12383 ty1: ty1, 12384 tx2: tx2, 12385 ty2: ty2, 12386 x1: x1, 12387 y1: y1, 12388 x2: x2, 12389 y2: y2, 12390 labelX: labelX, 12391 labelY: labelY, 12392 glWidth: lineWidth, 12393 glColor: lineColor, 12394 glBorderDash: borderDash, 12395 glBorderDashOffset: borderDashOffset, 12396 rotation: -1 * labelRotationRadians, 12397 label: label, 12398 major: tick.major, 12399 textBaseline: textBaseline, 12400 textAlign: textAlign 12401 }); 12402 }); 12403 12404 // Draw all of the tick labels, tick marks, and grid lines at the correct places 12405 helpers.each(itemsToDraw, function(itemToDraw) { 12406 if (gridLines.display) { 12407 context.save(); 12408 context.lineWidth = itemToDraw.glWidth; 12409 context.strokeStyle = itemToDraw.glColor; 12410 if (context.setLineDash) { 12411 context.setLineDash(itemToDraw.glBorderDash); 12412 context.lineDashOffset = itemToDraw.glBorderDashOffset; 12413 } 12414 12415 context.beginPath(); 12416 12417 if (gridLines.drawTicks) { 12418 context.moveTo(itemToDraw.tx1, itemToDraw.ty1); 12419 context.lineTo(itemToDraw.tx2, itemToDraw.ty2); 12420 } 12421 12422 if (gridLines.drawOnChartArea) { 12423 context.moveTo(itemToDraw.x1, itemToDraw.y1); 12424 context.lineTo(itemToDraw.x2, itemToDraw.y2); 12425 } 12426 12427 context.stroke();
12428 context.restore(); 12429 } 12430 12431 if (optionTicks.display) { 12432 // Make sure we draw text in the correct color and font 12433 context.save(); 12434 context.translate(itemToDraw.labelX, itemToDraw.labelY); 12435 context.rotate(itemToDraw.rotation); 12436 context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; 12437 context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; 12438 context.textBaseline = itemToDraw.textBaseline; 12439 context.textAlign = itemToDraw.textAlign; 12440 12441 var label = itemToDraw.label; 12442 if (helpers.isArray(label)) { 12443 var lineCount = label.length; 12444 var lineHeight = tickFont.size * 1.5; 12445 var y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2; 12446 12447 for (var i = 0; i < lineCount; ++i) { 12448 // We just make sure the multiline element is a string here.. 12449 context.fillText('' + label[i], 0, y); 12450 // apply same lineSpacing as calculated @ L#320 12451 y += lineHeight; 12452 } 12453 } else { 12454 context.fillText(label, 0, 0); 12455 } 12456 context.restore(); 12457 } 12458 }); 12459 12460 if (scaleLabel.display) { 12461 // Draw the scale label 12462 var scaleLabelX; 12463 var scaleLabelY; 12464 var rotation = 0; 12465 var halfLineHeight = parseLineHeight(scaleLabel) / 2; 12466 12467 if (isHorizontal) { 12468 scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width 12469 scaleLabelY = options.position === 'bottom' 12470 ? me.bottom - halfLineHeight - scaleLabelPadding.bottom 12471 : me.top + halfLineHeight + scaleLabelPadding.top; 12472 } else { 12473 var isLeft = options.position === 'left'; 12474 scaleLabelX = isLeft 12475 ? me.left + halfLineHeight + scaleLabelPadding.top 12476 : me.right - halfLineHeight - scaleLabelPadding.top; 12477 scaleLabelY = me.top + ((me.bottom - me.top) / 2); 12478 rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; 12479 } 12480 12481 context.save(); 12482 context.translate(scaleLabelX, scaleLabelY); 12483 context.rotate(rotation); 12484 context.textAlign = 'center'; 12485 context.textBaseline = 'middle'; 12486 context.fillStyle = scaleLabelFontColor; // render in correct colour 12487 context.font = scaleLabelFont.font;
12488 context.fillText(scaleLabel.labelString, 0, 0); 12489 context.restore(); 12490 } 12491 12492 if (gridLines.drawBorder) { 12493 // Draw the line at the edge of the axis 12494 context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); 12495 context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); 12496 var x1 = me.left; 12497 var x2 = me.right + axisWidth; 12498 var y1 = me.top; 12499 var y2 = me.bottom + axisWidth; 12500 12501 var aliasPixel = helpers.aliasPixel(context.lineWidth); 12502 if (isHorizontal) { 12503 y1 = y2 = options.position === 'top' ? me.bottom : me.top; 12504 y1 += aliasPixel; 12505 y2 += aliasPixel; 12506 } else { 12507 x1 = x2 = options.position === 'left' ? me.right : me.left; 12508 x1 += aliasPixel; 12509 x2 += aliasPixel; 12510 } 12511 12512 context.beginPath(); 12513 context.moveTo(x1, y1); 12514 context.lineTo(x2, y2); 12515 context.stroke(); 12516 } 12517 } 12518}); 12519 12520},{"26":26,"27":27,"35":35,"46":46}],34:[function(require,module,exports){ 12521'use strict'; 12522 12523var defaults = require(26); 12524var helpers = require(46); 12525var layouts = require(31); 12526 12527module.exports = { 12528 // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then 12529 // use the new chart options to grab the correct scale 12530 constructors: {}, 12531 // Use a registration function so that we can move to an ES6 map when we no longer need to support 12532 // old browsers 12533 12534 // Scale config defaults 12535 defaults: {}, 12536 registerScaleType: function(type, scaleConstructor, scaleDefaults) { 12537 this.constructors[type] = scaleConstructor; 12538 this.defaults[type] = helpers.clone(scaleDefaults); 12539 }, 12540 getScaleConstructor: function(type) { 12541 return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; 12542 }, 12543 getScaleDefaults: function(type) { 12544 // Return the scale defaults merged with the global settings so that we always use the latest ones 12545 return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; 12546 }, 12547 updateScaleDefaults: function(type, additions) { 12548 var me = this; 12549 if (me.defaults.hasOwnProperty(type)) { 12550 me.defaults[type] = helpers.extend(me.defaults[type], additions); 12551 } 12552 }, 12553 addScalesToLayout: function(chart) { 12554 // Adds each scale to the chart.boxes array to be sized accordingly 12555 helpers.each(chart.scales, function(scale) { 12556 // Set ILayoutItem parameters for backwards compatibility 12557 scale.fullWidth = scale.options.fullWidth; 12558 scale.position = scale.options.position; 12559 scale.weight = scale.options.weight; 12560 layouts.addBox(chart, scale); 12561 }); 12562 } 12563}; 12564 12565},{"26":26,"31":31,"46":46}],35:[function(require,module,exports){ 12566'use strict'; 12567 12568var helpers = require(46); 12569 12570/** 12571 * Namespace to hold static tick generation functions 12572 * @namespace Chart.Ticks 12573 */ 12574module.exports = { 12575 /** 12576 * Namespace to hold formatters for different types of ticks 12577 * @namespace Chart.Ticks.formatters 12578 */ 12579 formatters: { 12580 /** 12581 * Formatter for value labels 12582 * @method Chart.Ticks.formatters.values 12583 * @param value the value to display 12584 * @return {String|Array} the label to display 12585 */ 12586 values: function(value) { 12587 return helpers.isArray(value) ? value : '' + value; 12588 }, 12589 12590 /** 12591 * Formatter for linear numeric ticks 12592 * @method Chart.Ticks.formatters.linear 12593 * @param tickValue {Number} the value to be formatted 12594 * @param index {Number} the position of the tickValue parameter in the ticks array 12595 * @param ticks {Array<Number>} the list of ticks being converted 12596 * @return {String} string representation of the tickValue parameter 12597 */ 12598 linear: function(tickValue, index, ticks) { 12599 // If we have lots of ticks, don't use the ones 12600 var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; 12601 12602 // If we have a number like 2.5 as the delta, figure out how many decimal places we need 12603 if (Math.abs(delta) > 1) { 12604 if (tickValue !== Math.floor(tickValue)) { 12605 // not an integer 12606 delta = tickValue - Math.floor(tickValue); 12607 } 12608 } 12609 12610 var logDelta = helpers.log10(Math.abs(delta)); 12611 var tickString = ''; 12612 12613 if (tickValue !== 0) { 12614 var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1])); 12615 if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation 12616 var logTick = helpers.log10(Math.abs(tickValue)); 12617 tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta)); 12618 } else { 12619 var numDecimal = -1 * Math.floor(logDelta); 12620 numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places 12621 tickString = tickValue.toFixed(numDecimal); 12622 } 12623 } else { 12624 tickString = '0'; // never show decimal places for 0 12625 } 12626 12627 return tickString; 12628 }, 12629 12630 logarithmic: function(tickValue, index, ticks) { 12631 var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); 12632 12633 if (tickValue === 0) { 12634 return '0'; 12635 } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { 12636 return tickValue.toExponential(); 12637 } 12638 return ''; 12639 } 12640 } 12641}; 12642 12643},{"46":46}],36:[function(require,module,exports){ 12644'use strict'; 12645 12646var defaults = require(26); 12647var Element = require(27); 12648var helpers = require(46); 12649 12650defaults._set('global', { 12651 tooltips: { 12652 enabled: true, 12653 custom: null, 12654 mode: 'nearest', 12655 position: 'average', 12656 intersect: true, 12657 backgroundColor: 'rgba(0,0,0,0.8)', 12658 titleFontStyle: 'bold', 12659 titleSpacing: 2, 12660 titleMarginBottom: 6, 12661 titleFontColor: '#fff', 12662 titleAlign: 'left', 12663 bodySpacing: 2, 12664 bodyFontColor: '#fff', 12665 bodyAlign: 'left', 12666 footerFontStyle: 'bold', 12667 footerSpacing: 2, 12668 footerMarginTop: 6, 12669 footerFontColor: '#fff', 12670 footerAlign: 'left', 12671 yPadding: 6, 12672 xPadding: 6, 12673 caretPadding: 2, 12674 caretSize: 5, 12675 cornerRadius: 6,
12676 multiKeyBackground: '#fff', 12677 displayColors: true, 12678 borderColor: 'rgba(0,0,0,0)', 12679 borderWidth: 0, 12680 callbacks: { 12681 // Args are: (tooltipItems, data) 12682 beforeTitle: helpers.noop, 12683 title: function(tooltipItems, data) { 12684 // Pick first xLabel for now 12685 var title = ''; 12686 var labels = data.labels; 12687 var labelCount = labels ? labels.length : 0; 12688 12689 if (tooltipItems.length > 0) { 12690 var item = tooltipItems[0]; 12691 12692 if (item.xLabel) { 12693 title = item.xLabel; 12694 } else if (labelCount > 0 && item.index < labelCount) { 12695 title = labels[item.index]; 12696 } 12697 } 12698 12699 return title; 12700 }, 12701 afterTitle: helpers.noop, 12702 12703 // Args are: (tooltipItems, data) 12704 beforeBody: helpers.noop, 12705 12706 // Args are: (tooltipItem, data) 12707 beforeLabel: helpers.noop, 12708 label: function(tooltipItem, data) { 12709 var label = data.datasets[tooltipItem.datasetIndex].label || ''; 12710 12711 if (label) { 12712 label += ': '; 12713 } 12714 label += tooltipItem.yLabel; 12715 return label; 12716 }, 12717 labelColor: function(tooltipItem, chart) { 12718 var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); 12719 var activeElement = meta.data[tooltipItem.index]; 12720 var view = activeElement._view; 12721 return { 12722 borderColor: view.borderColor, 12723 backgroundColor: view.backgroundColor 12724 }; 12725 }, 12726 labelTextColor: function() { 12727 return this._options.bodyFontColor; 12728 }, 12729 afterLabel: helpers.noop, 12730 12731 // Args are: (tooltipItems, data) 12732 afterBody: helpers.noop, 12733 12734 // Args are: (tooltipItems, data) 12735 beforeFooter: helpers.noop, 12736 footer: helpers.noop, 12737 afterFooter: helpers.noop 12738 } 12739 } 12740}); 12741 12742var positioners = { 12743 /** 12744 * Average mode places the tooltip at the average position of the elements shown 12745 * @function Chart.Tooltip.positioners.average 12746 * @param elements {ChartElement[]} the elements being displayed in the tooltip 12747 * @returns {Point} tooltip position 12748 */ 12749 average: function(elements) { 12750 if (!elements.length) { 12751 return false; 12752 } 12753 12754 var i, len; 12755 var x = 0; 12756 var y = 0; 12757 var count = 0; 12758 12759 for (i = 0, len = elements.length; i < len; ++i) { 12760 var el = elements[i]; 12761 if (el && el.hasValue()) { 12762 var pos = el.tooltipPosition(); 12763 x += pos.x; 12764 y += pos.y; 12765 ++count; 12766 } 12767 } 12768 12769 return { 12770 x: Math.round(x / count), 12771 y: Math.round(y / count) 12772 }; 12773 }, 12774 12775 /** 12776 * Gets the tooltip position nearest of the item nearest to the event position 12777 * @function Chart.Tooltip.positioners.nearest 12778 * @param elements {Chart.Element[]} the tooltip elements 12779 * @param eventPosition {Point} the position of the event in canvas coordinates 12780 * @returns {Point} the tooltip position 12781 */ 12782 nearest: function(elements, eventPosition) { 12783 var x = eventPosition.x; 12784 var y = eventPosition.y; 12785 var minDistance = Number.POSITIVE_INFINITY; 12786 var i, len, nearestElement; 12787 12788 for (i = 0, len = elements.length; i < len; ++i) { 12789 var el = elements[i]; 12790 if (el && el.hasValue()) { 12791 var center = el.getCenterPoint(); 12792 var d = helpers.distanceBetweenPoints(eventPosition, center); 12793 12794 if (d < minDistance) { 12795 minDistance = d; 12796 nearestElement = el; 12797 } 12798 } 12799 } 12800 12801 if (nearestElement) { 12802 var tp = nearestElement.tooltipPosition(); 12803 x = tp.x; 12804 y = tp.y; 12805 } 12806 12807 return { 12808 x: x, 12809 y: y 12810 }; 12811 } 12812}; 12813 12814/** 12815 * Helper method to merge the opacity into a color 12816 */
vendor: 14,085 bytes, lines 12817-13285
12817function mergeOpacity(colorString, opacity) { 12818 var color = helpers.color(colorString); 12819 return color.alpha(opacity * color.alpha()).rgbaString(); 12820} 12821 12822// Helper to push or concat based on if the 2nd parameter is an array or not 12823function pushOrConcat(base, toPush) { 12824 if (toPush) { 12825 if (helpers.isArray(toPush)) { 12826 // base = base.concat(toPush); 12827 Array.prototype.push.apply(base, toPush); 12828 } else { 12829 base.push(toPush); 12830 } 12831 } 12832 12833 return base; 12834} 12835 12836/** 12837 * Returns array of strings split by newline 12838 * @param {String} value - The value to split by newline. 12839 * @returns {Array} value if newline present - Returned from String split() method 12840 * @function 12841 */ 12842function splitNewlines(str) { 12843 if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) { 12844 return str.split('\n'); 12845 } 12846 return str; 12847} 12848 12849 12850// Private helper to create a tooltip item model 12851// @param element : the chart element (point, arc, bar) to create the tooltip item for 12852// @return : new tooltip item 12853function createTooltipItem(element) { 12854 var xScale = element._xScale; 12855 var yScale = element._yScale || element._scale; // handle radar || polarArea charts 12856 var index = element._index; 12857 var datasetIndex = element._datasetIndex; 12858 12859 return { 12860 xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', 12861 yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', 12862 index: index, 12863 datasetIndex: datasetIndex, 12864 x: element._model.x, 12865 y: element._model.y 12866 }; 12867} 12868 12869/** 12870 * Helper to get the reset model for the tooltip 12871 * @param tooltipOpts {Object} the tooltip options 12872 */ 12873function getBaseModel(tooltipOpts) { 12874 var globalDefaults = defaults.global; 12875 var valueOrDefault = helpers.valueOrDefault; 12876 12877 return { 12878 // Positioning 12879 xPadding: tooltipOpts.xPadding, 12880 yPadding: tooltipOpts.yPadding, 12881 xAlign: tooltipOpts.xAlign, 12882 yAlign: tooltipOpts.yAlign, 12883 12884 // Body 12885 bodyFontColor: tooltipOpts.bodyFontColor, 12886 _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), 12887 _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), 12888 _bodyAlign: tooltipOpts.bodyAlign, 12889 bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), 12890 bodySpacing: tooltipOpts.bodySpacing, 12891 12892 // Title 12893 titleFontColor: tooltipOpts.titleFontColor, 12894 _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), 12895 _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), 12896 titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), 12897 _titleAlign: tooltipOpts.titleAlign, 12898 titleSpacing: tooltipOpts.titleSpacing, 12899 titleMarginBottom: tooltipOpts.titleMarginBottom, 12900 12901 // Footer 12902 footerFontColor: tooltipOpts.footerFontColor, 12903 _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), 12904 _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), 12905 footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), 12906 _footerAlign: tooltipOpts.footerAlign, 12907 footerSpacing: tooltipOpts.footerSpacing, 12908 footerMarginTop: tooltipOpts.footerMarginTop, 12909 12910 // Appearance 12911 caretSize: tooltipOpts.caretSize, 12912 cornerRadius: tooltipOpts.cornerRadius, 12913 backgroundColor: tooltipOpts.backgroundColor, 12914 opacity: 0, 12915 legendColorBackground: tooltipOpts.multiKeyBackground, 12916 displayColors: tooltipOpts.displayColors, 12917 borderColor: tooltipOpts.borderColor, 12918 borderWidth: tooltipOpts.borderWidth 12919 }; 12920} 12921 12922/** 12923 * Get the size of the tooltip 12924 */ 12925function getTooltipSize(tooltip, model) { 12926 var ctx = tooltip._chart.ctx; 12927 12928 var height = model.yPadding * 2; // Tooltip Padding 12929 var width = 0; 12930 12931 // Count of all lines in the body 12932 var body = model.body; 12933 var combinedBodyLength = body.reduce(function(count, bodyItem) { 12934 return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; 12935 }, 0); 12936 combinedBodyLength += model.beforeBody.length + model.afterBody.length; 12937 12938 var titleLineCount = model.title.length; 12939 var footerLineCount = model.footer.length; 12940 var titleFontSize = model.titleFontSize; 12941 var bodyFontSize = model.bodyFontSize; 12942 var footerFontSize = model.footerFontSize; 12943 12944 height += titleLineCount * titleFontSize; // Title Lines 12945 height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing 12946 height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin 12947 height += combinedBodyLength * bodyFontSize; // Body Lines 12948 height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing 12949 height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin 12950 height += footerLineCount * (footerFontSize); // Footer Lines 12951 height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing 12952 12953 // Title width 12954 var widthPadding = 0; 12955 var maxLineWidth = function(line) { 12956 width = Math.max(width, ctx.measureText(line).width + widthPadding); 12957 }; 12958 12959 ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); 12960 helpers.each(model.title, maxLineWidth); 12961 12962 // Body width 12963 ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); 12964 helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); 12965 12966 // Body lines may include some extra width due to the color box 12967 widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; 12968 helpers.each(body, function(bodyItem) { 12969 helpers.each(bodyItem.before, maxLineWidth); 12970 helpers.each(bodyItem.lines, maxLineWidth); 12971 helpers.each(bodyItem.after, maxLineWidth); 12972 }); 12973 12974 // Reset back to 0 12975 widthPadding = 0; 12976 12977 // Footer width 12978 ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); 12979 helpers.each(model.footer, maxLineWidth); 12980 12981 // Add padding 12982 width += 2 * model.xPadding; 12983 12984 return { 12985 width: width, 12986 height: height 12987 }; 12988} 12989 12990/** 12991 * Helper to get the alignment of a tooltip given the size 12992 */ 12993function determineAlignment(tooltip, size) { 12994 var model = tooltip._model; 12995 var chart = tooltip._chart; 12996 var chartArea = tooltip._chart.chartArea; 12997 var xAlign = 'center'; 12998 var yAlign = 'center'; 12999 13000 if (model.y < size.height) { 13001 yAlign = 'top'; 13002 } else if (model.y > (chart.height - size.height)) { 13003 yAlign = 'bottom'; 13004 } 13005 13006 var lf, rf; // functions to determine left, right alignment 13007 var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart 13008 var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges 13009 var midX = (chartArea.left + chartArea.right) / 2; 13010 var midY = (chartArea.top + chartArea.bottom) / 2; 13011 13012 if (yAlign === 'center') { 13013 lf = function(x) { 13014 return x <= midX; 13015 }; 13016 rf = function(x) { 13017 return x > midX; 13018 }; 13019 } else { 13020 lf = function(x) { 13021 return x <= (size.width / 2); 13022 }; 13023 rf = function(x) { 13024 return x >= (chart.width - (size.width / 2)); 13025 }; 13026 } 13027 13028 olf = function(x) { 13029 return x + size.width + model.caretSize + model.caretPadding > chart.width; 13030 }; 13031 orf = function(x) { 13032 return x - size.width - model.caretSize - model.caretPadding < 0; 13033 }; 13034 yf = function(y) { 13035 return y <= midY ? 'top' : 'bottom'; 13036 }; 13037 13038 if (lf(model.x)) { 13039 xAlign = 'left'; 13040 13041 // Is tooltip too wide and goes over the right side of the chart.? 13042 if (olf(model.x)) { 13043 xAlign = 'center'; 13044 yAlign = yf(model.y); 13045 } 13046 } else if (rf(model.x)) { 13047 xAlign = 'right'; 13048 13049 // Is tooltip too wide and goes outside left edge of canvas? 13050 if (orf(model.x)) { 13051 xAlign = 'center'; 13052 yAlign = yf(model.y); 13053 } 13054 } 13055 13056 var opts = tooltip._options; 13057 return { 13058 xAlign: opts.xAlign ? opts.xAlign : xAlign, 13059 yAlign: opts.yAlign ? opts.yAlign : yAlign 13060 }; 13061} 13062 13063/** 13064 * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment 13065 */ 13066function getBackgroundPoint(vm, size, alignment, chart) { 13067 // Background Position 13068 var x = vm.x; 13069 var y = vm.y; 13070 13071 var caretSize = vm.caretSize; 13072 var caretPadding = vm.caretPadding; 13073 var cornerRadius = vm.cornerRadius; 13074 var xAlign = alignment.xAlign; 13075 var yAlign = alignment.yAlign; 13076 var paddingAndSize = caretSize + caretPadding; 13077 var radiusAndPadding = cornerRadius + caretPadding; 13078 13079 if (xAlign === 'right') { 13080 x -= size.width; 13081 } else if (xAlign === 'center') { 13082 x -= (size.width / 2); 13083 if (x + size.width > chart.width) { 13084 x = chart.width - size.width; 13085 } 13086 if (x < 0) { 13087 x = 0; 13088 } 13089 } 13090 13091 if (yAlign === 'top') { 13092 y += paddingAndSize; 13093 } else if (yAlign === 'bottom') { 13094 y -= size.height + paddingAndSize; 13095 } else { 13096 y -= (size.height / 2); 13097 } 13098 13099 if (yAlign === 'center') { 13100 if (xAlign === 'left') { 13101 x += paddingAndSize; 13102 } else if (xAlign === 'right') { 13103 x -= paddingAndSize; 13104 } 13105 } else if (xAlign === 'left') { 13106 x -= radiusAndPadding; 13107 } else if (xAlign === 'right') { 13108 x += radiusAndPadding; 13109 } 13110 13111 return { 13112 x: x, 13113 y: y 13114 }; 13115} 13116 13117/** 13118 * Helper to build before and after body lines 13119 */ 13120function getBeforeAfterBodyLines(callback) { 13121 return pushOrConcat([], splitNewlines(callback)); 13122} 13123 13124var exports = module.exports = Element.extend({ 13125 initialize: function() { 13126 this._model = getBaseModel(this._options); 13127 this._lastActive = []; 13128 }, 13129 13130 // Get the title 13131 // Args are: (tooltipItem, data) 13132 getTitle: function() { 13133 var me = this; 13134 var opts = me._options; 13135 var callbacks = opts.callbacks; 13136 13137 var beforeTitle = callbacks.beforeTitle.apply(me, arguments); 13138 var title = callbacks.title.apply(me, arguments); 13139 var afterTitle = callbacks.afterTitle.apply(me, arguments); 13140 13141 var lines = []; 13142 lines = pushOrConcat(lines, splitNewlines(beforeTitle)); 13143 lines = pushOrConcat(lines, splitNewlines(title)); 13144 lines = pushOrConcat(lines, splitNewlines(afterTitle)); 13145 13146 return lines; 13147 }, 13148 13149 // Args are: (tooltipItem, data) 13150 getBeforeBody: function() { 13151 return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments)); 13152 }, 13153 13154 // Args are: (tooltipItem, data) 13155 getBody: function(tooltipItems, data) { 13156 var me = this; 13157 var callbacks = me._options.callbacks; 13158 var bodyItems = []; 13159 13160 helpers.each(tooltipItems, function(tooltipItem) { 13161 var bodyItem = { 13162 before: [], 13163 lines: [], 13164 after: [] 13165 }; 13166 pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data))); 13167 pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); 13168 pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data))); 13169 13170 bodyItems.push(bodyItem); 13171 }); 13172 13173 return bodyItems; 13174 }, 13175 13176 // Args are: (tooltipItem, data) 13177 getAfterBody: function() { 13178 return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments)); 13179 }, 13180 13181 // Get the footer and beforeFooter and afterFooter lines 13182 // Args are: (tooltipItem, data) 13183 getFooter: function() { 13184 var me = this; 13185 var callbacks = me._options.callbacks; 13186 13187 var beforeFooter = callbacks.beforeFooter.apply(me, arguments); 13188 var footer = callbacks.footer.apply(me, arguments); 13189 var afterFooter = callbacks.afterFooter.apply(me, arguments); 13190 13191 var lines = []; 13192 lines = pushOrConcat(lines, splitNewlines(beforeFooter)); 13193 lines = pushOrConcat(lines, splitNewlines(footer)); 13194 lines = pushOrConcat(lines, splitNewlines(afterFooter)); 13195 13196 return lines; 13197 }, 13198 13199 update: function(changed) { 13200 var me = this; 13201 var opts = me._options; 13202 13203 // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition 13204 // 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 13205 // which breaks any animations. 13206 var existingModel = me._model; 13207 var model = me._model = getBaseModel(opts); 13208 var active = me._active; 13209 13210 var data = me._data; 13211 13212 // In the case where active.length === 0 we need to keep these at existing values for good animations 13213 var alignment = { 13214 xAlign: existingModel.xAlign, 13215 yAlign: existingModel.yAlign 13216 }; 13217 var backgroundPoint = { 13218 x: existingModel.x, 13219 y: existingModel.y 13220 }; 13221 var tooltipSize = { 13222 width: existingModel.width, 13223 height: existingModel.height 13224 }; 13225 var tooltipPosition = { 13226 x: existingModel.caretX, 13227 y: existingModel.caretY 13228 }; 13229 13230 var i, len; 13231 13232 if (active.length) { 13233 model.opacity = 1; 13234 13235 var labelColors = []; 13236 var labelTextColors = []; 13237 tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition); 13238 13239 var tooltipItems = []; 13240 for (i = 0, len = active.length; i < len; ++i) { 13241 tooltipItems.push(createTooltipItem(active[i])); 13242 } 13243 13244 // If the user provided a filter function, use it to modify the tooltip items 13245 if (opts.filter) { 13246 tooltipItems = tooltipItems.filter(function(a) { 13247 return opts.filter(a, data); 13248 }); 13249 } 13250 13251 // If the user provided a sorting function, use it to modify the tooltip items 13252 if (opts.itemSort) { 13253 tooltipItems = tooltipItems.sort(function(a, b) { 13254 return opts.itemSort(a, b, data); 13255 }); 13256 } 13257 13258 // Determine colors for boxes 13259 helpers.each(tooltipItems, function(tooltipItem) { 13260 labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); 13261 labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); 13262 }); 13263 13264 13265 // Build the Text Lines 13266 model.title = me.getTitle(tooltipItems, data); 13267 model.beforeBody = me.getBeforeBody(tooltipItems, data); 13268 model.body = me.getBody(tooltipItems, data); 13269 model.afterBody = me.getAfterBody(tooltipItems, data); 13270 model.footer = me.getFooter(tooltipItems, data); 13271 13272 // Initial positioning and colors 13273 model.x = Math.round(tooltipPosition.x); 13274 model.y = Math.round(tooltipPosition.y); 13275 model.caretPadding = opts.caretPadding; 13276 model.labelColors = labelColors; 13277 model.labelTextColors = labelTextColors; 13278 13279 // data points 13280 model.dataPoints = tooltipItems; 13281 13282 // We need to determine alignment of the tooltip 13283 tooltipSize = getTooltipSize(this, model); 13284 alignment = determineAlignment(this, tooltipSize); 13285 // Final Size and Position
13286 backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); 13287 } else { 13288 model.opacity = 0; 13289 } 13290 13291 model.xAlign = alignment.xAlign; 13292 model.yAlign = alignment.yAlign; 13293 model.x = backgroundPoint.x; 13294 model.y = backgroundPoint.y; 13295 model.width = tooltipSize.width; 13296 model.height = tooltipSize.height; 13297 13298 // Point where the caret on the tooltip points to 13299 model.caretX = tooltipPosition.x; 13300 model.caretY = tooltipPosition.y; 13301 13302 me._model = model; 13303 13304 if (changed && opts.custom) { 13305 opts.custom.call(me, model); 13306 } 13307 13308 return me; 13309 }, 13310 13311 drawCaret: function(tooltipPoint, size) { 13312 var ctx = this._chart.ctx; 13313 var vm = this._view; 13314 var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); 13315 13316 ctx.lineTo(caretPosition.x1, caretPosition.y1); 13317 ctx.lineTo(caretPosition.x2, caretPosition.y2); 13318 ctx.lineTo(caretPosition.x3, caretPosition.y3); 13319 }, 13320 getCaretPosition: function(tooltipPoint, size, vm) { 13321 var x1, x2, x3, y1, y2, y3; 13322 var caretSize = vm.caretSize; 13323 var cornerRadius = vm.cornerRadius; 13324 var xAlign = vm.xAlign; 13325 var yAlign = vm.yAlign; 13326 var ptX = tooltipPoint.x; 13327 var ptY = tooltipPoint.y; 13328 var width = size.width; 13329 var height = size.height; 13330 13331 if (yAlign === 'center') { 13332 y2 = ptY + (height / 2); 13333 13334 if (xAlign === 'left') { 13335 x1 = ptX; 13336 x2 = x1 - caretSize; 13337 x3 = x1; 13338 13339 y1 = y2 + caretSize; 13340 y3 = y2 - caretSize; 13341 } else { 13342 x1 = ptX + width; 13343 x2 = x1 + caretSize; 13344 x3 = x1; 13345 13346 y1 = y2 - caretSize; 13347 y3 = y2 + caretSize; 13348 } 13349 } else { 13350 if (xAlign === 'left') { 13351 x2 = ptX + cornerRadius + (caretSize); 13352 x1 = x2 - caretSize; 13353 x3 = x2 + caretSize; 13354 } else if (xAlign === 'right') { 13355 x2 = ptX + width - cornerRadius - caretSize; 13356 x1 = x2 - caretSize; 13357 x3 = x2 + caretSize; 13358 } else { 13359 x2 = vm.caretX; 13360 x1 = x2 - caretSize; 13361 x3 = x2 + caretSize; 13362 } 13363 if (yAlign === 'top') { 13364 y1 = ptY; 13365 y2 = y1 - caretSize; 13366 y3 = y1; 13367 } else { 13368 y1 = ptY + height; 13369 y2 = y1 + caretSize; 13370 y3 = y1; 13371 // invert drawing order 13372 var tmp = x3; 13373 x3 = x1; 13374 x1 = tmp; 13375 } 13376 } 13377 return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; 13378 }, 13379 13380 drawTitle: function(pt, vm, ctx, opacity) { 13381 var title = vm.title; 13382 13383 if (title.length) { 13384 ctx.textAlign = vm._titleAlign; 13385 ctx.textBaseline = 'top'; 13386 13387 var titleFontSize = vm.titleFontSize; 13388 var titleSpacing = vm.titleSpacing; 13389 13390 ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); 13391 ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); 13392 13393 var i, len; 13394 for (i = 0, len = title.length; i < len; ++i) { 13395 ctx.fillText(title[i], pt.x, pt.y); 13396 pt.y += titleFontSize + titleSpacing; // Line Height and spacing 13397 13398 if (i + 1 === title.length) { 13399 pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing 13400 } 13401 } 13402 } 13403 }, 13404 13405 drawBody: function(pt, vm, ctx, opacity) { 13406 var bodyFontSize = vm.bodyFontSize; 13407 var bodySpacing = vm.bodySpacing; 13408 var body = vm.body; 13409 13410 ctx.textAlign = vm._bodyAlign; 13411 ctx.textBaseline = 'top'; 13412 ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); 13413 13414 // Before Body 13415 var xLinePadding = 0; 13416 var fillLineOfText = function(line) { 13417 ctx.fillText(line, pt.x + xLinePadding, pt.y); 13418 pt.y += bodyFontSize + bodySpacing; 13419 }; 13420 13421 // Before body lines 13422 ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity); 13423 helpers.each(vm.beforeBody, fillLineOfText); 13424 13425 var drawColorBoxes = vm.displayColors; 13426 xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; 13427 13428 // Draw body lines now 13429 helpers.each(body, function(bodyItem, i) { 13430 var textColor = mergeOpacity(vm.labelTextColors[i], opacity); 13431 ctx.fillStyle = textColor; 13432 helpers.each(bodyItem.before, fillLineOfText); 13433 13434 helpers.each(bodyItem.lines, function(line) { 13435 // Draw Legend-like boxes if needed 13436 if (drawColorBoxes) { 13437 // Fill a white rect so that colours merge nicely if the opacity is < 1 13438 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
13439 ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); 13440 13441 // Border 13442 ctx.lineWidth = 1; 13443 ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); 13444 ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); 13445 13446 // Inner square 13447 ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); 13448 ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); 13449 ctx.fillStyle = textColor; 13450 } 13451 13452 fillLineOfText(line); 13453 }); 13454 13455 helpers.each(bodyItem.after, fillLineOfText); 13456 }); 13457 13458 // Reset back to 0 for after body 13459 xLinePadding = 0; 13460 13461 // After body lines 13462 helpers.each(vm.afterBody, fillLineOfText); 13463 pt.y -= bodySpacing; // Remove last body spacing 13464 }, 13465 13466 drawFooter: function(pt, vm, ctx, opacity) { 13467 var footer = vm.footer; 13468 13469 if (footer.length) { 13470 pt.y += vm.footerMarginTop; 13471 13472 ctx.textAlign = vm._footerAlign; 13473 ctx.textBaseline = 'top'; 13474 13475 ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); 13476 ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); 13477 13478 helpers.each(footer, function(line) { 13479 ctx.fillText(line, pt.x, pt.y); 13480 pt.y += vm.footerFontSize + vm.footerSpacing; 13481 }); 13482 } 13483 }, 13484 13485 drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { 13486 ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); 13487 ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); 13488 ctx.lineWidth = vm.borderWidth; 13489 var xAlign = vm.xAlign; 13490 var yAlign = vm.yAlign; 13491 var x = pt.x; 13492 var y = pt.y; 13493 var width = tooltipSize.width; 13494 var height = tooltipSize.height; 13495 var radius = vm.cornerRadius; 13496 13497 ctx.beginPath(); 13498 ctx.moveTo(x + radius, y); 13499 if (yAlign === 'top') { 13500 this.drawCaret(pt, tooltipSize); 13501 } 13502 ctx.lineTo(x + width - radius, y); 13503 ctx.quadraticCurveTo(x + width, y, x + width, y + radius); 13504 if (yAlign === 'center' && xAlign === 'right') { 13505 this.drawCaret(pt, tooltipSize); 13506 } 13507 ctx.lineTo(x + width, y + height - radius); 13508 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); 13509 if (yAlign === 'bottom') { 13510 this.drawCaret(pt, tooltipSize); 13511 } 13512 ctx.lineTo(x + radius, y + height); 13513 ctx.quadraticCurveTo(x, y + height, x, y + height - radius); 13514 if (yAlign === 'center' && xAlign === 'left') { 13515 this.drawCaret(pt, tooltipSize); 13516 } 13517 ctx.lineTo(x, y + radius); 13518 ctx.quadraticCurveTo(x, y, x + radius, y); 13519 ctx.closePath(); 13520 13521 ctx.fill(); 13522 13523 if (vm.borderWidth > 0) { 13524 ctx.stroke(); 13525 } 13526 }, 13527 13528 draw: function() { 13529 var ctx = this._chart.ctx; 13530 var vm = this._view; 13531 13532 if (vm.opacity === 0) { 13533 return; 13534 } 13535 13536 var tooltipSize = { 13537 width: vm.width, 13538 height: vm.height 13539 }; 13540 var pt = { 13541 x: vm.x, 13542 y: vm.y 13543 }; 13544 13545 // IE11/Edge does not like very small opacities, so snap to 0 13546 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; 13547 13548 // Truthy/falsey value for empty tooltip 13549 var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; 13550 13551 if (this._options.enabled && hasTooltipContent) { 13552 // Draw Background 13553 this.drawBackground(pt, vm, ctx, tooltipSize, opacity); 13554 13555 // Draw Title, Body, and Footer 13556 pt.x += vm.xPadding; 13557 pt.y += vm.yPadding; 13558 13559 // Titles 13560 this.drawTitle(pt, vm, ctx, opacity); 13561 13562 // Body 13563 this.drawBody(pt, vm, ctx, opacity); 13564 13565 // Footer 13566 this.drawFooter(pt, vm, ctx, opacity); 13567 } 13568 }, 13569 13570 /** 13571 * Handle an event 13572 * @private 13573 * @param {IEvent} event - The event to handle 13574 * @returns {Boolean} true if the tooltip changed 13575 */ 13576 handleEvent: function(e) { 13577 var me = this; 13578 var options = me._options; 13579 var changed = false; 13580 13581 me._lastActive = me._lastActive || []; 13582 13583 // Find Active Elements for tooltips 13584 if (e.type === 'mouseout') { 13585 me._active = []; 13586 } else { 13587 me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); 13588 } 13589 13590 // Remember Last Actives 13591 changed = !helpers.arrayEquals(me._active, me._lastActive); 13592 13593 // Only handle target event on tooltip change 13594 if (changed) { 13595 me._lastActive = me._active; 13596 13597 if (options.enabled || options.custom) { 13598 me._eventPosition = { 13599 x: e.x, 13600 y: e.y 13601 }; 13602 13603 me.update(true); 13604 me.pivot(); 13605 } 13606 } 13607 13608 return changed; 13609 } 13610}); 13611 13612/** 13613 * @namespace Chart.Tooltip.positioners 13614 */ 13615exports.positioners = positioners; 13616 13617 13618},{"26":26,"27":27,"46":46}],37:[function(require,module,exports){ 13619'use strict'; 13620 13621var defaults = require(26); 13622var Element = require(27); 13623var helpers = require(46); 13624 13625defaults._set('global', { 13626 elements: { 13627 arc: { 13628 backgroundColor: defaults.global.defaultColor, 13629 borderColor: '#fff', 13630 borderWidth: 2 13631 } 13632 } 13633}); 13634 13635module.exports = Element.extend({ 13636 inLabelRange: function(mouseX) { 13637 var vm = this._view; 13638 13639 if (vm) { 13640 return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); 13641 } 13642 return false;
13643 }, 13644 13645 inRange: function(chartX, chartY) { 13646 var vm = this._view; 13647 13648 if (vm) { 13649 var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); 13650 var angle = pointRelativePosition.angle; 13651 var distance = pointRelativePosition.distance; 13652 13653 // Sanitise angle range 13654 var startAngle = vm.startAngle; 13655 var endAngle = vm.endAngle; 13656 while (endAngle < startAngle) { 13657 endAngle += 2.0 * Math.PI; 13658 } 13659 while (angle > endAngle) { 13660 angle -= 2.0 * Math.PI; 13661 } 13662 while (angle < startAngle) { 13663 angle += 2.0 * Math.PI; 13664 } 13665 13666 // Check if within the range of the open/close angle 13667 var betweenAngles = (angle >= startAngle && angle <= endAngle); 13668 var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); 13669 13670 return (betweenAngles && withinRadius); 13671 } 13672 return false; 13673 }, 13674 13675 getCenterPoint: function() { 13676 var vm = this._view; 13677 var halfAngle = (vm.startAngle + vm.endAngle) / 2; 13678 var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; 13679 return { 13680 x: vm.x + Math.cos(halfAngle) * halfRadius, 13681 y: vm.y + Math.sin(halfAngle) * halfRadius 13682 }; 13683 }, 13684 13685 getArea: function() { 13686 var vm = this._view; 13687 return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); 13688 }, 13689 13690 tooltipPosition: function() { 13691 var vm = this._view; 13692 var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); 13693 var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; 13694 13695 return { 13696 x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), 13697 y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) 13698 }; 13699 }, 13700 13701 draw: function() { 13702 var ctx = this._chart.ctx; 13703 var vm = this._view; 13704 var sA = vm.startAngle; 13705 var eA = vm.endAngle; 13706 13707 ctx.beginPath(); 13708 13709 ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); 13710 ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); 13711 13712 ctx.closePath(); 13713 ctx.strokeStyle = vm.borderColor; 13714 ctx.lineWidth = vm.borderWidth; 13715 13716 ctx.fillStyle = vm.backgroundColor; 13717 13718 ctx.fill(); 13719 ctx.lineJoin = 'bevel'; 13720 13721 if (vm.borderWidth) { 13722 ctx.stroke(); 13723 } 13724 } 13725}); 13726 13727},{"26":26,"27":27,"46":46}],38:[function(require,module,exports){ 13728'use strict'; 13729 13730var defaults = require(26); 13731var Element = require(27); 13732var helpers = require(46); 13733 13734var globalDefaults = defaults.global; 13735 13736defaults._set('global', { 13737 elements: { 13738 line: { 13739 tension: 0.4, 13740 backgroundColor: globalDefaults.defaultColor, 13741 borderWidth: 3, 13742 borderColor: globalDefaults.defaultColor, 13743 borderCapStyle: 'butt', 13744 borderDash: [], 13745 borderDashOffset: 0.0, 13746 borderJoinStyle: 'miter', 13747 capBezierPoints: true, 13748 fill: true, // do we fill in the area between the line and its base axis 13749 } 13750 } 13751}); 13752 13753module.exports = Element.extend({ 13754 draw: function() { 13755 var me = this; 13756 var vm = me._view; 13757 var ctx = me._chart.ctx; 13758 var spanGaps = vm.spanGaps; 13759 var points = me._children.slice(); // clone array 13760 var globalOptionLineElements = globalDefaults.elements.line; 13761 var lastDrawnIndex = -1; 13762 var index, current, previous, currentVM; 13763 13764 // If we are looping, adding the first point again 13765 if (me._loop && points.length) { 13766 points.push(points[0]); 13767 } 13768 13769 ctx.save(); 13770 13771 // Stroke Line Options 13772 ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; 13773 13774 // IE 9 and 10 do not support line dash 13775 if (ctx.setLineDash) { 13776 ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); 13777 } 13778 13779 ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; 13780 ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; 13781 ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; 13782 ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; 13783 13784 // Stroke Line 13785 ctx.beginPath(); 13786 lastDrawnIndex = -1; 13787 13788 for (index = 0; index < points.length; ++index) { 13789 current = points[index]; 13790 previous = helpers.previousItem(points, index); 13791 currentVM = current._view; 13792 13793 // First point moves to it's starting position no matter what 13794 if (index === 0) { 13795 if (!currentVM.skip) { 13796 ctx.moveTo(currentVM.x, currentVM.y); 13797 lastDrawnIndex = index; 13798 } 13799 } else { 13800 previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; 13801 13802 if (!currentVM.skip) { 13803 if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { 13804 // There was a gap and this is the first point after the gap 13805 ctx.moveTo(currentVM.x, currentVM.y); 13806 } else { 13807 // Line to next point 13808 helpers.canvas.lineTo(ctx, previous._view, current._view); 13809 } 13810 lastDrawnIndex = index; 13811 } 13812 } 13813 } 13814 13815 ctx.stroke();
13816 ctx.restore(); 13817 } 13818}); 13819 13820},{"26":26,"27":27,"46":46}],39:[function(require,module,exports){ 13821'use strict'; 13822 13823var defaults = require(26); 13824var Element = require(27); 13825var helpers = require(46); 13826 13827var defaultColor = defaults.global.defaultColor; 13828 13829defaults._set('global', { 13830 elements: { 13831 point: { 13832 radius: 3, 13833 pointStyle: 'circle', 13834 backgroundColor: defaultColor, 13835 borderColor: defaultColor, 13836 borderWidth: 1, 13837 // Hover 13838 hitRadius: 1, 13839 hoverRadius: 4, 13840 hoverBorderWidth: 1 13841 } 13842 } 13843}); 13844 13845function xRange(mouseX) { 13846 var vm = this._view; 13847 return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; 13848} 13849 13850function yRange(mouseY) { 13851 var vm = this._view; 13852 return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; 13853} 13854 13855module.exports = Element.extend({ 13856 inRange: function(mouseX, mouseY) { 13857 var vm = this._view; 13858 return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; 13859 }, 13860 13861 inLabelRange: xRange, 13862 inXRange: xRange, 13863 inYRange: yRange, 13864 13865 getCenterPoint: function() { 13866 var vm = this._view; 13867 return { 13868 x: vm.x, 13869 y: vm.y 13870 }; 13871 }, 13872 13873 getArea: function() { 13874 return Math.PI * Math.pow(this._view.radius, 2); 13875 }, 13876 13877 tooltipPosition: function() { 13878 var vm = this._view; 13879 return { 13880 x: vm.x, 13881 y: vm.y, 13882 padding: vm.radius + vm.borderWidth 13883 }; 13884 }, 13885 13886 draw: function(chartArea) { 13887 var vm = this._view; 13888 var model = this._model; 13889 var ctx = this._chart.ctx; 13890 var pointStyle = vm.pointStyle; 13891 var rotation = vm.rotation; 13892 var radius = vm.radius; 13893 var x = vm.x; 13894 var y = vm.y; 13895 var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) 13896 13897 if (vm.skip) { 13898 return; 13899 } 13900 13901 // Clipping for Points. 13902 if (chartArea === undefined || (model.x >= chartArea.left && chartArea.right * errMargin >= model.x && model.y >= chartArea.top && chartArea.bottom * errMargin >= model.y)) { 13903 ctx.strokeStyle = vm.borderColor || defaultColor; 13904 ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); 13905 ctx.fillStyle = vm.backgroundColor || defaultColor; 13906 helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation); 13907 } 13908 } 13909}); 13910 13911},{"26":26,"27":27,"46":46}],40:[function(require,module,exports){ 13912'use strict'; 13913 13914var defaults = require(26); 13915var Element = require(27); 13916 13917defaults._set('global', { 13918 elements: { 13919 rectangle: { 13920 backgroundColor: defaults.global.defaultColor, 13921 borderColor: defaults.global.defaultColor, 13922 borderSkipped: 'bottom', 13923 borderWidth: 0 13924 } 13925 } 13926}); 13927 13928function isVertical(bar) { 13929 return bar._view.width !== undefined; 13930} 13931 13932/** 13933 * Helper function to get the bounds of the bar regardless of the orientation 13934 * @param bar {Chart.Element.Rectangle} the bar 13935 * @return {Bounds} bounds of the bar 13936 * @private 13937 */ 13938function getBarBounds(bar) { 13939 var vm = bar._view; 13940 var x1, x2, y1, y2; 13941 13942 if (isVertical(bar)) { 13943 // vertical 13944 var halfWidth = vm.width / 2; 13945 x1 = vm.x - halfWidth; 13946 x2 = vm.x + halfWidth; 13947 y1 = Math.min(vm.y, vm.base); 13948 y2 = Math.max(vm.y, vm.base); 13949 } else { 13950 // horizontal bar 13951 var halfHeight = vm.height / 2; 13952 x1 = Math.min(vm.x, vm.base); 13953 x2 = Math.max(vm.x, vm.base); 13954 y1 = vm.y - halfHeight; 13955 y2 = vm.y + halfHeight; 13956 } 13957 13958 return { 13959 left: x1, 13960 top: y1, 13961 right: x2, 13962 bottom: y2 13963 }; 13964} 13965 13966module.exports = Element.extend({ 13967 draw: function() { 13968 var ctx = this._chart.ctx; 13969 var vm = this._view; 13970 var left, right, top, bottom, signX, signY, borderSkipped; 13971 var borderWidth = vm.borderWidth; 13972 13973 if (!vm.horizontal) { 13974 // bar 13975 left = vm.x - vm.width / 2; 13976 right = vm.x + vm.width / 2; 13977 top = vm.y; 13978 bottom = vm.base; 13979 signX = 1; 13980 signY = bottom > top ? 1 : -1; 13981 borderSkipped = vm.borderSkipped || 'bottom'; 13982 } else { 13983 // horizontal bar 13984 left = vm.base; 13985 right = vm.x; 13986 top = vm.y - vm.height / 2; 13987 bottom = vm.y + vm.height / 2; 13988 signX = right > left ? 1 : -1; 13989 signY = 1; 13990 borderSkipped = vm.borderSkipped || 'left'; 13991 } 13992 13993 // Canvas doesn't allow us to stroke inside the width so we can 13994 // adjust the sizes to fit if we're setting a stroke on the line 13995 if (borderWidth) { 13996 // borderWidth shold be less than bar width and bar height. 13997 var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); 13998 borderWidth = borderWidth > barSize ? barSize : borderWidth; 13999 var halfStroke = borderWidth / 2; 14000 // Adjust borderWidth when bar top position is near vm.base(zero). 14001 var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); 14002 var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); 14003 var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); 14004 var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); 14005 // not become a vertical line? 14006 if (borderLeft !== borderRight) { 14007 top = borderTop; 14008 bottom = borderBottom; 14009 } 14010 // not become a horizontal line? 14011 if (borderTop !== borderBottom) { 14012 left = borderLeft; 14013 right = borderRight; 14014 } 14015 } 14016 14017 ctx.beginPath(); 14018 ctx.fillStyle = vm.backgroundColor; 14019 ctx.strokeStyle = vm.borderColor; 14020 ctx.lineWidth = borderWidth; 14021 14022 // Corner points, from bottom-left to bottom-right clockwise 14023 // | 1 2 | 14024 // | 0 3 | 14025 var corners = [ 14026 [left, bottom], 14027 [left, top], 14028 [right, top], 14029 [right, bottom] 14030 ]; 14031 14032 // Find first (starting) corner with fallback to 'bottom' 14033 var borders = ['bottom', 'left', 'top', 'right']; 14034 var startCorner = borders.indexOf(borderSkipped, 0); 14035 if (startCorner === -1) { 14036 startCorner = 0; 14037 } 14038 14039 function cornerAt(index) { 14040 return corners[(startCorner + index) % 4]; 14041 } 14042 14043 // Draw rectangle from 'startCorner' 14044 var corner = cornerAt(0); 14045 ctx.moveTo(corner[0], corner[1]); 14046 14047 for (var i = 1; i < 4; i++) { 14048 corner = cornerAt(i); 14049 ctx.lineTo(corner[0], corner[1]); 14050 } 14051 14052 ctx.fill(); 14053 if (borderWidth) { 14054 ctx.stroke(); 14055 } 14056 }, 14057 14058 height: function() { 14059 var vm = this._view; 14060 return vm.base - vm.y; 14061 }, 14062 14063 inRange: function(mouseX, mouseY) { 14064 var inRange = false;
14065 14066 if (this._view) { 14067 var bounds = getBarBounds(this); 14068 inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; 14069 } 14070 14071 return inRange; 14072 }, 14073 14074 inLabelRange: function(mouseX, mouseY) { 14075 var me = this; 14076 if (!me._view) { 14077 return false; 14078 } 14079 14080 var inRange = false; 14081 var bounds = getBarBounds(me); 14082 14083 if (isVertical(me)) { 14084 inRange = mouseX >= bounds.left && mouseX <= bounds.right; 14085 } else { 14086 inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; 14087 } 14088 14089 return inRange; 14090 }, 14091 14092 inXRange: function(mouseX) { 14093 var bounds = getBarBounds(this); 14094 return mouseX >= bounds.left && mouseX <= bounds.right; 14095 }, 14096 14097 inYRange: function(mouseY) { 14098 var bounds = getBarBounds(this); 14099 return mouseY >= bounds.top && mouseY <= bounds.bottom; 14100 }, 14101 14102 getCenterPoint: function() { 14103 var vm = this._view; 14104 var x, y; 14105 if (isVertical(this)) { 14106 x = vm.x; 14107 y = (vm.y + vm.base) / 2; 14108 } else { 14109 x = (vm.x + vm.base) / 2; 14110 y = vm.y; 14111 } 14112 14113 return {x: x, y: y}; 14114 }, 14115 14116 getArea: function() { 14117 var vm = this._view; 14118 return vm.width * Math.abs(vm.y - vm.base); 14119 }, 14120 14121 tooltipPosition: function() { 14122 var vm = this._view; 14123 return { 14124 x: vm.x, 14125 y: vm.y 14126 }; 14127 } 14128}); 14129 14130},{"26":26,"27":27}],41:[function(require,module,exports){ 14131'use strict'; 14132 14133module.exports = {}; 14134module.exports.Arc = require(37); 14135module.exports.Line = require(38); 14136module.exports.Point = require(39); 14137module.exports.Rectangle = require(40); 14138 14139},{"37":37,"38":38,"39":39,"40":40}],42:[function(require,module,exports){ 14140'use strict'; 14141 14142var helpers = require(43); 14143 14144/** 14145 * @namespace Chart.helpers.canvas 14146 */ 14147var exports = module.exports = { 14148 /** 14149 * Clears the entire canvas associated to the given `chart`. 14150 * @param {Chart} chart - The chart for which to clear the canvas. 14151 */ 14152 clear: function(chart) { 14153 chart.ctx.clearRect(0, 0, chart.width, chart.height); 14154 }, 14155 14156 /** 14157 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a 14158 * given size (width, height) and the same `radius` for all corners. 14159 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. 14160 * @param {Number} x - The x axis of the coordinate for the rectangle starting point. 14161 * @param {Number} y - The y axis of the coordinate for the rectangle starting point. 14162 * @param {Number} width - The rectangle's width. 14163 * @param {Number} height - The rectangle's height. 14164 * @param {Number} radius - The rounded amount (in pixels) for the four corners. 14165 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? 14166 */ 14167 roundedRect: function(ctx, x, y, width, height, radius) { 14168 if (radius) { 14169 // NOTE(SB) `epsilon` helps to prevent minor artifacts appearing 14170 // on Chrome when `r` is exactly half the height or the width. 14171 var epsilon = 0.0000001; 14172 var r = Math.min(radius, (height / 2) - epsilon, (width / 2) - epsilon); 14173 14174 ctx.moveTo(x + r, y); 14175 ctx.lineTo(x + width - r, y); 14176 ctx.arcTo(x + width, y, x + width, y + r, r); 14177 ctx.lineTo(x + width, y + height - r); 14178 ctx.arcTo(x + width, y + height, x + width - r, y + height, r); 14179 ctx.lineTo(x + r, y + height); 14180 ctx.arcTo(x, y + height, x, y + height - r, r); 14181 ctx.lineTo(x, y + r); 14182 ctx.arcTo(x, y, x + r, y, r); 14183 ctx.closePath(); 14184 ctx.moveTo(x, y); 14185 } else { 14186 ctx.rect(x, y, width, height); 14187 } 14188 }, 14189 14190 drawPoint: function(ctx, style, radius, x, y, rotation) { 14191 var type, edgeLength, xOffset, yOffset, height, size; 14192 rotation = rotation || 0; 14193 14194 if (style && typeof style === 'object') { 14195 type = style.toString(); 14196 if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { 14197 ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); 14198 return; 14199 } 14200 } 14201 14202 if (isNaN(radius) || radius <= 0) { 14203 return; 14204 } 14205 14206 ctx.save(); 14207 ctx.translate(x, y); 14208 ctx.rotate(rotation * Math.PI / 180); 14209 ctx.beginPath(); 14210 14211 switch (style) { 14212 // Default includes circle 14213 default: 14214 ctx.arc(0, 0, radius, 0, Math.PI * 2); 14215 ctx.closePath(); 14216 break; 14217 case 'triangle': 14218 edgeLength = 3 * radius / Math.sqrt(3); 14219 height = edgeLength * Math.sqrt(3) / 2; 14220 ctx.moveTo(-edgeLength / 2, height / 3); 14221 ctx.lineTo(edgeLength / 2, height / 3); 14222 ctx.lineTo(0, -2 * height / 3); 14223 ctx.closePath(); 14224 break; 14225 case 'rect': 14226 size = 1 / Math.SQRT2 * radius; 14227 ctx.rect(-size, -size, 2 * size, 2 * size); 14228 break; 14229 case 'rectRounded': 14230 var offset = radius / Math.SQRT2; 14231 var leftX = -offset; 14232 var topY = -offset; 14233 var sideSize = Math.SQRT2 * radius; 14234 14235 // NOTE(SB) the rounded rect implementation changed to use `arcTo` 14236 // instead of `quadraticCurveTo` since it generates better results 14237 // when rect is almost a circle. 0.425 (instead of 0.5) produces 14238 // results visually closer to the previous impl. 14239 this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius * 0.425); 14240 break; 14241 case 'rectRot': 14242 size = 1 / Math.SQRT2 * radius; 14243 ctx.moveTo(-size, 0); 14244 ctx.lineTo(0, size); 14245 ctx.lineTo(size, 0); 14246 ctx.lineTo(0, -size); 14247 ctx.closePath(); 14248 break; 14249 case 'cross': 14250 ctx.moveTo(0, radius); 14251 ctx.lineTo(0, -radius); 14252 ctx.moveTo(-radius, 0); 14253 ctx.lineTo(radius, 0); 14254 break; 14255 case 'crossRot':
14256 xOffset = Math.cos(Math.PI / 4) * radius; 14257 yOffset = Math.sin(Math.PI / 4) * radius; 14258 ctx.moveTo(-xOffset, -yOffset); 14259 ctx.lineTo(xOffset, yOffset); 14260 ctx.moveTo(-xOffset, yOffset); 14261 ctx.lineTo(xOffset, -yOffset); 14262 break; 14263 case 'star': 14264 ctx.moveTo(0, radius); 14265 ctx.lineTo(0, -radius); 14266 ctx.moveTo(-radius, 0); 14267 ctx.lineTo(radius, 0); 14268 xOffset = Math.cos(Math.PI / 4) * radius; 14269 yOffset = Math.sin(Math.PI / 4) * radius; 14270 ctx.moveTo(-xOffset, -yOffset); 14271 ctx.lineTo(xOffset, yOffset); 14272 ctx.moveTo(-xOffset, yOffset); 14273 ctx.lineTo(xOffset, -yOffset); 14274 break; 14275 case 'line': 14276 ctx.moveTo(-radius, 0); 14277 ctx.lineTo(radius, 0); 14278 break; 14279 case 'dash': 14280 ctx.moveTo(0, 0); 14281 ctx.lineTo(radius, 0); 14282 break; 14283 } 14284 14285 ctx.fill(); 14286 ctx.stroke(); 14287 ctx.restore(); 14288 }, 14289 14290 clipArea: function(ctx, area) { 14291 ctx.save(); 14292 ctx.beginPath(); 14293 ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); 14294 ctx.clip(); 14295 }, 14296 14297 unclipArea: function(ctx) { 14298 ctx.restore(); 14299 }, 14300 14301 lineTo: function(ctx, previous, target, flip) { 14302 if (target.steppedLine) { 14303 if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { 14304 ctx.lineTo(previous.x, target.y); 14305 } else { 14306 ctx.lineTo(target.x, previous.y); 14307 } 14308 ctx.lineTo(target.x, target.y); 14309 return; 14310 } 14311 14312 if (!target.tension) { 14313 ctx.lineTo(target.x, target.y); 14314 return; 14315 } 14316 14317 ctx.bezierCurveTo( 14318 flip ? previous.controlPointPreviousX : previous.controlPointNextX, 14319 flip ? previous.controlPointPreviousY : previous.controlPointNextY, 14320 flip ? target.controlPointNextX : target.controlPointPreviousX, 14321 flip ? target.controlPointNextY : target.controlPointPreviousY, 14322 target.x, 14323 target.y); 14324 } 14325}; 14326 14327// DEPRECATIONS 14328 14329/** 14330 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. 14331 * @namespace Chart.helpers.clear 14332 * @deprecated since version 2.7.0 14333 * @todo remove at version 3 14334 * @private 14335 */ 14336helpers.clear = exports.clear; 14337 14338/** 14339 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. 14340 * @namespace Chart.helpers.drawRoundedRectangle 14341 * @deprecated since version 2.7.0 14342 * @todo remove at version 3 14343 * @private 14344 */ 14345helpers.drawRoundedRectangle = function(ctx) { 14346 ctx.beginPath(); 14347 exports.roundedRect.apply(exports, arguments); 14348}; 14349 14350},{"43":43}],43:[function(require,module,exports){ 14351'use strict'; 14352 14353/** 14354 * @namespace Chart.helpers 14355 */ 14356var helpers = { 14357 /** 14358 * An empty function that can be used, for example, for optional callback. 14359 */ 14360 noop: function() {}, 14361 14362 /** 14363 * Returns a unique id, sequentially generated from a global variable. 14364 * @returns {Number} 14365 * @function 14366 */ 14367 uid: (function() { 14368 var id = 0; 14369 return function() { 14370 return id++; 14371 }; 14372 }()), 14373 14374 /** 14375 * Returns true if `value` is neither null nor undefined, else returns false. 14376 * @param {*} value - The value to test. 14377 * @returns {Boolean} 14378 * @since 2.7.0 14379 */ 14380 isNullOrUndef: function(value) { 14381 return value === null || typeof value === 'undefined'; 14382 }, 14383 14384 /** 14385 * Returns true if `value` is an array, else returns false. 14386 * @param {*} value - The value to test. 14387 * @returns {Boolean} 14388 * @function 14389 */ 14390 isArray: Array.isArray ? Array.isArray : function(value) { 14391 return Object.prototype.toString.call(value) === '[object Array]'; 14392 }, 14393 14394 /** 14395 * Returns true if `value` is an object (excluding null), else returns false. 14396 * @param {*} value - The value to test. 14397 * @returns {Boolean} 14398 * @since 2.7.0 14399 */ 14400 isObject: function(value) { 14401 return value !== null && Object.prototype.toString.call(value) === '[object Object]'; 14402 }, 14403 14404 /** 14405 * Returns `value` if defined, else returns `defaultValue`. 14406 * @param {*} value - The value to return if defined. 14407 * @param {*} defaultValue - The value to return if `value` is undefined. 14408 * @returns {*} 14409 */ 14410 valueOrDefault: function(value, defaultValue) { 14411 return typeof value === 'undefined' ? defaultValue : value; 14412 }, 14413 14414 /** 14415 * Returns value at the given `index` in array if defined, else returns `defaultValue`. 14416 * @param {Array} value - The array to lookup for value at `index`. 14417 * @param {Number} index - The index in `value` to lookup for value. 14418 * @param {*} defaultValue - The value to return if `value[index]` is undefined. 14419 * @returns {*} 14420 */ 14421 valueAtIndexOrDefault: function(value, index, defaultValue) { 14422 return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); 14423 }, 14424 14425 /** 14426 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the 14427 * value returned by `fn`. If `fn` is not a function, this method returns undefined. 14428 * @param {Function} fn - The function to call. 14429 * @param {Array|undefined|null} args - The arguments with which `fn` should be called. 14430 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. 14431 * @returns {*} 14432 */ 14433 callback: function(fn, args, thisArg) { 14434 if (fn && typeof fn.call === 'function') { 14435 return fn.apply(thisArg, args); 14436 } 14437 }, 14438 14439 /** 14440 * Note(SB) for performance sake, this method should only be used when loopable type 14441 * is unknown or in none intensive code (not called often and small loopable). Else 14442 * it's preferable to use a regular for() loop and save extra function calls. 14443 * @param {Object|Array} loopable - The object or array to be iterated. 14444 * @param {Function} fn - The function to call for each item. 14445 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. 14446 * @param {Boolean} [reverse] - If true, iterates backward on the loopable. 14447 */ 14448 each: function(loopable, fn, thisArg, reverse) { 14449 var i, len, keys; 14450 if (helpers.isArray(loopable)) { 14451 len = loopable.length; 14452 if (reverse) { 14453 for (i = len - 1; i >= 0; i--) { 14454 fn.call(thisArg, loopable[i], i); 14455 } 14456 } else { 14457 for (i = 0; i < len; i++) { 14458 fn.call(thisArg, loopable[i], i); 14459 } 14460 } 14461 } else if (helpers.isObject(loopable)) { 14462 keys = Object.keys(loopable); 14463 len = keys.length; 14464 for (i = 0; i < len; i++) { 14465 fn.call(thisArg, loopable[keys[i]], keys[i]); 14466 } 14467 } 14468 }, 14469 14470 /** 14471 * Returns true if the `a0` and `a1` arrays have the same content, else returns false. 14472 * @see http://stackoverflow.com/a/14853974 14473 * @param {Array} a0 - The array to compare 14474 * @param {Array} a1 - The array to compare 14475 * @returns {Boolean} 14476 */ 14477 arrayEquals: function(a0, a1) { 14478 var i, ilen, v0, v1; 14479 14480 if (!a0 || !a1 || a0.length !== a1.length) { 14481 return false;
vendor: 10,367 bytes, lines 14482-14935
14482 } 14483 14484 for (i = 0, ilen = a0.length; i < ilen; ++i) { 14485 v0 = a0[i]; 14486 v1 = a1[i]; 14487 14488 if (v0 instanceof Array && v1 instanceof Array) { 14489 if (!helpers.arrayEquals(v0, v1)) { 14490 return false; 14491 } 14492 } else if (v0 !== v1) { 14493 // NOTE: two different object instances will never be equal: {x:20} != {x:20} 14494 return false; 14495 } 14496 } 14497 14498 return true; 14499 }, 14500 14501 /** 14502 * Returns a deep copy of `source` without keeping references on objects and arrays. 14503 * @param {*} source - The value to clone. 14504 * @returns {*} 14505 */ 14506 clone: function(source) { 14507 if (helpers.isArray(source)) { 14508 return source.map(helpers.clone); 14509 } 14510 14511 if (helpers.isObject(source)) { 14512 var target = {}; 14513 var keys = Object.keys(source); 14514 var klen = keys.length; 14515 var k = 0; 14516 14517 for (; k < klen; ++k) { 14518 target[keys[k]] = helpers.clone(source[keys[k]]); 14519 } 14520 14521 return target; 14522 } 14523 14524 return source; 14525 }, 14526 14527 /** 14528 * The default merger when Chart.helpers.merge is called without merger option. 14529 * Note(SB): this method is also used by configMerge and scaleMerge as fallback. 14530 * @private 14531 */ 14532 _merger: function(key, target, source, options) { 14533 var tval = target[key]; 14534 var sval = source[key]; 14535 14536 if (helpers.isObject(tval) && helpers.isObject(sval)) { 14537 helpers.merge(tval, sval, options); 14538 } else { 14539 target[key] = helpers.clone(sval); 14540 } 14541 }, 14542 14543 /** 14544 * Merges source[key] in target[key] only if target[key] is undefined. 14545 * @private 14546 */ 14547 _mergerIf: function(key, target, source) { 14548 var tval = target[key]; 14549 var sval = source[key]; 14550 14551 if (helpers.isObject(tval) && helpers.isObject(sval)) { 14552 helpers.mergeIf(tval, sval); 14553 } else if (!target.hasOwnProperty(key)) { 14554 target[key] = helpers.clone(sval); 14555 } 14556 }, 14557 14558 /** 14559 * Recursively deep copies `source` properties into `target` with the given `options`. 14560 * IMPORTANT: `target` is not cloned and will be updated with `source` properties. 14561 * @param {Object} target - The target object in which all sources are merged into. 14562 * @param {Object|Array(Object)} source - Object(s) to merge into `target`. 14563 * @param {Object} [options] - Merging options: 14564 * @param {Function} [options.merger] - The merge method (key, target, source, options) 14565 * @returns {Object} The `target` object. 14566 */ 14567 merge: function(target, source, options) { 14568 var sources = helpers.isArray(source) ? source : [source]; 14569 var ilen = sources.length; 14570 var merge, i, keys, klen, k; 14571 14572 if (!helpers.isObject(target)) { 14573 return target; 14574 } 14575 14576 options = options || {}; 14577 merge = options.merger || helpers._merger; 14578 14579 for (i = 0; i < ilen; ++i) { 14580 source = sources[i]; 14581 if (!helpers.isObject(source)) { 14582 continue; 14583 } 14584 14585 keys = Object.keys(source); 14586 for (k = 0, klen = keys.length; k < klen; ++k) { 14587 merge(keys[k], target, source, options); 14588 } 14589 } 14590 14591 return target; 14592 }, 14593 14594 /** 14595 * Recursively deep copies `source` properties into `target` *only* if not defined in target. 14596 * IMPORTANT: `target` is not cloned and will be updated with `source` properties. 14597 * @param {Object} target - The target object in which all sources are merged into. 14598 * @param {Object|Array(Object)} source - Object(s) to merge into `target`. 14599 * @returns {Object} The `target` object. 14600 */ 14601 mergeIf: function(target, source) { 14602 return helpers.merge(target, source, {merger: helpers._mergerIf}); 14603 }, 14604 14605 /** 14606 * Applies the contents of two or more objects together into the first object. 14607 * @param {Object} target - The target object in which all objects are merged into. 14608 * @param {Object} arg1 - Object containing additional properties to merge in target. 14609 * @param {Object} argN - Additional objects containing properties to merge in target. 14610 * @returns {Object} The `target` object. 14611 */ 14612 extend: function(target) { 14613 var setFn = function(value, key) { 14614 target[key] = value; 14615 }; 14616 for (var i = 1, ilen = arguments.length; i < ilen; ++i) { 14617 helpers.each(arguments[i], setFn); 14618 } 14619 return target; 14620 }, 14621 14622 /** 14623 * Basic javascript inheritance based on the model created in Backbone.js 14624 */ 14625 inherits: function(extensions) { 14626 var me = this; 14627 var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { 14628 return me.apply(this, arguments); 14629 }; 14630 14631 var Surrogate = function() { 14632 this.constructor = ChartElement; 14633 }; 14634 14635 Surrogate.prototype = me.prototype; 14636 ChartElement.prototype = new Surrogate(); 14637 ChartElement.extend = helpers.inherits; 14638 14639 if (extensions) { 14640 helpers.extend(ChartElement.prototype, extensions); 14641 } 14642 14643 ChartElement.__super__ = me.prototype; 14644 return ChartElement; 14645 } 14646}; 14647 14648module.exports = helpers; 14649 14650// DEPRECATIONS 14651 14652/** 14653 * Provided for backward compatibility, use Chart.helpers.callback instead. 14654 * @function Chart.helpers.callCallback 14655 * @deprecated since version 2.6.0 14656 * @todo remove at version 3 14657 * @private 14658 */ 14659helpers.callCallback = helpers.callback; 14660 14661/** 14662 * Provided for backward compatibility, use Array.prototype.indexOf instead. 14663 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ 14664 * @function Chart.helpers.indexOf 14665 * @deprecated since version 2.7.0 14666 * @todo remove at version 3 14667 * @private 14668 */ 14669helpers.indexOf = function(array, item, fromIndex) { 14670 return Array.prototype.indexOf.call(array, item, fromIndex); 14671}; 14672 14673/** 14674 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. 14675 * @function Chart.helpers.getValueOrDefault 14676 * @deprecated since version 2.7.0 14677 * @todo remove at version 3 14678 * @private 14679 */ 14680helpers.getValueOrDefault = helpers.valueOrDefault; 14681 14682/** 14683 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. 14684 * @function Chart.helpers.getValueAtIndexOrDefault 14685 * @deprecated since version 2.7.0 14686 * @todo remove at version 3 14687 * @private 14688 */ 14689helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 14690 14691},{}],44:[function(require,module,exports){ 14692'use strict'; 14693 14694var helpers = require(43); 14695 14696/** 14697 * Easing functions adapted from Robert Penner's easing equations. 14698 * @namespace Chart.helpers.easingEffects 14699 * @see http://www.robertpenner.com/easing/ 14700 */ 14701var effects = { 14702 linear: function(t) { 14703 return t; 14704 }, 14705 14706 easeInQuad: function(t) { 14707 return t * t; 14708 }, 14709 14710 easeOutQuad: function(t) { 14711 return -t * (t - 2); 14712 }, 14713 14714 easeInOutQuad: function(t) { 14715 if ((t /= 0.5) < 1) { 14716 return 0.5 * t * t; 14717 } 14718 return -0.5 * ((--t) * (t - 2) - 1); 14719 }, 14720 14721 easeInCubic: function(t) { 14722 return t * t * t; 14723 }, 14724 14725 easeOutCubic: function(t) { 14726 return (t = t - 1) * t * t + 1; 14727 }, 14728 14729 easeInOutCubic: function(t) { 14730 if ((t /= 0.5) < 1) { 14731 return 0.5 * t * t * t; 14732 } 14733 return 0.5 * ((t -= 2) * t * t + 2); 14734 }, 14735 14736 easeInQuart: function(t) { 14737 return t * t * t * t; 14738 }, 14739 14740 easeOutQuart: function(t) { 14741 return -((t = t - 1) * t * t * t - 1); 14742 }, 14743 14744 easeInOutQuart: function(t) { 14745 if ((t /= 0.5) < 1) { 14746 return 0.5 * t * t * t * t; 14747 } 14748 return -0.5 * ((t -= 2) * t * t * t - 2); 14749 }, 14750 14751 easeInQuint: function(t) { 14752 return t * t * t * t * t; 14753 }, 14754 14755 easeOutQuint: function(t) { 14756 return (t = t - 1) * t * t * t * t + 1; 14757 }, 14758 14759 easeInOutQuint: function(t) { 14760 if ((t /= 0.5) < 1) { 14761 return 0.5 * t * t * t * t * t; 14762 } 14763 return 0.5 * ((t -= 2) * t * t * t * t + 2); 14764 }, 14765 14766 easeInSine: function(t) { 14767 return -Math.cos(t * (Math.PI / 2)) + 1; 14768 }, 14769 14770 easeOutSine: function(t) { 14771 return Math.sin(t * (Math.PI / 2)); 14772 }, 14773 14774 easeInOutSine: function(t) { 14775 return -0.5 * (Math.cos(Math.PI * t) - 1); 14776 }, 14777 14778 easeInExpo: function(t) { 14779 return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); 14780 }, 14781 14782 easeOutExpo: function(t) { 14783 return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; 14784 }, 14785 14786 easeInOutExpo: function(t) { 14787 if (t === 0) { 14788 return 0; 14789 } 14790 if (t === 1) { 14791 return 1; 14792 } 14793 if ((t /= 0.5) < 1) { 14794 return 0.5 * Math.pow(2, 10 * (t - 1)); 14795 } 14796 return 0.5 * (-Math.pow(2, -10 * --t) + 2); 14797 }, 14798 14799 easeInCirc: function(t) { 14800 if (t >= 1) { 14801 return t; 14802 } 14803 return -(Math.sqrt(1 - t * t) - 1); 14804 }, 14805 14806 easeOutCirc: function(t) { 14807 return Math.sqrt(1 - (t = t - 1) * t); 14808 }, 14809 14810 easeInOutCirc: function(t) { 14811 if ((t /= 0.5) < 1) { 14812 return -0.5 * (Math.sqrt(1 - t * t) - 1); 14813 } 14814 return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); 14815 }, 14816 14817 easeInElastic: function(t) { 14818 var s = 1.70158; 14819 var p = 0; 14820 var a = 1; 14821 if (t === 0) { 14822 return 0; 14823 } 14824 if (t === 1) { 14825 return 1; 14826 } 14827 if (!p) { 14828 p = 0.3; 14829 } 14830 if (a < 1) { 14831 a = 1; 14832 s = p / 4; 14833 } else { 14834 s = p / (2 * Math.PI) * Math.asin(1 / a); 14835 } 14836 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); 14837 }, 14838 14839 easeOutElastic: function(t) { 14840 var s = 1.70158; 14841 var p = 0; 14842 var a = 1; 14843 if (t === 0) { 14844 return 0; 14845 } 14846 if (t === 1) { 14847 return 1; 14848 } 14849 if (!p) { 14850 p = 0.3; 14851 } 14852 if (a < 1) { 14853 a = 1; 14854 s = p / 4; 14855 } else { 14856 s = p / (2 * Math.PI) * Math.asin(1 / a); 14857 } 14858 return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; 14859 }, 14860 14861 easeInOutElastic: function(t) { 14862 var s = 1.70158; 14863 var p = 0; 14864 var a = 1; 14865 if (t === 0) { 14866 return 0; 14867 } 14868 if ((t /= 0.5) === 2) { 14869 return 1; 14870 } 14871 if (!p) { 14872 p = 0.45; 14873 } 14874 if (a < 1) { 14875 a = 1; 14876 s = p / 4; 14877 } else { 14878 s = p / (2 * Math.PI) * Math.asin(1 / a); 14879 } 14880 if (t < 1) { 14881 return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); 14882 } 14883 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; 14884 }, 14885 easeInBack: function(t) { 14886 var s = 1.70158; 14887 return t * t * ((s + 1) * t - s); 14888 }, 14889 14890 easeOutBack: function(t) { 14891 var s = 1.70158; 14892 return (t = t - 1) * t * ((s + 1) * t + s) + 1; 14893 }, 14894 14895 easeInOutBack: function(t) { 14896 var s = 1.70158; 14897 if ((t /= 0.5) < 1) { 14898 return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); 14899 } 14900 return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); 14901 }, 14902 14903 easeInBounce: function(t) { 14904 return 1 - effects.easeOutBounce(1 - t); 14905 }, 14906 14907 easeOutBounce: function(t) { 14908 if (t < (1 / 2.75)) { 14909 return 7.5625 * t * t; 14910 } 14911 if (t < (2 / 2.75)) { 14912 return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; 14913 } 14914 if (t < (2.5 / 2.75)) { 14915 return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; 14916 } 14917 return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; 14918 }, 14919 14920 easeInOutBounce: function(t) { 14921 if (t < 0.5) { 14922 return effects.easeInBounce(t * 2) * 0.5; 14923 } 14924 return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; 14925 } 14926}; 14927 14928module.exports = { 14929 effects: effects 14930}; 14931 14932// DEPRECATIONS 14933 14934/** 14935 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
14936 * @function Chart.helpers.easingEffects 14937 * @deprecated since version 2.7.0 14938 * @todo remove at version 3 14939 * @private 14940 */ 14941helpers.easingEffects = effects; 14942 14943},{"43":43}],45:[function(require,module,exports){ 14944'use strict'; 14945 14946var helpers = require(43); 14947 14948/** 14949 * @alias Chart.helpers.options 14950 * @namespace 14951 */ 14952module.exports = { 14953 /** 14954 * Converts the given line height `value` in pixels for a specific font `size`. 14955 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). 14956 * @param {Number} size - The font size (in pixels) used to resolve relative `value`. 14957 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). 14958 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height 14959 * @since 2.7.0 14960 */ 14961 toLineHeight: function(value, size) { 14962 var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); 14963 if (!matches || matches[1] === 'normal') { 14964 return size * 1.2; 14965 } 14966 14967 value = +matches[2]; 14968 14969 switch (matches[3]) { 14970 case 'px': 14971 return value; 14972 case '%': 14973 value /= 100; 14974 break; 14975 default: 14976 break; 14977 } 14978 14979 return size * value; 14980 }, 14981 14982 /** 14983 * Converts the given value into a padding object with pre-computed width/height. 14984 * @param {Number|Object} value - If a number, set the value to all TRBL component, 14985 * else, if and object, use defined properties and sets undefined ones to 0. 14986 * @returns {Object} The padding values (top, right, bottom, left, width, height) 14987 * @since 2.7.0 14988 */ 14989 toPadding: function(value) { 14990 var t, r, b, l; 14991 14992 if (helpers.isObject(value)) { 14993 t = +value.top || 0; 14994 r = +value.right || 0; 14995 b = +value.bottom || 0; 14996 l = +value.left || 0; 14997 } else { 14998 t = r = b = l = +value || 0; 14999 } 15000 15001 return { 15002 top: t, 15003 right: r, 15004 bottom: b, 15005 left: l, 15006 height: t + b, 15007 width: l + r 15008 }; 15009 }, 15010 15011 /** 15012 * Evaluates the given `inputs` sequentially and returns the first defined value. 15013 * @param {Array[]} inputs - An array of values, falling back to the last value. 15014 * @param {Object} [context] - If defined and the current value is a function, the value 15015 * is called with `context` as first argument and the result becomes the new input. 15016 * @param {Number} [index] - If defined and the current value is an array, the value 15017 * at `index` become the new input. 15018 * @since 2.7.0 15019 */ 15020 resolve: function(inputs, context, index) { 15021 var i, ilen, value; 15022 15023 for (i = 0, ilen = inputs.length; i < ilen; ++i) { 15024 value = inputs[i]; 15025 if (value === undefined) { 15026 continue; 15027 } 15028 if (context !== undefined && typeof value === 'function') { 15029 value = value(context); 15030 } 15031 if (index !== undefined && helpers.isArray(value)) { 15032 value = value[index]; 15033 } 15034 if (value !== undefined) { 15035 return value; 15036 } 15037 } 15038 } 15039}; 15040 15041},{"43":43}],46:[function(require,module,exports){ 15042'use strict'; 15043 15044module.exports = require(43); 15045module.exports.easing = require(44); 15046module.exports.canvas = require(42); 15047module.exports.options = require(45); 15048 15049},{"42":42,"43":43,"44":44,"45":45}],47:[function(require,module,exports){ 15050/** 15051 * Platform fallback implementation (minimal). 15052 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 15053 */ 15054 15055module.exports = { 15056 acquireContext: function(item) { 15057 if (item && item.canvas) { 15058 // Support for any object associated to a canvas (including a context2d) 15059 item = item.canvas; 15060 } 15061 15062 return item && item.getContext('2d') || null; 15063 } 15064}; 15065 15066},{}],48:[function(require,module,exports){ 15067/** 15068 * Chart.Platform implementation for targeting a web browser 15069 */ 15070 15071'use strict'; 15072 15073var helpers = require(46); 15074 15075var EXPANDO_KEY = '$chartjs'; 15076var CSS_PREFIX = 'chartjs-'; 15077var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; 15078var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; 15079var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; 15080 15081/** 15082 * DOM event types -> Chart.js event types. 15083 * Note: only events with different types are mapped. 15084 * @see https://developer.mozilla.org/en-US/docs/Web/Events 15085 */ 15086var EVENT_TYPES = { 15087 touchstart: 'mousedown', 15088 touchmove: 'mousemove', 15089 touchend: 'mouseup', 15090 pointerenter: 'mouseenter', 15091 pointerdown: 'mousedown', 15092 pointermove: 'mousemove', 15093 pointerup: 'mouseup', 15094 pointerleave: 'mouseout', 15095 pointerout: 'mouseout' 15096}; 15097 15098/** 15099 * The "used" size is the final value of a dimension property after all calculations have 15100 * been performed. This method uses the computed style of `element` but returns undefined 15101 * if the computed style is not expressed in pixels. That can happen in some cases where 15102 * `element` has a size relative to its parent and this last one is not yet displayed, 15103 * for example because of `display: none` on a parent node. 15104 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value 15105 * @returns {Number} Size in pixels or undefined if unknown. 15106 */ 15107function readUsedSize(element, property) { 15108 var value = helpers.getStyle(element, property); 15109 var matches = value && value.match(/^(\d+)(\.\d+)?px$/); 15110 return matches ? Number(matches[1]) : undefined; 15111} 15112 15113/** 15114 * Initializes the canvas style and render size without modifying the canvas display size, 15115 * since responsiveness is handled by the controller.resize() method. The config is used 15116 * to determine the aspect ratio to apply in case no explicit height has been specified. 15117 */ 15118function initCanvas(canvas, config) { 15119 var style = canvas.style; 15120 15121 // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it 15122 // returns null or '' if no explicit value has been set to the canvas attribute. 15123 var renderHeight = canvas.getAttribute('height'); 15124 var renderWidth = canvas.getAttribute('width'); 15125 15126 // Chart.js modifies some canvas values that we want to restore on de
15126stroy 15127 canvas[EXPANDO_KEY] = { 15128 initial: { 15129 height: renderHeight, 15130 width: renderWidth, 15131 style: { 15132 display: style.display, 15133 height: style.height, 15134 width: style.width 15135 } 15136 } 15137 }; 15138 15139 // Force canvas to display as block to avoid extra space caused by inline 15140 // elements, which would interfere with the responsive resize process. 15141 // https://github.com/chartjs/Chart.js/issues/2538 15142 style.display = style.display || 'block'; 15143 15144 if (renderWidth === null || renderWidth === '') { 15145 var displayWidth = readUsedSize(canvas, 'width'); 15146 if (displayWidth !== undefined) { 15147 canvas.width = displayWidth; 15148 } 15149 } 15150 15151 if (renderHeight === null || renderHeight === '') { 15152 if (canvas.style.height === '') { 15153 // If no explicit render height and style height, let's apply the aspect ratio, 15154 // which one can be specified by the user but also by charts as default option 15155 // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. 15156 canvas.height = canvas.width / (config.options.aspectRatio || 2); 15157 } else { 15158 var displayHeight = readUsedSize(canvas, 'height'); 15159 if (displayWidth !== undefined) { 15160 canvas.height = displayHeight; 15161 } 15162 } 15163 } 15164 15165 return canvas; 15166} 15167 15168/** 15169 * Detects support for options object argument in addEventListener. 15170 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support 15171 * @private 15172 */ 15173var supportsEventListenerOptions = (function() { 15174 var supports = false; 15175 try { 15176 var options = Object.defineProperty({}, 'passive', { 15177 get: function() { 15178 supports = true; 15179 } 15180 }); 15181 window.addEventListener('e', null, options); 15182 } catch (e) { 15183 // continue regardless of error 15184 } 15185 return supports; 15186}()); 15187 15188// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. 15189// https://github.com/chartjs/Chart.js/issues/4287 15190var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; 15191 15192function addEventListener(node, type, listener) { 15193 node.addEventListener(type, listener, eventListenerOptions); 15194} 15195 15196function removeEventListener(node, type, listener) { 15197 node.removeEventListener(type, listener, eventListenerOptions); 15198} 15199 15200function createEvent(type, chart, x, y, nativeEvent) { 15201 return { 15202 type: type, 15203 chart: chart, 15204 native: nativeEvent || null, 15205 x: x !== undefined ? x : null, 15206 y: y !== undefined ? y : null, 15207 }; 15208} 15209 15210function fromNativeEvent(event, chart) { 15211 var type = EVENT_TYPES[event.type] || event.type; 15212 var pos = helpers.getRelativePosition(event, chart); 15213 return createEvent(type, chart, pos.x, pos.y, event); 15214} 15215 15216function throttled(fn, thisArg) { 15217 var ticking = false; 15218 var args = []; 15219 15220 return function() { 15221 args = Array.prototype.slice.call(arguments); 15222 thisArg = thisArg || this; 15223 15224 if (!ticking) { 15225 ticking = true; 15226 helpers.requestAnimFrame.call(window, function() { 15227 ticking = false; 15228 fn.apply(thisArg, args); 15229 }); 15230 } 15231 }; 15232} 15233 15234// Implementation based on https://github.com/marcj/css-element-queries 15235function createResizer(handler) { 15236 var resizer = document.createElement('div'); 15237 var cls = CSS_PREFIX + 'size-monitor'; 15238 var maxSize = 1000000; 15239 var style = 15240 'position:absolute;' + 15241 'left:0;' + 15242 'top:0;' + 15243 'right:0;' + 15244 'bottom:0;' + 15245 'overflow:hidden;' + 15246 'pointer-events:none;' + 15247 'visibility:hidden;' + 15248 'z-index:-1;'; 15249 15250 resizer.style.cssText = style; 15251 resizer.className = cls; 15252 resizer.innerHTML = 15253 '<div class="' + cls + '-expand" style="' + style + '">' + 15254 '<div style="' + 15255 'position:absolute;' + 15256 'width:' + maxSize + 'px;' + 15257 'height:' + maxSize + 'px;' + 15258 'left:0;' + 15259 'top:0">' + 15260 '</div>' + 15261 '</div>' + 15262 '<div class="' + cls + '-shrink" style="' + style + '">' + 15263 '<div style="' + 15264 'position:absolute;' + 15265 'width:200%;' + 15266 'height:200%;' + 15267 'left:0; ' + 15268 'top:0">' + 15269 '</div>' + 15270 '</div>'; 15271 15272 var expand = resizer.childNodes[0]; 15273 var shrink = resizer.childNodes[1]; 15274 15275 resizer._reset = function() { 15276 expand.scrollLeft = maxSize; 15277 expand.scrollTop = maxSize; 15278 shrink.scrollLeft = maxSize; 15279 shrink.scrollTop = maxSize; 15280 }; 15281 var onScroll = function() { 15282 resizer._reset(); 15283 handler(); 15284 }; 15285 15286 addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); 15287 addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); 15288 15289 return resizer; 15290} 15291 15292// https://davidwalsh.name/detect-node-insertion 15293function watchForRender(node, handler) { 15294 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); 15295 var proxy = expando.renderProxy = function(e) { 15296 if (e.animationName === CSS_RENDER_ANIMATION) { 15297 handler(); 15298 } 15299 }; 15300
15301 helpers.each(ANIMATION_START_EVENTS, function(type) { 15302 addEventListener(node, type, proxy); 15303 }); 15304 15305 // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class 15306 // is removed then added back immediately (same animation frame?). Accessing the 15307 // `offsetParent` property will force a reflow and re-evaluate the CSS animation. 15308 // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics 15309 // https://github.com/chartjs/Chart.js/issues/4737 15310 expando.reflow = !!node.offsetParent; 15311 15312 node.classList.add(CSS_RENDER_MONITOR); 15313} 15314 15315function unwatchForRender(node) { 15316 var expando = node[EXPANDO_KEY] || {}; 15317 var proxy = expando.renderProxy; 15318 15319 if (proxy) { 15320 helpers.each(ANIMATION_START_EVENTS, function(type) { 15321 removeEventListener(node, type, proxy); 15322 }); 15323 15324 delete expando.renderProxy; 15325 } 15326 15327 node.classList.remove(CSS_RENDER_MONITOR); 15328} 15329 15330function addResizeListener(node, listener, chart) { 15331 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); 15332 15333 // Let's keep track of this added resizer and thus avoid DOM query when removing it. 15334 var resizer = expando.resizer = createResizer(throttled(function() { 15335 if (expando.resizer) { 15336 return listener(createEvent('resize', chart)); 15337 } 15338 })); 15339 15340 // The resizer needs to be attached to the node parent, so we first need to be 15341 // sure that `node` is attached to the DOM before injecting the resizer element. 15342 watchForRender(node, function() { 15343 if (expando.resizer) { 15344 var container = node.parentNode; 15345 if (container && container !== resizer.parentNode) { 15346 container.insertBefore(resizer, container.firstChild); 15347 } 15348 15349 // The container size might have changed, let's reset the resizer state. 15350 resizer._reset(); 15351 } 15352 }); 15353} 15354 15355function removeResizeListener(node) { 15356 var expando = node[EXPANDO_KEY] || {}; 15357 var resizer = expando.resizer; 15358 15359 delete expando.resizer; 15360 unwatchForRender(node); 15361 15362 if (resizer && resizer.parentNode) { 15363 resizer.parentNode.removeChild(resizer); 15364 } 15365} 15366 15367function injectCSS(platform, css) { 15368 // http://stackoverflow.com/q/3922139 15369 var style = platform._style || document.createElement('style'); 15370 if (!platform._style) { 15371 platform._style = style; 15372 css = '/* Chart.js */\n' + css; 15373 style.setAttribute('type', 'text/css'); 15374 document.getElementsByTagName('head')[0].appendChild(style); 15375 } 15376 15377 style.appendChild(document.createTextNode(css)); 15378} 15379 15380module.exports = { 15381 /** 15382 * This property holds whether this platform is enabled for the current environment. 15383 * Currently used by platform.js to select the proper implementation. 15384 * @private 15385 */ 15386 _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', 15387 15388 initialize: function() { 15389 var keyframes = 'from{opacity:0.99}to{opacity:1}'; 15390 15391 injectCSS(this, 15392 // DOM rendering detection 15393 // https://davidwalsh.name/detect-node-insertion 15394 '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + 15395 '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + 15396 '.' + CSS_RENDER_MONITOR + '{' + 15397 '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 15398 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 15399 '}' 15400 ); 15401 }, 15402 15403 acquireContext: function(item, config) { 15404 if (typeof item === 'string') { 15405 item = document.getElementById(item); 15406 } else if (item.length) { 15407 // Support for array based queries (such as jQuery) 15408 item = item[0]; 15409 } 15410 15411 if (item && item.canvas) { 15412 // Support for any object associated to a canvas (including a context2d) 15413 item = item.canvas; 15414 } 15415 15416 // To prevent canvas fingerprinting, some add-ons undefine the getContext 15417 // method, for example: https://github.com/kkapsner/CanvasBlocker 15418 // https://github.com/chartjs/Chart.js/issues/2807 15419 var context = item && item.getContext && item.getContext('2d'); 15420 15421 // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is 15422 // inside an iframe or when running in a protected environment. We could guess the 15423 // types from their toString() value but let's keep things flexible and assume it's 15424 // a sufficient condition if the item has a context2D which has item as `canvas`. 15425 // https://github.com/chartjs/Chart.js/issues/3887 15426 // https://github.com/chartjs/Chart.js/issues/4102 15427 // https://github.com/chartjs/Chart.js/issues/4152 15428 if (context && context.canvas === item) { 15429 initCanvas(item, config); 15430 return context; 15431 } 15432 15433 return null; 15434 }, 15435 15436 releaseContext: function(context) { 15437 var canvas = context.canvas; 15438 if (!canvas[EXPANDO_KEY]) { 15439 return; 15440 } 15441 15442 var initial = canvas[EXPANDO_KEY].initial; 15443 ['height', 'width'].forEach(function(prop) { 15444 var value = initial[prop]; 15445 if (helpers.isNullOrUndef(value)) { 15446 canvas.removeAttribute(prop); 15447 } else { 15448 canvas.setAttribute(prop, value); 15449 } 15450 }); 15451 15452 helpers.each(initial.style || {}, function(value, key) { 15453 canvas.style[key] = value; 15454 }); 15455 15456 // The canvas render size might have been changed (and thus the state stack discarded),
15457 // we can't use save() and restore() to restore the initial state. So make sure that at 15458 // least the canvas context is reset to the default state by setting the canvas width. 15459 // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html 15460 canvas.width = canvas.width; 15461 15462 delete canvas[EXPANDO_KEY]; 15463 }, 15464 15465 addEventListener: function(chart, type, listener) { 15466 var canvas = chart.canvas; 15467 if (type === 'resize') { 15468 // Note: the resize event is not supported on all browsers. 15469 addResizeListener(canvas, listener, chart); 15470 return; 15471 } 15472 15473 var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); 15474 var proxies = expando.proxies || (expando.proxies = {}); 15475 var proxy = proxies[chart.id + '_' + type] = function(event) { 15476 listener(fromNativeEvent(event, chart)); 15477 }; 15478 15479 addEventListener(canvas, type, proxy); 15480 }, 15481 15482 removeEventListener: function(chart, type, listener) { 15483 var canvas = chart.canvas; 15484 if (type === 'resize') { 15485 // Note: the resize event is not supported on all browsers. 15486 removeResizeListener(canvas, listener); 15487 return; 15488 } 15489 15490 var expando = listener[EXPANDO_KEY] || {}; 15491 var proxies = expando.proxies || {}; 15492 var proxy = proxies[chart.id + '_' + type]; 15493 if (!proxy) { 15494 return; 15495 } 15496 15497 removeEventListener(canvas, type, proxy); 15498 } 15499}; 15500 15501// DEPRECATIONS 15502 15503/** 15504 * Provided for backward compatibility, use EventTarget.addEventListener instead. 15505 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ 15506 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener 15507 * @function Chart.helpers.addEvent 15508 * @deprecated since version 2.7.0 15509 * @todo remove at version 3 15510 * @private 15511 */ 15512helpers.addEvent = addEventListener; 15513 15514/** 15515 * Provided for backward compatibility, use EventTarget.removeEventListener instead. 15516 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ 15517 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener 15518 * @function Chart.helpers.removeEvent 15519 * @deprecated since version 2.7.0 15520 * @todo remove at version 3 15521 * @private 15522 */ 15523helpers.removeEvent = removeEventListener; 15524 15525},{"46":46}],49:[function(require,module,exports){ 15526'use strict'; 15527 15528var helpers = require(46); 15529var basic = require(47); 15530var dom = require(48); 15531 15532// @TODO Make possible to select another platform at build time. 15533var implementation = dom._enabled ? dom : basic; 15534 15535/** 15536 * @namespace Chart.platform 15537 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html 15538 * @since 2.4.0 15539 */ 15540module.exports = helpers.extend({ 15541 /** 15542 * @since 2.7.0 15543 */ 15544 initialize: function() {}, 15545 15546 /** 15547 * Called at chart construction time, returns a context2d instance implementing 15548 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. 15549 * @param {*} item - The native item from which to acquire context (platform specific) 15550 * @param {Object} options - The chart options 15551 * @returns {CanvasRenderingContext2D} context2d instance 15552 */ 15553 acquireContext: function() {}, 15554 15555 /** 15556 * Called at chart destruction time, releases any resources associated to the context 15557 * previously returned by the acquireContext() method. 15558 * @param {CanvasRenderingContext2D} context - The context2d instance 15559 * @returns {Boolean} true if the method succeeded, else false 15560 */ 15561 releaseContext: function() {}, 15562 15563 /** 15564 * Registers the specified listener on the given chart. 15565 * @param {Chart} chart - Chart from which to listen for event 15566 * @param {String} type - The ({@link IEvent}) type to listen for 15567 * @param {Function} listener - Receives a notification (an object that implements 15568 * the {@link IEvent} interface) when an event of the specified type occurs. 15569 */ 15570 addEventListener: function() {}, 15571 15572 /** 15573 * Removes the specified listener previously registered with addEventListener. 15574 * @param {Chart} chart -Chart from which to remove the listener 15575 * @param {String} type - The ({@link IEvent}) type to remove 15576 * @param {Function} listener - The listener function to remove from the event target. 15577 */ 15578 removeEventListener: function() {} 15579 15580}, implementation); 15581 15582/** 15583 * @interface IPlatform 15584 * Allows abstracting platform dependencies away from the chart 15585 * @borrows Chart.platform.acquireContext as acquireContext 15586 * @borrows Chart.platform.releaseContext as releaseContext 15587 * @borrows Chart.platform.addEventListener as addEventListener 15588 * @borrows Chart.platform.removeEventListener as removeEventListener 15589 */ 15590 15591/** 15592 * @interface IEvent 15593 * @prop {String} type - The event type name, possible values are: 15594 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
15595 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' 15596 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') 15597 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) 15598 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) 15599 */ 15600 15601},{"46":46,"47":47,"48":48}],50:[function(require,module,exports){ 15602'use strict'; 15603 15604module.exports = {}; 15605module.exports.filler = require(51); 15606module.exports.legend = require(52); 15607module.exports.title = require(53); 15608 15609},{"51":51,"52":52,"53":53}],51:[function(require,module,exports){ 15610/** 15611 * Plugin based on discussion from the following Chart.js issues: 15612 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 15613 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 15614 */ 15615 15616'use strict'; 15617 15618var defaults = require(26); 15619var elements = require(41); 15620var helpers = require(46); 15621 15622defaults._set('global', { 15623 plugins: { 15624 filler: { 15625 propagate: true 15626 } 15627 } 15628}); 15629 15630var mappers = { 15631 dataset: function(source) { 15632 var index = source.fill; 15633 var chart = source.chart; 15634 var meta = chart.getDatasetMeta(index); 15635 var visible = meta && chart.isDatasetVisible(index); 15636 var points = (visible && meta.dataset._children) || []; 15637 var length = points.length || 0; 15638 15639 return !length ? null : function(point, i) { 15640 return (i < length && points[i]._view) || null; 15641 }; 15642 }, 15643 15644 boundary: function(source) { 15645 var boundary = source.boundary; 15646 var x = boundary ? boundary.x : null; 15647 var y = boundary ? boundary.y : null; 15648 15649 return function(point) { 15650 return { 15651 x: x === null ? point.x : x, 15652 y: y === null ? point.y : y, 15653 }; 15654 }; 15655 } 15656}; 15657 15658// @todo if (fill[0] === '#') 15659function decodeFill(el, index, count) { 15660 var model = el._model || {}; 15661 var fill = model.fill; 15662 var target; 15663 15664 if (fill === undefined) { 15665 fill = !!model.backgroundColor; 15666 } 15667 15668 if (fill === false || fill === null) { 15669 return false; 15670 } 15671 15672 if (fill === true) { 15673 return 'origin'; 15674 } 15675 15676 target = parseFloat(fill, 10); 15677 if (isFinite(target) && Math.floor(target) === target) { 15678 if (fill[0] === '-' || fill[0] === '+') { 15679 target = index + target; 15680 } 15681 15682 if (target === index || target < 0 || target >= count) { 15683 return false; 15684 } 15685 15686 return target; 15687 } 15688 15689 switch (fill) { 15690 // compatibility 15691 case 'bottom': 15692 return 'start'; 15693 case 'top': 15694 return 'end'; 15695 case 'zero': 15696 return 'origin'; 15697 // supported boundaries 15698 case 'origin': 15699 case 'start': 15700 case 'end': 15701 return fill; 15702 // invalid fill values 15703 default: 15704 return false; 15705 } 15706} 15707 15708function computeBoundary(source) { 15709 var model = source.el._model || {}; 15710 var scale = source.el._scale || {}; 15711 var fill = source.fill; 15712 var target = null; 15713 var horizontal; 15714 15715 if (isFinite(fill)) { 15716 return null; 15717 } 15718 15719 // Backward compatibility: until v3, we still need to support boundary values set on 15720 // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and 15721 // controllers might still use it (e.g. the Smith chart). 15722 15723 if (fill === 'start') { 15724 target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; 15725 } else if (fill === 'end') { 15726 target = model.scaleTop === undefined ? scale.top : model.scaleTop; 15727 } else if (model.scaleZero !== undefined) { 15728 target = model.scaleZero; 15729 } else if (scale.getBasePosition) { 15730 target = scale.getBasePosition(); 15731 } else if (scale.getBasePixel) { 15732 target = scale.getBasePixel(); 15733 } 15734 15735 if (target !== undefined && target !== null) { 15736 if (target.x !== undefined && target.y !== undefined) { 15737 return target; 15738 } 15739 15740 if (typeof target === 'number' && isFinite(target)) { 15741 horizontal = scale.isHorizontal(); 15742 return { 15743 x: horizontal ? target : null, 15744 y: horizontal ? null : target 15745 }; 15746 } 15747 } 15748 15749 return null; 15750} 15751 15752function resolveTarget(sources, index, propagate) { 15753 var source = sources[index]; 15754 var fill = source.fill; 15755 var visited = [index]; 15756 var target; 15757 15758 if (!propagate) { 15759 return fill; 15760 } 15761 15762 while (fill !== false && visited.indexOf(fill) === -1) { 15763 if (!isFinite(fill)) { 15764 return fill; 15765 } 15766 15767 target = sources[fill]; 15768 if (!target) { 15769 return false;
15770 } 15771 15772 if (target.visible) { 15773 return fill; 15774 } 15775 15776 visited.push(fill); 15777 fill = target.fill; 15778 } 15779 15780 return false; 15781} 15782 15783function createMapper(source) { 15784 var fill = source.fill; 15785 var type = 'dataset'; 15786 15787 if (fill === false) { 15788 return null; 15789 } 15790 15791 if (!isFinite(fill)) { 15792 type = 'boundary'; 15793 } 15794 15795 return mappers[type](source); 15796} 15797 15798function isDrawable(point) { 15799 return point && !point.skip; 15800} 15801 15802function drawArea(ctx, curve0, curve1, len0, len1) { 15803 var i; 15804 15805 if (!len0 || !len1) { 15806 return; 15807 } 15808 15809 // building first area curve (normal) 15810 ctx.moveTo(curve0[0].x, curve0[0].y); 15811 for (i = 1; i < len0; ++i) { 15812 helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); 15813 } 15814 15815 // joining the two area curves 15816 ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); 15817 15818 // building opposite area curve (reverse) 15819 for (i = len1 - 1; i > 0; --i) { 15820 helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); 15821 } 15822} 15823 15824function doFill(ctx, points, mapper, view, color, loop) { 15825 var count = points.length; 15826 var span = view.spanGaps; 15827 var curve0 = []; 15828 var curve1 = []; 15829 var len0 = 0; 15830 var len1 = 0; 15831 var i, ilen, index, p0, p1, d0, d1; 15832 15833 ctx.beginPath(); 15834 15835 for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { 15836 index = i % count; 15837 p0 = points[index]._view; 15838 p1 = mapper(p0, index, view); 15839 d0 = isDrawable(p0); 15840 d1 = isDrawable(p1); 15841 15842 if (d0 && d1) { 15843 len0 = curve0.push(p0); 15844 len1 = curve1.push(p1); 15845 } else if (len0 && len1) { 15846 if (!span) { 15847 drawArea(ctx, curve0, curve1, len0, len1); 15848 len0 = len1 = 0; 15849 curve0 = []; 15850 curve1 = []; 15851 } else { 15852 if (d0) { 15853 curve0.push(p0); 15854 } 15855 if (d1) { 15856 curve1.push(p1); 15857 } 15858 } 15859 } 15860 } 15861 15862 drawArea(ctx, curve0, curve1, len0, len1); 15863 15864 ctx.closePath(); 15865 ctx.fillStyle = color; 15866 ctx.fill(); 15867} 15868 15869module.exports = { 15870 id: 'filler', 15871 15872 afterDatasetsUpdate: function(chart, options) { 15873 var count = (chart.data.datasets || []).length; 15874 var propagate = options.propagate; 15875 var sources = []; 15876 var meta, i, el, source; 15877 15878 for (i = 0; i < count; ++i) { 15879 meta = chart.getDatasetMeta(i); 15880 el = meta.dataset; 15881 source = null; 15882 15883 if (el && el._model && el instanceof elements.Line) { 15884 source = { 15885 visible: chart.isDatasetVisible(i), 15886 fill: decodeFill(el, i, count), 15887 chart: chart, 15888 el: el 15889 }; 15890 } 15891 15892 meta.$filler = source; 15893 sources.push(source); 15894 } 15895 15896 for (i = 0; i < count; ++i) { 15897 source = sources[i]; 15898 if (!source) { 15899 continue; 15900 } 15901 15902 source.fill = resolveTarget(sources, i, propagate); 15903 source.boundary = computeBoundary(source); 15904 source.mapper = createMapper(source); 15905 } 15906 }, 15907 15908 beforeDatasetDraw: function(chart, args) { 15909 var meta = args.meta.$filler; 15910 if (!meta) { 15911 return; 15912 } 15913 15914 var ctx = chart.ctx; 15915 var el = meta.el; 15916 var view = el._view; 15917 var points = el._children || []; 15918 var mapper = meta.mapper; 15919 var color = view.backgroundColor || defaults.global.defaultColor; 15920 15921 if (mapper && color && points.length) { 15922 helpers.canvas.clipArea(ctx, chart.chartArea); 15923 doFill(ctx, points, mapper, view, color, el._loop); 15924 helpers.canvas.unclipArea(ctx); 15925 } 15926 } 15927}; 15928 15929},{"26":26,"41":41,"46":46}],52:[function(require,module,exports){ 15930'use strict'; 15931 15932var defaults = require(26); 15933var Element = require(27); 15934var helpers = require(46); 15935var layouts = require(31); 15936 15937var noop = helpers.noop; 15938 15939defaults._set('global', { 15940 legend: { 15941 display: true, 15942 position: 'top', 15943 fullWidth: true, 15944 reverse: false, 15945 weight: 1000, 15946 15947 // a callback that will handle 15948 onClick: function(e, legendItem) { 15949 var index = legendItem.datasetIndex; 15950 var ci = this.chart; 15951 var meta = ci.getDatasetMeta(index); 15952 15953 // See controller.isDatasetVisible comment 15954 meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; 15955 15956 // We hid a dataset ... rerender the chart 15957 ci.update(); 15958 }, 15959 15960 onHover: null, 15961 15962 labels: { 15963 boxWidth: 40, 15964 padding: 10, 15965 // Generates labels shown in the legend 15966 // Valid properties to return: 15967 // text : text to display 15968 // fillStyle : fill of coloured box 15969 // strokeStyle: stroke of coloured box 15970 // hidden : if this legend item refers to a hidden item 15971 // lineCap : cap style for line 15972 // lineDash 15973 // lineDashOffset : 15974 // lineJoin : 15975 // lineWidth :
15976 generateLabels: function(chart) { 15977 var data = chart.data; 15978 return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { 15979 return { 15980 text: dataset.label, 15981 fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), 15982 hidden: !chart.isDatasetVisible(i), 15983 lineCap: dataset.borderCapStyle, 15984 lineDash: dataset.borderDash, 15985 lineDashOffset: dataset.borderDashOffset, 15986 lineJoin: dataset.borderJoinStyle, 15987 lineWidth: dataset.borderWidth, 15988 strokeStyle: dataset.borderColor, 15989 pointStyle: dataset.pointStyle, 15990 15991 // Below is extra data used for toggling the datasets 15992 datasetIndex: i 15993 }; 15994 }, this) : []; 15995 } 15996 } 15997 }, 15998 15999 legendCallback: function(chart) { 16000 var text = []; 16001 text.push('<ul class="' + chart.id + '-legend">'); 16002 for (var i = 0; i < chart.data.datasets.length; i++) { 16003 text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); 16004 if (chart.data.datasets[i].label) { 16005 text.push(chart.data.datasets[i].label); 16006 } 16007 text.push('</li>'); 16008 } 16009 text.push('</ul>'); 16010 return text.join(''); 16011 } 16012}); 16013 16014/** 16015 * Helper function to get the box width based on the usePointStyle option 16016 * @param labelopts {Object} the label options on the legend 16017 * @param fontSize {Number} the label font size 16018 * @return {Number} width of the color box area 16019 */ 16020function getBoxWidth(labelOpts, fontSize) { 16021 return labelOpts.usePointStyle ? 16022 fontSize * Math.SQRT2 : 16023 labelOpts.boxWidth; 16024} 16025 16026/** 16027 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! 16028 */ 16029var Legend = Element.extend({ 16030 16031 initialize: function(config) { 16032 helpers.extend(this, config); 16033 16034 // Contains hit boxes for each dataset (in dataset order) 16035 this.legendHitBoxes = []; 16036 16037 // Are we in doughnut mode which has a different data type 16038 this.doughnutMode = false; 16039 }, 16040 16041 // These methods are ordered by lifecycle. Utilities then follow. 16042 // Any function defined here is inherited by all legend types. 16043 // Any function can be extended by the legend type 16044 16045 beforeUpdate: noop, 16046 update: function(maxWidth, maxHeight, margins) { 16047 var me = this; 16048 16049 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) 16050 me.beforeUpdate(); 16051 16052 // Absorb the master measurements 16053 me.maxWidth = maxWidth; 16054 me.maxHeight = maxHeight; 16055 me.margins = margins; 16056 16057 // Dimensions 16058 me.beforeSetDimensions(); 16059 me.setDimensions(); 16060 me.afterSetDimensions(); 16061 // Labels 16062 me.beforeBuildLabels(); 16063 me.buildLabels(); 16064 me.afterBuildLabels(); 16065 16066 // Fit 16067 me.beforeFit(); 16068 me.fit(); 16069 me.afterFit(); 16070 // 16071 me.afterUpdate(); 16072 16073 return me.minSize; 16074 }, 16075 afterUpdate: noop, 16076 16077 // 16078 16079 beforeSetDimensions: noop, 16080 setDimensions: function() { 16081 var me = this; 16082 // Set the unconstrained dimension before label rotation 16083 if (me.isHorizontal()) { 16084 // Reset position before calculating rotation 16085 me.width = me.maxWidth; 16086 me.left = 0; 16087 me.right = me.width; 16088 } else { 16089 me.height = me.maxHeight; 16090 16091 // Reset position before calculating rotation 16092 me.top = 0; 16093 me.bottom = me.height; 16094 } 16095 16096 // Reset padding 16097 me.paddingLeft = 0; 16098 me.paddingTop = 0; 16099 me.paddingRight = 0; 16100 me.paddingBottom = 0; 16101 16102 // Reset minSize 16103 me.minSize = { 16104 width: 0, 16105 height: 0 16106 }; 16107 }, 16108 afterSetDimensions: noop, 16109 16110 // 16111 16112 beforeBuildLabels: noop, 16113 buildLabels: function() { 16114 var me = this; 16115 var labelOpts = me.options.labels || {}; 16116 var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; 16117 16118 if (labelOpts.filter) { 16119 legendItems = legendItems.filter(function(item) { 16120 return labelOpts.filter(item, me.chart.data); 16121 }); 16122 } 16123 16124 if (me.options.reverse) { 16125 legendItems.reverse(); 16126 } 16127 16128 me.legendItems = legendItems; 16129 }, 16130 afterBuildLabels: noop, 16131 16132 // 16133 16134 beforeFit: noop, 16135 fit: function() { 16136 var me = this; 16137 var opts = me.options; 16138 var labelOpts = opts.labels; 16139 var display = opts.display; 16140 16141 var ctx = me.ctx; 16142 16143 var globalDefault = defaults.global; 16144 var valueOrDefault = helpers.valueOrDefault; 16145 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); 16146 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); 16147 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); 16148 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16149 16150 // Reset hit boxes 16151 var hitboxes = me.legendHitBoxes = []; 16152 16153 var minSize = me.minSize; 16154 var isHorizontal = me.isHorizontal(); 16155 16156 if (isHorizontal) { 16157 minSize.width = me.maxWidth; // fill all the width 16158 minSize.height = display ? 10 : 0; 16159 } else { 16160 minSize.width = display ? 10 : 0; 16161 minSize.height = me.maxHeight; // fill all the height 16162 } 16163 16164 // Increase sizes here 16165 if (display) { 16166 ctx.font = labelFont; 16167 16168 if (isHorizontal) { 16169 // Labels 16170
16171 // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one 16172 var lineWidths = me.lineWidths = [0]; 16173 var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; 16174 16175 ctx.textAlign = 'left'; 16176 ctx.textBaseline = 'top'; 16177 16178 helpers.each(me.legendItems, function(legendItem, i) { 16179 var boxWidth = getBoxWidth(labelOpts, fontSize); 16180 var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; 16181 16182 if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { 16183 totalHeight += fontSize + (labelOpts.padding); 16184 lineWidths[lineWidths.length] = me.left; 16185 } 16186 16187 // Store the hitbox width and height here. Final position will be updated in `draw` 16188 hitboxes[i] = { 16189 left: 0, 16190 top: 0, 16191 width: width, 16192 height: fontSize 16193 }; 16194 16195 lineWidths[lineWidths.length - 1] += width + labelOpts.padding; 16196 }); 16197 16198 minSize.height += totalHeight; 16199 16200 } else { 16201 var vPadding = labelOpts.padding; 16202 var columnWidths = me.columnWidths = []; 16203 var totalWidth = labelOpts.padding; 16204 var currentColWidth = 0; 16205 var currentColHeight = 0; 16206 var itemHeight = fontSize + vPadding; 16207 16208 helpers.each(me.legendItems, function(legendItem, i) { 16209 var boxWidth = getBoxWidth(labelOpts, fontSize); 16210 var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; 16211 16212 // If too tall, go to new column 16213 if (currentColHeight + itemHeight > minSize.height) { 16214 totalWidth += currentColWidth + labelOpts.padding; 16215 columnWidths.push(currentColWidth); // previous column width 16216 16217 currentColWidth = 0; 16218 currentColHeight = 0; 16219 } 16220 16221 // Get max width 16222 currentColWidth = Math.max(currentColWidth, itemWidth); 16223 currentColHeight += itemHeight; 16224 16225 // Store the hitbox width and height here. Final position will be updated in `draw` 16226 hitboxes[i] = { 16227 left: 0, 16228 top: 0, 16229 width: itemWidth, 16230 height: fontSize 16231 }; 16232 }); 16233 16234 totalWidth += currentColWidth; 16235 columnWidths.push(currentColWidth); 16236 minSize.width += totalWidth; 16237 } 16238 } 16239 16240 me.width = minSize.width; 16241 me.height = minSize.height; 16242 }, 16243 afterFit: noop, 16244 16245 // Shared Methods 16246 isHorizontal: function() { 16247 return this.options.position === 'top' || this.options.position === 'bottom'; 16248 }, 16249 16250 // Actually draw the legend on the canvas 16251 draw: function() { 16252 var me = this; 16253 var opts = me.options; 16254 var labelOpts = opts.labels; 16255 var globalDefault = defaults.global; 16256 var lineDefault = globalDefault.elements.line; 16257 var legendWidth = me.width; 16258 var lineWidths = me.lineWidths; 16259 16260 if (opts.display) { 16261 var ctx = me.ctx; 16262 var valueOrDefault = helpers.valueOrDefault; 16263 var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); 16264 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); 16265 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); 16266 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); 16267 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16268 var cursor; 16269 16270 // Canvas setup 16271 ctx.textAlign = 'left'; 16272 ctx.textBaseline = 'middle'; 16273 ctx.lineWidth = 0.5; 16274 ctx.strokeStyle = fontColor; // for strikethrough effect 16275 ctx.fillStyle = fontColor; // render in correct colour 16276 ctx.font = labelFont; 16277 16278 var boxWidth = getBoxWidth(labelOpts, fontSize); 16279 var hitboxes = me.legendHitBoxes; 16280 16281 // current position 16282 var drawLegendBox = function(x, y, legendItem) { 16283 if (isNaN(boxWidth) || boxWidth <= 0) { 16284 return; 16285 } 16286 16287 // Set the ctx for the box 16288 ctx.save(); 16289 16290 ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); 16291 ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); 16292 ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); 16293 ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); 16294 ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth); 16295 ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); 16296 var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); 16297 16298 if (ctx.setLineDash) { 16299 // IE 9 and 10 do not support line dash 16300 ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); 16301 } 16302 16303 if (opts.labels && opts.labels.usePointStyle) { 16304 // Recalculate x and y for drawPoint() because its expecting 16305 // x and y to be center of figure (instead of top left) 16306 var radius = fontSize * Math.SQRT2 / 2; 16307 var offSet = radius / Math.SQRT2; 16308 var centerX = x + offSet; 16309 var centerY = y + offSet; 16310 16311 // Draw pointStyle as legend symbol 16312 helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); 16313 } else { 16314 // Draw box as legend symbol
16315 if (!isLineWidthZero) { 16316 ctx.strokeRect(x, y, boxWidth, fontSize); 16317 } 16318 ctx.fillRect(x, y, boxWidth, fontSize); 16319 } 16320 16321 ctx.restore(); 16322 }; 16323 var fillText = function(x, y, legendItem, textWidth) { 16324 var halfFontSize = fontSize / 2; 16325 var xLeft = boxWidth + halfFontSize + x; 16326 var yMiddle = y + halfFontSize; 16327 16328 ctx.fillText(legendItem.text, xLeft, yMiddle); 16329 16330 if (legendItem.hidden) { 16331 // Strikethrough the text if hidden 16332 ctx.beginPath(); 16333 ctx.lineWidth = 2; 16334 ctx.moveTo(xLeft, yMiddle); 16335 ctx.lineTo(xLeft + textWidth, yMiddle); 16336 ctx.stroke(); 16337 } 16338 }; 16339 16340 // Horizontal 16341 var isHorizontal = me.isHorizontal(); 16342 if (isHorizontal) { 16343 cursor = { 16344 x: me.left + ((legendWidth - lineWidths[0]) / 2), 16345 y: me.top + labelOpts.padding, 16346 line: 0 16347 }; 16348 } else { 16349 cursor = { 16350 x: me.left + labelOpts.padding, 16351 y: me.top + labelOpts.padding, 16352 line: 0 16353 }; 16354 } 16355 16356 var itemHeight = fontSize + labelOpts.padding; 16357 helpers.each(me.legendItems, function(legendItem, i) { 16358 var textWidth = ctx.measureText(legendItem.text).width; 16359 var width = boxWidth + (fontSize / 2) + textWidth; 16360 var x = cursor.x; 16361 var y = cursor.y; 16362 16363 if (isHorizontal) { 16364 if (x + width >= legendWidth) { 16365 y = cursor.y += itemHeight; 16366 cursor.line++; 16367 x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); 16368 } 16369 } else if (y + itemHeight > me.bottom) { 16370 x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; 16371 y = cursor.y = me.top + labelOpts.padding; 16372 cursor.line++; 16373 } 16374 16375 drawLegendBox(x, y, legendItem); 16376 16377 hitboxes[i].left = x; 16378 hitboxes[i].top = y; 16379 16380 // Fill the actual label 16381 fillText(x, y, legendItem, textWidth); 16382 16383 if (isHorizontal) { 16384 cursor.x += width + (labelOpts.padding); 16385 } else { 16386 cursor.y += itemHeight; 16387 } 16388 16389 }); 16390 } 16391 }, 16392 16393 /** 16394 * Handle an event 16395 * @private 16396 * @param {IEvent} event - The event to handle 16397 * @return {Boolean} true if a change occured 16398 */ 16399 handleEvent: function(e) { 16400 var me = this; 16401 var opts = me.options; 16402 var type = e.type === 'mouseup' ? 'click' : e.type; 16403 var changed = false; 16404 16405 if (type === 'mousemove') { 16406 if (!opts.onHover) { 16407 return; 16408 } 16409 } else if (type === 'click') { 16410 if (!opts.onClick) { 16411 return; 16412 } 16413 } else { 16414 return; 16415 } 16416 16417 // Chart event already has relative position in it 16418 var x = e.x; 16419 var y = e.y; 16420 16421 if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { 16422 // See if we are touching one of the dataset boxes 16423 var lh = me.legendHitBoxes; 16424 for (var i = 0; i < lh.length; ++i) { 16425 var hitBox = lh[i]; 16426 16427 if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { 16428 // Touching an element 16429 if (type === 'click') { 16430 // use e.native for backwards compatibility 16431 opts.onClick.call(me, e.native, me.legendItems[i]); 16432 changed = true; 16433 break; 16434 } else if (type === 'mousemove') { 16435 // use e.native for backwards compatibility 16436 opts.onHover.call(me, e.native, me.legendItems[i]); 16437 changed = true; 16438 break; 16439 } 16440 } 16441 } 16442 } 16443 16444 return changed; 16445 } 16446}); 16447 16448function createNewLegendAndAttach(chart, legendOpts) { 16449 var legend = new Legend({ 16450 ctx: chart.ctx, 16451 options: legendOpts, 16452 chart: chart 16453 }); 16454 16455 layouts.configure(chart, legend, legendOpts); 16456 layouts.addBox(chart, legend); 16457 chart.legend = legend; 16458} 16459 16460module.exports = { 16461 id: 'legend', 16462 16463 /** 16464 * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making 16465 * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of 16466 * the plugin, which one will be re-exposed in the chart.js file. 16467 * https://github.com/chartjs/Chart.js/pull/2640 16468 * @private 16469 */ 16470 _element: Legend, 16471 16472 beforeInit: function(chart) { 16473 var legendOpts = chart.options.legend; 16474 16475 if (legendOpts) { 16476 createNewLegendAndAttach(chart, legendOpts); 16477 } 16478 }, 16479 16480 beforeUpdate: function(chart) { 16481 var legendOpts = chart.options.legend; 16482 var legend = chart.legend; 16483 16484 if (legendOpts) { 16485 helpers.mergeIf(legendOpts, defaults.global.legend); 16486 16487 if (legend) { 16488 layouts.configure(chart, legend, legendOpts); 16489 legend.options = legendOpts; 16490 } else { 16491 createNewLegendAndAttach(chart, legendOpts); 16492 } 16493 } else if (legend) { 16494 layouts.removeBox(chart, legend); 16495 delete chart.legend; 16496 } 16497 }, 16498
16499 afterEvent: function(chart, e) { 16500 var legend = chart.legend; 16501 if (legend) { 16502 legend.handleEvent(e); 16503 } 16504 } 16505}; 16506 16507},{"26":26,"27":27,"31":31,"46":46}],53:[function(require,module,exports){ 16508'use strict'; 16509 16510var defaults = require(26); 16511var Element = require(27); 16512var helpers = require(46); 16513var layouts = require(31); 16514 16515var noop = helpers.noop; 16516 16517defaults._set('global', { 16518 title: { 16519 display: false, 16520 fontStyle: 'bold', 16521 fullWidth: true, 16522 lineHeight: 1.2, 16523 padding: 10, 16524 position: 'top', 16525 text: '', 16526 weight: 2000 // by default greater than legend (1000) to be above 16527 } 16528}); 16529 16530/** 16531 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! 16532 */ 16533var Title = Element.extend({ 16534 initialize: function(config) { 16535 var me = this; 16536 helpers.extend(me, config); 16537 16538 // Contains hit boxes for each dataset (in dataset order) 16539 me.legendHitBoxes = []; 16540 }, 16541 16542 // These methods are ordered by lifecycle. Utilities then follow. 16543 16544 beforeUpdate: noop, 16545 update: function(maxWidth, maxHeight, margins) { 16546 var me = this; 16547 16548 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) 16549 me.beforeUpdate(); 16550 16551 // Absorb the master measurements 16552 me.maxWidth = maxWidth; 16553 me.maxHeight = maxHeight; 16554 me.margins = margins; 16555 16556 // Dimensions 16557 me.beforeSetDimensions(); 16558 me.setDimensions(); 16559 me.afterSetDimensions(); 16560 // Labels 16561 me.beforeBuildLabels(); 16562 me.buildLabels(); 16563 me.afterBuildLabels(); 16564 16565 // Fit 16566 me.beforeFit(); 16567 me.fit(); 16568 me.afterFit(); 16569 // 16570 me.afterUpdate(); 16571 16572 return me.minSize; 16573 16574 }, 16575 afterUpdate: noop, 16576 16577 // 16578 16579 beforeSetDimensions: noop, 16580 setDimensions: function() { 16581 var me = this; 16582 // Set the unconstrained dimension before label rotation 16583 if (me.isHorizontal()) { 16584 // Reset position before calculating rotation 16585 me.width = me.maxWidth; 16586 me.left = 0; 16587 me.right = me.width; 16588 } else { 16589 me.height = me.maxHeight; 16590 16591 // Reset position before calculating rotation 16592 me.top = 0; 16593 me.bottom = me.height; 16594 } 16595 16596 // Reset padding 16597 me.paddingLeft = 0; 16598 me.paddingTop = 0; 16599 me.paddingRight = 0; 16600 me.paddingBottom = 0; 16601 16602 // Reset minSize 16603 me.minSize = { 16604 width: 0, 16605 height: 0 16606 }; 16607 }, 16608 afterSetDimensions: noop, 16609 16610 // 16611 16612 beforeBuildLabels: noop, 16613 buildLabels: noop, 16614 afterBuildLabels: noop, 16615 16616 // 16617 16618 beforeFit: noop, 16619 fit: function() { 16620 var me = this; 16621 var valueOrDefault = helpers.valueOrDefault; 16622 var opts = me.options; 16623 var display = opts.display; 16624 var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); 16625 var minSize = me.minSize; 16626 var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; 16627 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); 16628 var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; 16629 16630 if (me.isHorizontal()) { 16631 minSize.width = me.maxWidth; // fill all the width 16632 minSize.height = textSize; 16633 } else { 16634 minSize.width = textSize; 16635 minSize.height = me.maxHeight; // fill all the height 16636 } 16637 16638 me.width = minSize.width; 16639 me.height = minSize.height; 16640 16641 }, 16642 afterFit: noop, 16643 16644 // Shared Methods 16645 isHorizontal: function() { 16646 var pos = this.options.position; 16647 return pos === 'top' || pos === 'bottom'; 16648 }, 16649 16650 // Actually draw the title block on the canvas 16651 draw: function() { 16652 var me = this; 16653 var ctx = me.ctx; 16654 var valueOrDefault = helpers.valueOrDefault; 16655 var opts = me.options; 16656 var globalDefaults = defaults.global; 16657 16658 if (opts.display) { 16659 var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); 16660 var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); 16661 var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); 16662 var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16663 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); 16664 var offset = lineHeight / 2 + opts.padding; 16665 var rotation = 0; 16666 var top = me.top; 16667 var left = me.left; 16668 var bottom = me.bottom; 16669 var right = me.right; 16670 var maxWidth, titleX, titleY; 16671 16672 ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour 16673 ctx.font = titleFont; 16674 16675 // Horizontal 16676 if (me.isHorizontal()) { 16677 titleX = left + ((right - left) / 2); // midpoint of the width 16678 titleY = top + offset; 16679 maxWidth = right - left; 16680 } else { 16681 titleX = opts.position === 'left' ? left + offset : right - offset; 16682 titleY = top + ((bottom - top) / 2); 16683 maxWidth = bottom - top; 16684 rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); 16685 } 16686 16687 ctx.save(); 16688 ctx.translate(titleX, titleY); 16689 ctx.rotate(rotation); 16690 ctx.textAlign = 'center'; 16691 ctx.textBaseline = 'middle'; 16692 16693 var text = opts.text; 16694 if (helpers.isArray(text)) { 16695 var y = 0; 16696 for (var i = 0; i < text.length; ++i) { 16697 ctx.fillText(text[i], 0, y, maxWidth); 16698 y += lineHeight; 16699 } 16700 } else { 16701 ctx.fillText(text, 0, 0, maxWidth); 16702 } 16703
16704 ctx.restore(); 16705 } 16706 } 16707}); 16708 16709function createNewTitleBlockAndAttach(chart, titleOpts) { 16710 var title = new Title({ 16711 ctx: chart.ctx, 16712 options: titleOpts, 16713 chart: chart 16714 }); 16715 16716 layouts.configure(chart, title, titleOpts); 16717 layouts.addBox(chart, title); 16718 chart.titleBlock = title; 16719} 16720 16721module.exports = { 16722 id: 'title', 16723 16724 /** 16725 * Backward compatibility: since 2.1.5, the title is registered as a plugin, making 16726 * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of 16727 * the plugin, which one will be re-exposed in the chart.js file. 16728 * https://github.com/chartjs/Chart.js/pull/2640 16729 * @private 16730 */ 16731 _element: Title, 16732 16733 beforeInit: function(chart) { 16734 var titleOpts = chart.options.title; 16735 16736 if (titleOpts) { 16737 createNewTitleBlockAndAttach(chart, titleOpts); 16738 } 16739 }, 16740 16741 beforeUpdate: function(chart) { 16742 var titleOpts = chart.options.title; 16743 var titleBlock = chart.titleBlock; 16744 16745 if (titleOpts) { 16746 helpers.mergeIf(titleOpts, defaults.global.title); 16747 16748 if (titleBlock) { 16749 layouts.configure(chart, titleBlock, titleOpts); 16750 titleBlock.options = titleOpts; 16751 } else { 16752 createNewTitleBlockAndAttach(chart, titleOpts); 16753 } 16754 } else if (titleBlock) { 16755 layouts.removeBox(chart, titleBlock); 16756 delete chart.titleBlock; 16757 } 16758 } 16759}; 16760 16761},{"26":26,"27":27,"31":31,"46":46}],54:[function(require,module,exports){ 16762'use strict'; 16763 16764var Scale = require(33); 16765var scaleService = require(34); 16766 16767module.exports = function() { 16768 16769 // Default config for a category scale 16770 var defaultConfig = { 16771 position: 'bottom' 16772 }; 16773 16774 var DatasetScale = Scale.extend({ 16775 /** 16776 * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those 16777 * else fall back to data.labels 16778 * @private 16779 */ 16780 getLabels: function() { 16781 var data = this.chart.data; 16782 return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; 16783 }, 16784 16785 determineDataLimits: function() { 16786 var me = this; 16787 var labels = me.getLabels(); 16788 me.minIndex = 0; 16789 me.maxIndex = labels.length - 1; 16790 var findIndex; 16791 16792 if (me.options.ticks.min !== undefined) { 16793 // user specified min value 16794 findIndex = labels.indexOf(me.options.ticks.min); 16795 me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; 16796 } 16797 16798 if (me.options.ticks.max !== undefined) { 16799 // user specified max value 16800 findIndex = labels.indexOf(me.options.ticks.max); 16801 me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; 16802 } 16803 16804 me.min = labels[me.minIndex]; 16805 me.max = labels[me.maxIndex]; 16806 }, 16807 16808 buildTicks: function() { 16809 var me = this; 16810 var labels = me.getLabels(); 16811 // If we are viewing some subset of labels, slice the original array 16812 me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); 16813 }, 16814 16815 getLabelForIndex: function(index, datasetIndex) { 16816 var me = this; 16817 var data = me.chart.data; 16818 var isHorizontal = me.isHorizontal(); 16819 16820 if (data.yLabels && !isHorizontal) { 16821 return me.getRightValue(data.datasets[datasetIndex].data[index]); 16822 } 16823 return me.ticks[index - me.minIndex]; 16824 }, 16825 16826 // Used to get data value locations. Value can either be an index or a numerical value 16827 getPixelForValue: function(value, index) { 16828 var me = this; 16829 var offset = me.options.offset; 16830 // 1 is added because we need the length but we have the indexes 16831 var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); 16832 16833 // If value is a data object, then index is the index in the data array, 16834 // not the index of the scale. We need to change that. 16835 var valueCategory; 16836 if (value !== undefined && value !== null) { 16837 valueCategory = me.isHorizontal() ? value.x : value.y; 16838 } 16839 if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { 16840 var labels = me.getLabels(); 16841 value = valueCategory || value; 16842 var idx = labels.indexOf(value); 16843 index = idx !== -1 ? idx : index; 16844 } 16845 16846 if (me.isHorizontal()) { 16847 var valueWidth = me.width / offsetAmt; 16848 var widthOffset = (valueWidth * (index - me.minIndex)); 16849 16850 if (offset) { 16851 widthOffset += (valueWidth / 2); 16852 } 16853 16854 return me.left + Math.round(widthOffset); 16855 } 16856 var valueHeight = me.height / offsetAmt; 16857 var heightOffset = (valueHeight * (index - me.minIndex)); 16858 16859 if (offset) { 16860 heightOffset += (valueHeight / 2); 16861 } 16862 16863 return me.top + Math.round(heightOffset); 16864 }, 16865 getPixelForTick: function(index) { 16866 return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); 16867 }, 16868 getValueForPixel: function(pixel) { 16869 var me = this; 16870 var offset = me.options.offset; 16871 var value; 16872 var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); 16873 var horz = me.isHorizontal(); 16874 var valueDimension = (horz ? me.width : me.height) / offsetAmt; 16875 16876 pixel -= horz ? me.left : me.top; 16877 16878 if (offset) { 16879 pixel -= (valueDimension / 2); 16880 } 16881 16882 if (pixel <= 0) { 16883 value = 0; 16884 } else { 16885 value = Math.round(pixel / valueDimension); 16886 } 16887 16888 return value + me.minIndex; 16889 }, 16890 getBasePixel: function() { 16891 return this.bottom; 16892 } 16893 }); 16894 16895 scaleService.registerScaleType('category', DatasetScale, defaultConfig); 16896}; 16897 16898},{"33":33,"34":34}],55:[function(require,module,exports){ 16899'use strict'; 16900 16901var defaults = require(26); 16902var helpers = require(46); 16903var scaleService = require(34); 16904var Ticks = require(35); 16905 16906module.exports = function(Chart) { 16907 16908 var defaultConfig = { 16909 position: 'left', 16910 ticks: { 16911 callback: Ticks.formatters.linear 16912 } 16913 }; 16914 16915 var LinearScale = Chart.LinearScaleBase.extend({ 16916 16917 determineDataLimits: function() { 16918 var me = this; 16919 var opts = me.options; 16920 var chart = me.chart; 16921 var data = chart.data; 16922 var datasets = data.datasets; 16923 var isHorizontal = me.isHorizontal(); 16924 var DEFAULT_MIN = 0; 16925 var DEFAULT_MAX = 1; 16926 16927 function IDMatches(meta) { 16928 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; 16929 } 16930 16931 // First Calculate the range 16932 me.min = null; 16933 me.max = null; 16934 16935 var hasStacks = opts.stacked; 16936 if (hasStacks === undefined) { 16937 helpers.each(datasets, function(dataset, datasetIndex) { 16938 if (hasStacks) { 16939 return; 16940 } 16941 16942 var meta = chart.getDatasetMeta(datasetIndex); 16943 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && 16944 meta.stack !== undefined) { 16945 hasStacks = true; 16946 } 16947 }); 16948 } 16949 16950 if (opts.stacked || hasStacks) { 16951 var valuesPerStack = {}; 16952 16953 helpers.each(datasets, function(dataset, datasetIndex) { 16954 var meta = chart.getDatasetMeta(datasetIndex); 16955 var key = [ 16956 meta.type, 16957 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined 16958 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), 16959 meta.stack 16960 ].join('.'); 16961 16962 if (valuesPerStack[key] === undefined) { 16963 valuesPerStack[key] = { 16964 positiveValues: [], 16965 negativeValues: [] 16966 }; 16967 } 16968 16969 // Store these per type 16970 var positiveValues = valuesPerStack[key].positiveValues; 16971 var negativeValues = valuesPerStack[key].negativeValues; 16972 16973 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 16974 helpers.each(dataset.data, function(rawValue, index) { 16975 var value = +me.getRightValue(rawValue); 16976 if (isNaN(value) || meta.data[index].hidden) { 16977 return; 16978 } 16979 16980 positiveValues[index] = positiveValues[index] || 0; 16981 negativeValues[index] = negativeValues[index] || 0; 16982 16983 if (opts.relativePoints) { 16984 positiveValues[index] = 100; 16985 } else if (value < 0) { 16986 negativeValues[index] += value; 16987 } else { 16988 positiveValues[index] += value; 16989 } 16990 }); 16991 } 16992 }); 16993 16994 helpers.each(valuesPerStack, function(valuesForType) { 16995 var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); 16996 var minVal = helpers.min(values); 16997 var maxVal = helpers.max(values); 16998 me.min = me.min === null ? minVal : Math.min(me.min, minVal); 16999 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); 17000 }); 17001 17002 } else { 17003 helpers.each(datasets, function(dataset, datasetIndex) { 17004 var meta = chart.getDatasetMeta(datasetIndex); 17005 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17006 helpers.each(dataset.data, function(rawValue, index) { 17007 var value = +me.getRightValue(rawValue); 17008 if (isNaN(value) || meta.data[index].hidden) { 17009 return; 17010 } 17011 17012 if (me.min === null) { 17013 me.min = value; 17014 } else if (value < me.min) { 17015 me.min = value; 17016 } 17017 17018 if (me.max === null) { 17019 me.max = value; 17020 } else if (value > me.max) { 17021 me.max = value; 17022 } 17023 }); 17024 } 17025 }); 17026 } 17027 17028 me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; 17029 me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; 17030 17031 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero 17032 this.handleTickRangeOptions(); 17033 }, 17034 getTickLimit: function() { 17035 var maxTicks; 17036 var me = this; 17037 var tickOpts = me.options.ticks; 17038 17039 if (me.isHorizontal()) { 17040 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); 17041 } else { 17042 // The factor of 2 used to scale the font size has been experimentally determined.
17043 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); 17044 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); 17045 } 17046 17047 return maxTicks; 17048 }, 17049 // Called after the ticks are built. We need 17050 handleDirectionalChanges: function() { 17051 if (!this.isHorizontal()) { 17052 // We are in a vertical orientation. The top value is the highest. So reverse the array 17053 this.ticks.reverse(); 17054 } 17055 }, 17056 getLabelForIndex: function(index, datasetIndex) { 17057 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17058 }, 17059 // Utils 17060 getPixelForValue: function(value) { 17061 // This must be called after fit has been run so that 17062 // this.left, this.top, this.right, and this.bottom have been defined 17063 var me = this; 17064 var start = me.start; 17065 17066 var rightValue = +me.getRightValue(value); 17067 var pixel; 17068 var range = me.end - start; 17069 17070 if (me.isHorizontal()) { 17071 pixel = me.left + (me.width / range * (rightValue - start)); 17072 } else { 17073 pixel = me.bottom - (me.height / range * (rightValue - start)); 17074 } 17075 return pixel; 17076 }, 17077 getValueForPixel: function(pixel) { 17078 var me = this; 17079 var isHorizontal = me.isHorizontal(); 17080 var innerDimension = isHorizontal ? me.width : me.height; 17081 var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; 17082 return me.start + ((me.end - me.start) * offset); 17083 }, 17084 getPixelForTick: function(index) { 17085 return this.getPixelForValue(this.ticksAsNumbers[index]); 17086 } 17087 }); 17088 17089 scaleService.registerScaleType('linear', LinearScale, defaultConfig); 17090}; 17091 17092},{"26":26,"34":34,"35":35,"46":46}],56:[function(require,module,exports){ 17093'use strict'; 17094 17095var helpers = require(46); 17096var Scale = require(33); 17097 17098/** 17099 * Generate a set of linear ticks 17100 * @param generationOptions the options used to generate the ticks 17101 * @param dataRange the range of the data 17102 * @returns {Array<Number>} array of tick values 17103 */ 17104function generateTicks(generationOptions, dataRange) { 17105 var ticks = []; 17106 // To get a "nice" value for the tick spacing, we will use the appropriately named 17107 // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks 17108 // for details. 17109 17110 var factor; 17111 var precision; 17112 var spacing; 17113 17114 if (generationOptions.stepSize && generationOptions.stepSize > 0) { 17115 spacing = generationOptions.stepSize; 17116 } else { 17117 var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); 17118 spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); 17119 17120 precision = generationOptions.precision; 17121 if (precision !== undefined) { 17122 // If the user specified a precision, round to that number of decimal places 17123 factor = Math.pow(10, precision); 17124 spacing = Math.ceil(spacing * factor) / factor; 17125 } 17126 } 17127 var niceMin = Math.floor(dataRange.min / spacing) * spacing; 17128 var niceMax = Math.ceil(dataRange.max / spacing) * spacing; 17129 17130 // If min, max and stepSize is set and they make an evenly spaced scale use it. 17131 if (!helpers.isNullOrUndef(generationOptions.min) && !helpers.isNullOrUndef(generationOptions.max) && generationOptions.stepSize) { 17132 // If very close to our whole number, use it. 17133 if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { 17134 niceMin = generationOptions.min; 17135 niceMax = generationOptions.max; 17136 } 17137 } 17138 17139 var numSpaces = (niceMax - niceMin) / spacing; 17140 // If very close to our rounded value, use it. 17141 if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { 17142 numSpaces = Math.round(numSpaces); 17143 } else { 17144 numSpaces = Math.ceil(numSpaces); 17145 } 17146 17147 precision = 1; 17148 if (spacing < 1) { 17149 precision = Math.pow(10, 1 - Math.floor(helpers.log10(spacing))); 17150 niceMin = Math.round(niceMin * precision) / precision; 17151 niceMax = Math.round(niceMax * precision) / precision; 17152 } 17153 ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); 17154 for (var j = 1; j < numSpaces; ++j) { 17155 ticks.push(Math.round((niceMin + j * spacing) * precision) / precision); 17156 } 17157 ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); 17158 17159 return ticks; 17160} 17161 17162module.exports = function(Chart) { 17163 17164 var noop = helpers.noop; 17165 17166 Chart.LinearScaleBase = Scale.extend({ 17167 getRightValue: function(value) { 17168 if (typeof value === 'string') { 17169 return +value; 17170 } 17171 return Scale.prototype.getRightValue.call(this, value); 17172 }, 17173 17174 handleTickRangeOptions: function() { 17175 var me = this; 17176 var opts = me.options; 17177 var tickOpts = opts.ticks; 17178 17179 // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, 17180 // do nothing since that would make the chart weird. If the user really wants a weird chart 17181 // axis, they can manually override it 17182 if (tickOpts.beginAtZero) { 17183 var minSign = helpers.sign(me.min); 17184 var maxSign = helpers.sign(me.max); 17185 17186 if (minSign < 0 && maxSign < 0) { 17187 // move the top up to 0 17188 me.max = 0; 17189 } else if (minSign > 0 && maxSign > 0) { 17190 // move the bottom down to 0 17191 me.min = 0; 17192 } 17193 } 17194 17195 var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; 17196 var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; 17197 17198 if (tickOpts.min !== undefined) { 17199 me.min = tickOpts.min; 17200 } else if (tickOpts.suggestedMin !== undefined) { 17201 if (me.min === null) { 17202 me.min = tickOpts.suggestedMin; 17203 } else { 17204 me.min = Math.min(me.min, tickOpts.suggestedMin); 17205 } 17206 } 17207 17208 if (tickOpts.max !== undefined) { 17209 me.max = tickOpts.max; 17210 } else if (tickOpts.suggestedMax !== undefined) { 17211 if (me.max === null) { 17212 me.max = tickOpts.suggestedMax; 17213 } else { 17214 me.max = Math.max(me.max, tickOpts.suggestedMax); 17215 } 17216 } 17217 17218 if (setMin !== setMax) { 17219 // We set the min or the max but not both. 17220 // So ensure that our range is good 17221 // Inverted or 0 length range can happen when
17222 // ticks.min is set, and no datasets are visible 17223 if (me.min >= me.max) { 17224 if (setMin) { 17225 me.max = me.min + 1; 17226 } else { 17227 me.min = me.max - 1; 17228 } 17229 } 17230 } 17231 17232 if (me.min === me.max) { 17233 me.max++; 17234 17235 if (!tickOpts.beginAtZero) { 17236 me.min--; 17237 } 17238 } 17239 }, 17240 getTickLimit: noop, 17241 handleDirectionalChanges: noop, 17242 17243 buildTicks: function() { 17244 var me = this; 17245 var opts = me.options; 17246 var tickOpts = opts.ticks; 17247 17248 // Figure out what the max number of ticks we can support it is based on the size of 17249 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 17250 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 17251 // the graph. Make sure we always have at least 2 ticks 17252 var maxTicks = me.getTickLimit(); 17253 maxTicks = Math.max(2, maxTicks); 17254 17255 var numericGeneratorOptions = { 17256 maxTicks: maxTicks, 17257 min: tickOpts.min, 17258 max: tickOpts.max, 17259 precision: tickOpts.precision, 17260 stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) 17261 }; 17262 var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); 17263 17264 me.handleDirectionalChanges(); 17265 17266 // At this point, we need to update our max and min given the tick values since we have expanded the 17267 // range of the scale 17268 me.max = helpers.max(ticks); 17269 me.min = helpers.min(ticks); 17270 17271 if (tickOpts.reverse) { 17272 ticks.reverse(); 17273 17274 me.start = me.max; 17275 me.end = me.min; 17276 } else { 17277 me.start = me.min; 17278 me.end = me.max; 17279 } 17280 }, 17281 convertTicksToLabels: function() { 17282 var me = this; 17283 me.ticksAsNumbers = me.ticks.slice(); 17284 me.zeroLineIndex = me.ticks.indexOf(0); 17285 17286 Scale.prototype.convertTicksToLabels.call(me); 17287 } 17288 }); 17289}; 17290 17291},{"33":33,"46":46}],57:[function(require,module,exports){ 17292'use strict'; 17293 17294var helpers = require(46); 17295var Scale = require(33); 17296var scaleService = require(34); 17297var Ticks = require(35); 17298 17299/** 17300 * Generate a set of logarithmic ticks 17301 * @param generationOptions the options used to generate the ticks 17302 * @param dataRange the range of the data 17303 * @returns {Array<Number>} array of tick values 17304 */ 17305function generateTicks(generationOptions, dataRange) { 17306 var ticks = []; 17307 var valueOrDefault = helpers.valueOrDefault; 17308 17309 // Figure out what the max number of ticks we can support it is based on the size of 17310 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 17311 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 17312 // the graph 17313 var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); 17314 17315 var endExp = Math.floor(helpers.log10(dataRange.max)); 17316 var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); 17317 var exp, significand; 17318 17319 if (tickVal === 0) { 17320 exp = Math.floor(helpers.log10(dataRange.minNotZero)); 17321 significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); 17322 17323 ticks.push(tickVal); 17324 tickVal = significand * Math.pow(10, exp); 17325 } else { 17326 exp = Math.floor(helpers.log10(tickVal)); 17327 significand = Math.floor(tickVal / Math.pow(10, exp)); 17328 } 17329 var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; 17330 17331 do { 17332 ticks.push(tickVal); 17333 17334 ++significand; 17335 if (significand === 10) { 17336 significand = 1; 17337 ++exp; 17338 precision = exp >= 0 ? 1 : precision; 17339 } 17340 17341 tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; 17342 } while (exp < endExp || (exp === endExp && significand < endSignificand)); 17343 17344 var lastTick = valueOrDefault(generationOptions.max, tickVal); 17345 ticks.push(lastTick); 17346 17347 return ticks; 17348} 17349 17350 17351module.exports = function(Chart) { 17352 17353 var defaultConfig = { 17354 position: 'left', 17355 17356 // label settings 17357 ticks: { 17358 callback: Ticks.formatters.logarithmic 17359 } 17360 }; 17361 17362 var LogarithmicScale = Scale.extend({ 17363 determineDataLimits: function() { 17364 var me = this; 17365 var opts = me.options; 17366 var chart = me.chart; 17367 var data = chart.data; 17368 var datasets = data.datasets; 17369 var isHorizontal = me.isHorizontal(); 17370 function IDMatches(meta) { 17371 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; 17372 } 17373 17374 // Calculate Range 17375 me.min = null; 17376 me.max = null; 17377 me.minNotZero = null; 17378 17379 var hasStacks = opts.stacked; 17380 if (hasStacks === undefined) { 17381 helpers.each(datasets, function(dataset, datasetIndex) { 17382 if (hasStacks) { 17383 return; 17384 } 17385 17386 var meta = chart.getDatasetMeta(datasetIndex); 17387 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && 17388 meta.stack !== undefined) { 17389 hasStacks = true; 17390 } 17391 }); 17392 } 17393 17394 if (opts.stacked || hasStacks) { 17395 var valuesPerStack = {}; 17396 17397 helpers.each(datasets, function(dataset, datasetIndex) { 17398 var meta = chart.getDatasetMeta(datasetIndex); 17399 var key = [ 17400 meta.type, 17401 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined 17402 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), 17403 meta.stack 17404 ].join('.'); 17405 17406 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17407 if (valuesPerStack[key] === undefined) { 17408 valuesPerStack[key] = []; 17409 } 17410 17411 helpers.each(dataset.data, function(rawValue, index) { 17412 var values = valuesPerStack[key]; 17413 var value = +me.getRightValue(rawValue); 17414 // invalid, hidden and negative values are ignored 17415 if (isNaN(value) || meta.data[index].hidden || value < 0) { 17416 return; 17417 } 17418 values[index] = values[index] || 0; 17419 values[index] += value; 17420 }); 17421 } 17422 }); 17423 17424 helpers.each(valuesPerStack, function(valuesForType) { 17425 if (valuesForType.length > 0) { 17426 var minVal = helpers.min(valuesForType); 17427 var maxVal = helpers.max(valuesForType); 17428 me.min = me.min === null ? minVal : Math.min(me.min, minVal); 17429 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); 17430 } 17431 }); 17432 17433 } else { 17434 helpers.each(datasets, function(dataset, datasetIndex) { 17435 var meta = chart.getDatasetMeta(datasetIndex); 17436 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17437 helpers.each(dataset.data, function(rawValue, index) { 17438 var value = +me.getRightValue(rawValue); 17439 // invalid, hidden and negative values are ignored 17440 if (isNaN(value) || meta.data[index].hidden || value < 0) { 17441 return; 17442 } 17443 17444 if (me.min === null) { 17445 me.min = value; 17446 } else if (value < me.min) { 17447 me.min = value; 17448 } 17449 17450 if (me.max === null) { 17451 me.max = value; 17452 } else if (value > me.max) { 17453 me.max = value; 17454 } 17455 17456 if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { 17457 me.minNotZero = value; 17458 } 17459 }); 17460 } 17461 }); 17462 } 17463 17464 // Common base implementation to handle ticks.min, ticks.max 17465 this.handleTickRangeOptions(); 17466 }, 17467 handleTickRangeOptions: function() { 17468 var me = this; 17469 var opts = me.options; 17470 var tickOpts = opts.ticks; 17471 var valueOrDefault = helpers.valueOrDefault; 17472 var DEFAULT_MIN = 1; 17473 var DEFAULT_MAX = 10; 17474 17475 me.min = valueOrDefault(tickOpts.min, me.min); 17476 me.max = valueOrDefault(tickOpts.max, me.max); 17477 17478 if (me.min === me.max) { 17479 if (me.min !== 0 && me.min !== null) {
17480 me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); 17481 me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); 17482 } else { 17483 me.min = DEFAULT_MIN; 17484 me.max = DEFAULT_MAX; 17485 } 17486 } 17487 if (me.min === null) { 17488 me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1); 17489 } 17490 if (me.max === null) { 17491 me.max = me.min !== 0 17492 ? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1) 17493 : DEFAULT_MAX; 17494 } 17495 if (me.minNotZero === null) { 17496 if (me.min > 0) { 17497 me.minNotZero = me.min; 17498 } else if (me.max < 1) { 17499 me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max))); 17500 } else { 17501 me.minNotZero = DEFAULT_MIN; 17502 } 17503 } 17504 }, 17505 buildTicks: function() { 17506 var me = this; 17507 var opts = me.options; 17508 var tickOpts = opts.ticks; 17509 var reverse = !me.isHorizontal(); 17510 17511 var generationOptions = { 17512 min: tickOpts.min, 17513 max: tickOpts.max 17514 }; 17515 var ticks = me.ticks = generateTicks(generationOptions, me); 17516 17517 // At this point, we need to update our max and min given the tick values since we have expanded the 17518 // range of the scale 17519 me.max = helpers.max(ticks); 17520 me.min = helpers.min(ticks); 17521 17522 if (tickOpts.reverse) { 17523 reverse = !reverse; 17524 me.start = me.max; 17525 me.end = me.min; 17526 } else { 17527 me.start = me.min; 17528 me.end = me.max; 17529 } 17530 if (reverse) { 17531 ticks.reverse(); 17532 } 17533 }, 17534 convertTicksToLabels: function() { 17535 this.tickValues = this.ticks.slice(); 17536 17537 Scale.prototype.convertTicksToLabels.call(this); 17538 }, 17539 // Get the correct tooltip label 17540 getLabelForIndex: function(index, datasetIndex) { 17541 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17542 }, 17543 getPixelForTick: function(index) { 17544 return this.getPixelForValue(this.tickValues[index]); 17545 }, 17546 /** 17547 * Returns the value of the first tick. 17548 * @param {Number} value - The minimum not zero value. 17549 * @return {Number} The first tick value. 17550 * @private 17551 */ 17552 _getFirstTickValue: function(value) { 17553 var exp = Math.floor(helpers.log10(value)); 17554 var significand = Math.floor(value / Math.pow(10, exp)); 17555 17556 return significand * Math.pow(10, exp); 17557 }, 17558 getPixelForValue: function(value) { 17559 var me = this; 17560 var reverse = me.options.ticks.reverse; 17561 var log10 = helpers.log10; 17562 var firstTickValue = me._getFirstTickValue(me.minNotZero); 17563 var offset = 0; 17564 var innerDimension, pixel, start, end, sign; 17565 17566 value = +me.getRightValue(value); 17567 if (reverse) { 17568 start = me.end; 17569 end = me.start; 17570 sign = -1; 17571 } else { 17572 start = me.start; 17573 end = me.end; 17574 sign = 1; 17575 } 17576 if (me.isHorizontal()) { 17577 innerDimension = me.width; 17578 pixel = reverse ? me.right : me.left; 17579 } else { 17580 innerDimension = me.height; 17581 sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0) 17582 pixel = reverse ? me.top : me.bottom; 17583 } 17584 if (value !== start) { 17585 if (start === 0) { // include zero tick 17586 offset = helpers.getValueOrDefault( 17587 me.options.ticks.fontSize, 17588 Chart.defaults.global.defaultFontSize 17589 ); 17590 innerDimension -= offset; 17591 start = firstTickValue; 17592 } 17593 if (value !== 0) { 17594 offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start)); 17595 } 17596 pixel += sign * offset; 17597 } 17598 return pixel; 17599 }, 17600 getValueForPixel: function(pixel) { 17601 var me = this; 17602 var reverse = me.options.ticks.reverse; 17603 var log10 = helpers.log10; 17604 var firstTickValue = me._getFirstTickValue(me.minNotZero); 17605 var innerDimension, start, end, value; 17606 17607 if (reverse) { 17608 start = me.end; 17609 end = me.start; 17610 } else { 17611 start = me.start; 17612 end = me.end; 17613 } 17614 if (me.isHorizontal()) { 17615 innerDimension = me.width; 17616 value = reverse ? me.right - pixel : pixel - me.left; 17617 } else { 17618 innerDimension = me.height; 17619 value = reverse ? pixel - me.top : me.bottom - pixel; 17620 } 17621 if (value !== start) { 17622 if (start === 0) { // include zero tick 17623 var offset = helpers.getValueOrDefault( 17624 me.options.ticks.fontSize, 17625 Chart.defaults.global.defaultFontSize 17626 ); 17627 value -= offset; 17628 innerDimension -= offset; 17629 start = firstTickValue; 17630 } 17631 value *= log10(end) - log10(start); 17632 value /= innerDimension; 17633 value = Math.pow(10, log10(start) + value); 17634 } 17635 return value; 17636 } 17637 }); 17638 17639 scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); 17640}; 17641 17642},{"33":33,"34":34,"35":35,"46":46}],58:[function(require,module,exports){ 17643'use strict'; 17644 17645var defaults = require(26); 17646var helpers = require(46); 17647var scaleService = require(34); 17648var Ticks = require(35); 17649 17650module.exports = function(Chart) { 17651 17652 var globalDefaults = defaults.global; 17653 17654 var defaultConfig = { 17655 display: true, 17656 17657 // Boolean - Whether to animate scaling the chart from the centre 17658 animate: true, 17659 position: 'chartArea', 17660 17661 angleLines: { 17662 display: true, 17663 color: 'rgba(0, 0, 0, 0.1)', 17664 lineWidth: 1 17665 }, 17666 17667 gridLines: { 17668 circular: false 17669 }, 17670 17671 // label settings 17672 ticks: { 17673 // Boolean - Show a backdrop to the scale label 17674 showLabelBackdrop: true, 17675 17676 // String - The colour of the label backdrop 17677 backdropColor: 'rgba(255,255,255,0.75)', 17678 17679 // Number - The backdrop padding above & below the label in pixels 17680 backdropPaddingY: 2, 17681 17682 // Number - The backdrop padding to the side of the label in pixels 17683 backdropPaddingX: 2, 17684 17685 callback: Ticks.formatters.linear 17686 }, 17687 17688 pointLabels: { 17689 // Boolean - if true, show point labels 17690 display: true, 17691 17692 // Number - Point label font size in pixels 17693 fontSize: 10, 17694 17695 // Function - Used to convert point labels 17696 callback: function(label) { 17697 return label; 17698 } 17699 } 17700 }; 17701 17702 function getValueCount(scale) { 17703 var opts = scale.options; 17704 return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; 17705 } 17706 17707 function getPointLabelFontOptions(scale) { 17708 var pointLabelOptions = scale.options.pointLabels; 17709 var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); 17710 var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); 17711 var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); 17712 var font = helpers.fontString(fontSize, fontStyle, fontFamily); 17713 17714 return { 17715 size: fontSize, 17716 style: fontStyle, 17717 family: fontFamily, 17718 font: font 17719 }; 17720 } 17721 17722 function measureLabelSize(ctx, fontSize, label) { 17723 if (helpers.isArray(label)) { 17724 return { 17725 w: helpers.longestText(ctx, ctx.font, label), 17726 h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) 17727 }; 17728 } 17729 17730 return { 17731 w: ctx.measureText(label).width, 17732 h: fontSize 17733 }; 17734 } 17735 17736 function determineLimits(angle, pos, size, min, max) { 17737 if (angle === min || angle === max) { 17738 return { 17739 start: pos - (size / 2), 17740 end: pos + (size / 2) 17741 }; 17742 } else if (angle < min || angle > max) { 17743 return { 17744 start: pos - size - 5, 17745 end: pos 17746 }; 17747 } 17748 17749 return { 17750 start: pos, 17751 end: pos + size + 5 17752 }; 17753 } 17754 17755 /** 17756 * Helper function to fit a radial linear scale with point labels 17757 */ 17758 function fitWithPointLabels(scale) { 17759 /* 17760 * Right, this is really confusing and there is a lot of maths going on here 17761 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 17762 * 17763 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif 17764 * 17765 * Solution: 17766 * 17767 * We assume the radius of the polygon is half the size of the canvas at first 17768 * at each index we check if the text overlaps. 17769 * 17770 * Where it does, we store that angle and that index. 17771 * 17772 * After finding the largest index and angle we calculate how much we need to remove 17773 * from the shape radius to move the point inwards by that x. 17774 * 17775 * We average the left and right distances to get the maximum shape radius that can fit in the box 17776 * along with labels. 17777 * 17778 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion 17779 * on each side, removing that from the size, halving it and adding the left x protrusion width. 17780 * 17781 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels 17782 * and position it in the most space efficient manner 17783 * 17784 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif 17785 */ 17786 17787 var plFont = getPointLabelFontOptions(scale); 17788 17789 // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. 17790 // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points 17791 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); 17792 var furthestLimits = { 17793 r: scale.width, 17794 l: 0, 17795 t: scale.height, 17796 b: 0 17797 }; 17798 var furthestAngles = {}; 17799 var i, textSize, pointPosition; 17800 17801 scale.ctx.font = plFont.font; 17802 scale._pointLabelSizes = []; 17803 17804 var valueCount = getValueCount(scale); 17805 for (i = 0; i < valueCount; i++) { 17806 pointPosition = scale.getPointPosition(i, largestPossibleRadius); 17807 textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); 17808 scale._pointLabelSizes[i] = textSize; 17809 17810 // Add quarter circle to make degree 0 mean top of circle 17811 var angleRadians = scale.getIndexAngle(i); 17812 var angle = helpers.toDegrees(angleRadians) % 360; 17813 var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); 17814 var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); 17815 17816 if (hLimits.start < furthestLimits.l) { 17817 furthestLimits.l = hLimits.start; 17818 furthestAngles.l = angleRadians; 17819 } 17820 17821 if (hLimits.end > furthestLimits.r) { 17822 furthestLimits.r = hLimits.end; 17823 furthestAngles.r = angleRadians; 17824 } 17825 17826 if (vLimits.start < furthestLimits.t) { 17827 furthestLimits.t = vLimits.start; 17828 furthestAngles.t = angleRadians; 17829 } 17830 17831 if (vLimits.end > furthestLimits.b) { 17832 furthestLimits.b = vLimits.end; 17833 furthestAngles.b = angleRadians; 17834 } 17835 } 17836 17837 scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); 17838 } 17839 17840 /** 17841 * Helper function to fit a radial linear scale with no point labels 17842 */ 17843 function fit(scale) { 17844 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); 17845 scale.drawingArea = Math.round(largestPossibleRadius); 17846 scale.setCenterPoint(0, 0, 0, 0); 17847 } 17848 17849 function getTextAlignForAngle(angle) { 17850 if (angle === 0 || angle === 180) { 17851 return 'center'; 17852 } else if (angle < 180) { 17853 return 'left'; 17854 } 17855 17856 return 'right'; 17857 } 17858 17859 function fillText(ctx, text, position, fontSize) { 17860 if (helpers.isArray(text)) { 17861 var y = position.y; 17862 var spacing = 1.5 * fontSize; 17863 17864 for (var i = 0; i < text.length; ++i) { 17865 ctx.fillText(text[i], position.x, y); 17866 y += spacing; 17867 } 17868 } else { 17869 ctx.fillText(text, position.x, position.y); 17870 } 17871 } 17872 17873 function adjustPointPositionForLabelHeight(angle, textSize, position) { 17874 if (angle === 90 || angle === 270) { 17875 position.y -= (textSize.h / 2); 17876 } else if (angle > 270 || angle < 90) { 17877 position.y -= textSize.h; 17878 } 17879 } 17880 17881 function drawPointLabels(scale) { 17882 var ctx = scale.ctx; 17883 var opts = scale.options; 17884 var angleLineOpts = opts.angleLines; 17885 var pointLabelOpts = opts.pointLabels; 17886 17887 ctx.lineWidth = angleLineOpts.lineWidth; 17888 ctx.strokeStyle = angleLineOpts.color; 17889 17890 var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); 17891 17892 // Point Label Font 17893 var plFont = getPointLabelFontOptions(scale); 17894 17895 ctx.textBaseline = 'top'; 17896 17897 for (var i = getValueCount(scale) - 1; i >= 0; i--) { 17898 if (angleLineOpts.display) { 17899 var outerPosition = scale.getPointPosition(i, outerDistance); 17900 ctx.beginPath(); 17901 ctx.moveTo(scale.xCenter, scale.yCenter); 17902 ctx.lineTo(outerPosition.x, outerPosition.y); 17903 ctx.stroke(); 17904 ctx.closePath(); 17905 } 17906 17907 if (pointLabelOpts.display) { 17908 // Extra 3px out for some label spacing 17909 var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); 17910 17911 // Keep this in loop since we may support array properties here 17912 var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor); 17913 ctx.font = plFont.font; 17914 ctx.fillStyle = pointLabelFontColor; 17915 17916 var angleRadians = scale.getIndexAngle(i); 17917 var angle = helpers.toDegrees(angleRadians); 17918 ctx.textAlign = getTextAlignForAngle(angle); 17919 adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); 17920 fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); 17921 } 17922 } 17923 } 17924 17925 function drawRadiusLine(scale, gridLineOpts, radius, index) { 17926 var ctx = scale.ctx; 17927 ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); 17928 ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); 17929 17930 if (scale.options.gridLines.circular) { 17931 // Draw circular arcs between the points 17932 ctx.beginPath(); 17933 ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); 17934 ctx.closePath(); 17935 ctx.stroke(); 17936 } else { 17937 // Draw straight lines connecting each index 17938 var valueCount = getValueCount(scale); 17939
17940 if (valueCount === 0) { 17941 return; 17942 } 17943 17944 ctx.beginPath(); 17945 var pointPosition = scale.getPointPosition(0, radius); 17946 ctx.moveTo(pointPosition.x, pointPosition.y); 17947 17948 for (var i = 1; i < valueCount; i++) { 17949 pointPosition = scale.getPointPosition(i, radius); 17950 ctx.lineTo(pointPosition.x, pointPosition.y); 17951 } 17952 17953 ctx.closePath(); 17954 ctx.stroke(); 17955 } 17956 } 17957 17958 function numberOrZero(param) { 17959 return helpers.isNumber(param) ? param : 0; 17960 } 17961 17962 var LinearRadialScale = Chart.LinearScaleBase.extend({ 17963 setDimensions: function() { 17964 var me = this; 17965 var opts = me.options; 17966 var tickOpts = opts.ticks; 17967 // Set the unconstrained dimension before label rotation 17968 me.width = me.maxWidth; 17969 me.height = me.maxHeight; 17970 me.xCenter = Math.round(me.width / 2); 17971 me.yCenter = Math.round(me.height / 2); 17972 17973 var minSize = helpers.min([me.height, me.width]); 17974 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 17975 me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); 17976 }, 17977 determineDataLimits: function() { 17978 var me = this; 17979 var chart = me.chart; 17980 var min = Number.POSITIVE_INFINITY; 17981 var max = Number.NEGATIVE_INFINITY; 17982 17983 helpers.each(chart.data.datasets, function(dataset, datasetIndex) { 17984 if (chart.isDatasetVisible(datasetIndex)) { 17985 var meta = chart.getDatasetMeta(datasetIndex); 17986 17987 helpers.each(dataset.data, function(rawValue, index) { 17988 var value = +me.getRightValue(rawValue); 17989 if (isNaN(value) || meta.data[index].hidden) { 17990 return; 17991 } 17992 17993 min = Math.min(value, min); 17994 max = Math.max(value, max); 17995 }); 17996 } 17997 }); 17998 17999 me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); 18000 me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); 18001 18002 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero 18003 me.handleTickRangeOptions(); 18004 }, 18005 getTickLimit: function() { 18006 var tickOpts = this.options.ticks; 18007 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 18008 return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); 18009 }, 18010 convertTicksToLabels: function() { 18011 var me = this; 18012 18013 Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); 18014 18015 // Point labels 18016 me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); 18017 }, 18018 getLabelForIndex: function(index, datasetIndex) { 18019 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 18020 }, 18021 fit: function() { 18022 if (this.options.pointLabels.display) { 18023 fitWithPointLabels(this); 18024 } else { 18025 fit(this); 18026 } 18027 }, 18028 /** 18029 * Set radius reductions and determine new radius and center point 18030 * @private 18031 */ 18032 setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { 18033 var me = this; 18034 var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); 18035 var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); 18036 var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); 18037 var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); 18038 18039 radiusReductionLeft = numberOrZero(radiusReductionLeft); 18040 radiusReductionRight = numberOrZero(radiusReductionRight); 18041 radiusReductionTop = numberOrZero(radiusReductionTop); 18042 radiusReductionBottom = numberOrZero(radiusReductionBottom); 18043 18044 me.drawingArea = Math.min( 18045 Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), 18046 Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); 18047 me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); 18048 }, 18049 setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { 18050 var me = this; 18051 var maxRight = me.width - rightMovement - me.drawingArea; 18052 var maxLeft = leftMovement + me.drawingArea; 18053 var maxTop = topMovement + me.drawingArea; 18054 var maxBottom = me.height - bottomMovement - me.drawingArea; 18055 18056 me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); 18057 me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); 18058 }, 18059 18060 getIndexAngle: function(index) { 18061 var angleMultiplier = (Math.PI * 2) / getValueCount(this); 18062 var startAngle = this.chart.options && this.chart.options.startAngle ? 18063 this.chart.options.startAngle : 18064 0; 18065 18066 var startAngleRadians = startAngle * Math.PI * 2 / 360; 18067 18068 // Start from the top instead of right, so remove a quarter of the circle 18069 return index * angleMultiplier + startAngleRadians; 18070 }, 18071 getDistanceFromCenterForValue: function(value) { 18072 var me = this; 18073 18074 if (value === null) { 18075 return 0; // null always in center 18076 } 18077 18078 // Take into account half font size + the yPadding of the top value 18079 var scalingFactor = me.drawingArea / (me.max - me.min); 18080 if (me.options.ticks.reverse) { 18081 return (me.max - value) * scalingFactor; 18082 } 18083 return (value - me.min) * scalingFactor; 18084 }, 18085 getPointPosition: function(index, distanceFromCenter) { 18086 var me = this; 18087 var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); 18088 return { 18089 x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, 18090 y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter 18091 }; 18092 }, 18093 getPointPositionForValue: function(index, value) { 18094 return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); 18095 }, 18096 18097 getBasePosition: function() { 18098 var me = this; 18099 var min = me.min; 18100 var max = me.max; 18101 18102 return me.getPointPositionForValue(0, 18103 me.beginAtZero ? 0 : 18104 min < 0 && max < 0 ? max : 18105 min > 0 && max > 0 ? min : 18106 0); 18107 }, 18108 18109 draw: function() { 18110 var me = this; 18111 var opts = me.options; 18112 var gridLineOpts = opts.gridLines; 18113 var tickOpts = opts.ticks; 18114 var valueOrDefault = helpers.valueOrDefault; 18115 18116 if (opts.display) { 18117 var ctx = me.ctx; 18118 var startAngle = this.getIndexAngle(0); 18119 18120 // Tick Font
18121 var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 18122 var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); 18123 var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); 18124 var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); 18125 18126 helpers.each(me.ticks, function(label, index) { 18127 // Don't draw a centre value (if it is minimum) 18128 if (index > 0 || tickOpts.reverse) { 18129 var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); 18130 18131 // Draw circular lines around the scale 18132 if (gridLineOpts.display && index !== 0) { 18133 drawRadiusLine(me, gridLineOpts, yCenterOffset, index); 18134 } 18135 18136 if (tickOpts.display) { 18137 var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); 18138 ctx.font = tickLabelFont; 18139 18140 ctx.save(); 18141 ctx.translate(me.xCenter, me.yCenter); 18142 ctx.rotate(startAngle); 18143 18144 if (tickOpts.showLabelBackdrop) { 18145 var labelWidth = ctx.measureText(label).width; 18146 ctx.fillStyle = tickOpts.backdropColor; 18147 ctx.fillRect( 18148 -labelWidth / 2 - tickOpts.backdropPaddingX, 18149 -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, 18150 labelWidth + tickOpts.backdropPaddingX * 2, 18151 tickFontSize + tickOpts.backdropPaddingY * 2 18152 ); 18153 } 18154 18155 ctx.textAlign = 'center'; 18156 ctx.textBaseline = 'middle'; 18157 ctx.fillStyle = tickFontColor; 18158 ctx.fillText(label, 0, -yCenterOffset); 18159 ctx.restore(); 18160 } 18161 } 18162 }); 18163 18164 if (opts.angleLines.display || opts.pointLabels.display) { 18165 drawPointLabels(me); 18166 } 18167 } 18168 } 18169 }); 18170 18171 scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); 18172}; 18173 18174},{"26":26,"34":34,"35":35,"46":46}],59:[function(require,module,exports){ 18175/* global window: false */ 18176'use strict'; 18177 18178var moment = require(6); 18179moment = typeof moment === 'function' ? moment : window.moment; 18180 18181var defaults = require(26); 18182var helpers = require(46); 18183var Scale = require(33); 18184var scaleService = require(34); 18185 18186// Integer constants are from the ES6 spec. 18187var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; 18188var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; 18189 18190var INTERVALS = { 18191 millisecond: { 18192 common: true, 18193 size: 1, 18194 steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] 18195 }, 18196 second: { 18197 common: true, 18198 size: 1000, 18199 steps: [1, 2, 5, 10, 15, 30] 18200 }, 18201 minute: { 18202 common: true, 18203 size: 60000, 18204 steps: [1, 2, 5, 10, 15, 30] 18205 }, 18206 hour: { 18207 common: true, 18208 size: 3600000, 18209 steps: [1, 2, 3, 6, 12] 18210 }, 18211 day: { 18212 common: true, 18213 size: 86400000, 18214 steps: [1, 2, 5] 18215 }, 18216 week: { 18217 common: false, 18218 size: 604800000, 18219 steps: [1, 2, 3, 4] 18220 }, 18221 month: { 18222 common: true, 18223 size: 2.628e9, 18224 steps: [1, 2, 3] 18225 }, 18226 quarter: { 18227 common: false, 18228 size: 7.884e9, 18229 steps: [1, 2, 3, 4] 18230 }, 18231 year: { 18232 common: true, 18233 size: 3.154e10 18234 } 18235}; 18236 18237var UNITS = Object.keys(INTERVALS); 18238 18239function sorter(a, b) { 18240 return a - b; 18241} 18242 18243function arrayUnique(items) { 18244 var hash = {}; 18245 var out = []; 18246 var i, ilen, item; 18247 18248 for (i = 0, ilen = items.length; i < ilen; ++i) { 18249 item = items[i]; 18250 if (!hash[item]) { 18251 hash[item] = true; 18252 out.push(item); 18253 } 18254 } 18255 18256 return out; 18257} 18258 18259/** 18260 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position 18261 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is 18262 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other 18263 * extremity (left + width or top + height). Note that it would be more optimized to directly 18264 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need 18265 * to create the lookup table. The table ALWAYS contains at least two items: min and max. 18266 * 18267 * @param {Number[]} timestamps - timestamps sorted from lowest to highest. 18268 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min 18269 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. 18270 * If 'series', timestamps will be positioned at the same distance from each other. In this 18271 * case, only timestamps that break the time linearity are registered, meaning that in the 18272 * best case, all timestamps are linear, the table contains only min and max. 18273 */ 18274function buildLookupTable(timestamps, min, max, distribution) { 18275 if (distribution === 'linear' || !timestamps.length) { 18276 return [ 18277 {time: min, pos: 0}, 18278 {time: max, pos: 1} 18279 ]; 18280 } 18281 18282 var table = []; 18283 var items = [min]; 18284 var i, ilen, prev, curr, next; 18285 18286 for (i = 0, ilen = timestamps.length; i < ilen; ++i) { 18287 curr = timestamps[i]; 18288 if (curr > min && curr < max) { 18289 items.push(curr); 18290 } 18291 } 18292 18293 items.push(max); 18294 18295 for (i = 0, ilen = items.length; i < ilen; ++i) { 18296 next = items[i + 1]; 18297 prev = items[i - 1]; 18298 curr = items[i]; 18299 18300 // only add points that breaks the scale linearity 18301 if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { 18302 table.push({time: curr, pos: i / (ilen - 1)}); 18303 } 18304 } 18305 18306 return table; 18307} 18308 18309// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ 18310function lookup(table, key, value) { 18311 var lo = 0; 18312 var hi = table.length - 1; 18313 var mid, i0, i1; 18314 18315 while (lo >= 0 && lo <= hi) { 18316 mid = (lo + hi) >> 1; 18317 i0 = table[mid - 1] || null; 18318 i1 = table[mid]; 18319 18320 if (!i0) { 18321 // given value is outside table (before first item) 18322 return {lo: null, hi: i1}; 18323 } else if (i1[key] < value) { 18324 lo = mid + 1; 18325 } else if (i0[key] > value) {
18326 hi = mid - 1; 18327 } else { 18328 return {lo: i0, hi: i1}; 18329 } 18330 } 18331 18332 // given value is outside table (after last item) 18333 return {lo: i1, hi: null}; 18334} 18335 18336/** 18337 * Linearly interpolates the given source `value` using the table items `skey` values and 18338 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') 18339 * returns the position for a timestamp equal to 42. If value is out of bounds, values at 18340 * index [0, 1] or [n - 1, n] are used for the interpolation. 18341 */ 18342function interpolate(table, skey, sval, tkey) { 18343 var range = lookup(table, skey, sval); 18344 18345 // Note: the lookup table ALWAYS contains at least 2 items (min and max) 18346 var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; 18347 var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; 18348 18349 var span = next[skey] - prev[skey]; 18350 var ratio = span ? (sval - prev[skey]) / span : 0; 18351 var offset = (next[tkey] - prev[tkey]) * ratio; 18352 18353 return prev[tkey] + offset; 18354} 18355 18356/** 18357 * Convert the given value to a moment object using the given time options. 18358 * @see http://momentjs.com/docs/#/parsing/ 18359 */ 18360function momentify(value, options) { 18361 var parser = options.parser; 18362 var format = options.parser || options.format; 18363 18364 if (typeof parser === 'function') { 18365 return parser(value); 18366 } 18367 18368 if (typeof value === 'string' && typeof format === 'string') { 18369 return moment(value, format); 18370 } 18371 18372 if (!(value instanceof moment)) { 18373 value = moment(value); 18374 } 18375 18376 if (value.isValid()) { 18377 return value; 18378 } 18379 18380 // Labels are in an incompatible moment format and no `parser` has been provided. 18381 // The user might still use the deprecated `format` option to convert his inputs. 18382 if (typeof format === 'function') { 18383 return format(value); 18384 } 18385 18386 return value; 18387} 18388 18389function parse(input, scale) { 18390 if (helpers.isNullOrUndef(input)) { 18391 return null; 18392 } 18393 18394 var options = scale.options.time; 18395 var value = momentify(scale.getRightValue(input), options); 18396 if (!value.isValid()) { 18397 return null; 18398 } 18399 18400 if (options.round) { 18401 value.startOf(options.round); 18402 } 18403 18404 return value.valueOf(); 18405} 18406 18407/** 18408 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks 18409 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. 18410 */ 18411function determineStepSize(min, max, unit, capacity) { 18412 var range = max - min; 18413 var interval = INTERVALS[unit]; 18414 var milliseconds = interval.size; 18415 var steps = interval.steps; 18416 var i, ilen, factor; 18417 18418 if (!steps) { 18419 return Math.ceil(range / (capacity * milliseconds)); 18420 } 18421 18422 for (i = 0, ilen = steps.length; i < ilen; ++i) { 18423 factor = steps[i]; 18424 if (Math.ceil(range / (milliseconds * factor)) <= capacity) { 18425 break; 18426 } 18427 } 18428 18429 return factor; 18430} 18431 18432/** 18433 * Figures out what unit results in an appropriate number of auto-generated ticks 18434 */ 18435function determineUnitForAutoTicks(minUnit, min, max, capacity) { 18436 var ilen = UNITS.length; 18437 var i, interval, factor; 18438 18439 for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { 18440 interval = INTERVALS[UNITS[i]]; 18441 factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; 18442 18443 if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { 18444 return UNITS[i]; 18445 } 18446 } 18447 18448 return UNITS[ilen - 1]; 18449} 18450 18451/** 18452 * Figures out what unit to format a set of ticks with 18453 */ 18454function determineUnitForFormatting(ticks, minUnit, min, max) { 18455 var duration = moment.duration(moment(max).diff(moment(min))); 18456 var ilen = UNITS.length; 18457 var i, unit; 18458 18459 for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { 18460 unit = UNITS[i]; 18461 if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) { 18462 return unit; 18463 } 18464 } 18465 18466 return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; 18467} 18468 18469function determineMajorUnit(unit) { 18470 for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { 18471 if (INTERVALS[UNITS[i]].common) { 18472 return UNITS[i]; 18473 } 18474 } 18475} 18476 18477/** 18478 * Generates a maximum of `capacity` timestamps between min and max, rounded to the 18479 * `minor` unit, aligned on the `major` unit and using the given scale time `options`. 18480 * Important: this method can return ticks outside the min and max range, it's the 18481 * responsibility of the calling code to clamp values if needed. 18482 */ 18483function generate(min, max, capacity, options) { 18484 var timeOpts = options.time; 18485 var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); 18486 var major = determineMajorUnit(minor); 18487 var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); 18488 var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
18489 var majorTicksEnabled = options.ticks.major.enabled; 18490 var interval = INTERVALS[minor]; 18491 var first = moment(min); 18492 var last = moment(max); 18493 var ticks = []; 18494 var time; 18495 18496 if (!stepSize) { 18497 stepSize = determineStepSize(min, max, minor, capacity); 18498 } 18499 18500 // For 'week' unit, handle the first day of week option 18501 if (weekday) { 18502 first = first.isoWeekday(weekday); 18503 last = last.isoWeekday(weekday); 18504 } 18505 18506 // Align first/last ticks on unit 18507 first = first.startOf(weekday ? 'day' : minor); 18508 last = last.startOf(weekday ? 'day' : minor); 18509 18510 // Make sure that the last tick include max 18511 if (last < max) { 18512 last.add(1, minor); 18513 } 18514 18515 time = moment(first); 18516 18517 if (majorTicksEnabled && major && !weekday && !timeOpts.round) { 18518 // Align the first tick on the previous `minor` unit aligned on the `major` unit: 18519 // we first aligned time on the previous `major` unit then add the number of full 18520 // stepSize there is between first and the previous major time. 18521 time.startOf(major); 18522 time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); 18523 } 18524 18525 for (; time < last; time.add(stepSize, minor)) { 18526 ticks.push(+time); 18527 } 18528 18529 ticks.push(+time); 18530 18531 return ticks; 18532} 18533 18534/** 18535 * Returns the right and left offsets from edges in the form of {left, right}. 18536 * Offsets are added when the `offset` option is true. 18537 */ 18538function computeOffsets(table, ticks, min, max, options) { 18539 var left = 0; 18540 var right = 0; 18541 var upper, lower; 18542 18543 if (options.offset && ticks.length) { 18544 if (!options.time.min) { 18545 upper = ticks.length > 1 ? ticks[1] : max; 18546 lower = ticks[0]; 18547 left = ( 18548 interpolate(table, 'time', upper, 'pos') - 18549 interpolate(table, 'time', lower, 'pos') 18550 ) / 2; 18551 } 18552 if (!options.time.max) { 18553 upper = ticks[ticks.length - 1]; 18554 lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; 18555 right = ( 18556 interpolate(table, 'time', upper, 'pos') - 18557 interpolate(table, 'time', lower, 'pos') 18558 ) / 2; 18559 } 18560 } 18561 18562 return {left: left, right: right}; 18563} 18564 18565function ticksFromTimestamps(values, majorUnit) { 18566 var ticks = []; 18567 var i, ilen, value, major; 18568 18569 for (i = 0, ilen = values.length; i < ilen; ++i) { 18570 value = values[i]; 18571 major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; 18572 18573 ticks.push({ 18574 value: value, 18575 major: major 18576 }); 18577 } 18578 18579 return ticks; 18580} 18581 18582function determineLabelFormat(data, timeOpts) { 18583 var i, momentDate, hasTime; 18584 var ilen = data.length; 18585 18586 // find the label with the most parts (milliseconds, minutes, etc.) 18587 // format all labels with the same level of detail as the most specific label 18588 for (i = 0; i < ilen; i++) { 18589 momentDate = momentify(data[i], timeOpts); 18590 if (momentDate.millisecond() !== 0) { 18591 return 'MMM D, YYYY h:mm:ss.SSS a'; 18592 } 18593 if (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) { 18594 hasTime = true; 18595 } 18596 } 18597 if (hasTime) { 18598 return 'MMM D, YYYY h:mm:ss a'; 18599 } 18600 return 'MMM D, YYYY'; 18601} 18602 18603module.exports = function() { 18604 18605 var defaultConfig = { 18606 position: 'bottom', 18607 18608 /** 18609 * Data distribution along the scale: 18610 * - 'linear': data are spread according to their time (distances can vary), 18611 * - 'series': data are spread at the same distance from each other. 18612 * @see https://github.com/chartjs/Chart.js/pull/4507 18613 * @since 2.7.0 18614 */ 18615 distribution: 'linear', 18616 18617 /** 18618 * Scale boundary strategy (bypassed by min/max time options) 18619 * - `data`: make sure data are fully visible, ticks outside are removed 18620 * - `ticks`: make sure ticks are fully visible, data outside are truncated 18621 * @see https://github.com/chartjs/Chart.js/pull/4556 18622 * @since 2.7.0 18623 */ 18624 bounds: 'data', 18625 18626 time: { 18627 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 18628 format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ 18629 unit: false, // false == automatic or override with week, month, year, etc. 18630 round: false, // none, or override with week, month, year, etc. 18631 displayFormat: false, // DEPRECATED 18632 isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ 18633 minUnit: 'millisecond', 18634 18635 // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ 18636 displayFormats: { 18637 millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, 18638 second: 'h:mm:ss a', // 11:20:01 AM 18639 minute: 'h:mm a', // 11:20 AM 18640 hour: 'hA', // 5PM 18641 day: 'MMM D', // Sep 4 18642 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? 18643 month: 'MMM YYYY', // Sept 2015 18644 quarter: '[Q]Q - YYYY', // Q3 18645 year: 'YYYY' // 2015 18646 }, 18647 }, 18648 ticks: { 18649 autoSkip: false, 18650 18651 /** 18652 * Ticks generation input values: 18653 * - 'auto': generates "optimal" ticks based on scale size and time options. 18654 * - 'data': generates ticks from data (including labels from data {t|x|y} objects). 18655 * - 'labels': generates ticks from user given `data.labels` values ONLY. 18656 * @see https://github.com/chartjs/Chart.js/pull/4507 18657 * @since 2.7.0 18658 */ 18659 source: 'auto', 18660 18661 major: { 18662 enabled: false 18663 } 18664 } 18665 }; 18666 18667 var TimeScale = Scale.extend({ 18668 initialize: function() { 18669 if (!moment) { 18670 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'); 18671 } 18672 18673 this.mergeTicksOptions(); 18674 18675 Scale.prototype.initialize.call(this); 18676 }, 18677 18678 update: function() { 18679 var me = this; 18680 var options = me.options; 18681 18682 // DEPRECATIONS: output a message only one time per update 18683 if (options.time && options.time.format) { 18684 console.warn('options.time.format is deprecated and replaced by options.time.parser.'); 18685 } 18686 18687 return Scale.prototype.update.apply(me, arguments); 18688 }, 18689 18690 /** 18691 * Allows data to be referenced via 't' attribute 18692 */ 18693 getRightValue: function(rawValue) { 18694 if (rawValue && rawValue.t !== undefined) { 18695 rawValue = rawValue.t; 18696 } 18697 return Scale.prototype.getRightValue.call(this, rawValue); 18698 }, 18699 18700 determineDataLimits: function() { 18701 var me = this; 18702 var chart = me.chart; 18703 var timeOpts = me.options.time; 18704 var unit = timeOpts.unit || 'day'; 18705 var min = MAX_INTEGER; 18706 var max = MIN_INTEGER; 18707 var timestamps = []; 18708 var datasets = []; 18709 var labels = []; 18710 var i, j, ilen, jlen, data, timestamp; 18711 18712 // Convert labels to timestamps 18713 for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { 18714 labels.push(parse(chart.data.labels[i], me)); 18715 } 18716 18717 // Convert data to timestamps 18718 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 18719 if (chart.isDatasetVisible(i)) { 18720 data = chart.data.datasets[i].data; 18721 18722 // Let's consider that all data have the same format. 18723 if (helpers.isObject(data[0])) { 18724 datasets[i] = []; 18725 18726 for (j = 0, jlen = data.length; j < jlen; ++j) { 18727 timestamp = parse(data[j], me); 18728 timestamps.push(timestamp); 18729 datasets[i][j] = timestamp; 18730 } 18731 } else { 18732 timestamps.push.apply(timestamps, labels); 18733 datasets[i] = labels.slice(0); 18734 } 18735 } else { 18736 datasets[i] = []; 18737 } 18738 } 18739 18740 if (labels.length) { 18741 // Sort labels **after** data have been converted 18742 labels = arrayUnique(labels).sort(sorter); 18743 min = Math.min(min, labels[0]); 18744 max = Math.max(max, labels[labels.length - 1]); 18745 } 18746 18747 if (timestamps.length) { 18748 timestamps = arrayUnique(timestamps).sort(sorter); 18749 min = Math.min(min, timestamps[0]); 18750 max = Math.max(max, timestamps[timestamps.length - 1]); 18751 } 18752 18753 min = parse(timeOpts.min, me) || min; 18754 max = parse(timeOpts.max, me) || max; 18755 18756 // In case there is no valid min/max, set limits based on unit time option 18757 min = min === MAX_INTEGER ? +moment().startOf(unit) : min; 18758 max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max; 18759 18760 // Make sure that max is strictly higher than min (required by the lookup table) 18761 me.min = Math.min(min, max); 18762 me.max = Math.max(min + 1, max); 18763 18764 // PRIVATE 18765 me._horizontal = me.isHorizontal(); 18766 me._table = []; 18767 me._timestamps = { 18768 data: timestamps, 18769 datasets: datasets, 18770 labels: labels 18771 }; 18772 }, 18773 18774 buildTicks: function() { 18775 var me = this; 18776 var min = me.min; 18777 var max = me.max; 18778 var options = me.options; 18779 var timeOpts = options.time; 18780 var timestamps = []; 18781 var ticks = []; 18782 var i, ilen, timestamp; 18783 18784 switch (options.ticks.source) { 18785 case 'data': 18786 timestamps = me._timestamps.data; 18787 break; 18788 case 'labels': 18789 timestamps = me._timestamps.labels; 18790 break; 18791 case 'auto': 18792 default: 18793 timestamps = generate(min, max, me.getLabelCapacity(min), options); 18794 } 18795 18796 if (options.bounds === 'ticks' && timestamps.length) { 18797 min = timestamps[0]; 18798 max = timestamps[timestamps.length - 1]; 18799 } 18800 18801 // Enforce limits with user min/max options 18802 min = parse(timeOpts.min, me) || min; 18803 max = parse(timeOpts.max, me) || max; 18804 18805 // Remove ticks outside the min/max range 18806 for (i = 0, ilen = timestamps.length; i < ilen; ++i) { 18807 timestamp = timestamps[i]; 18808 if (timestamp >= min && timestamp <= max) { 18809 ticks.push(timestamp); 18810 } 18811 } 18812 18813 me.min = min; 18814 me.max = max; 18815 18816 // PRIVATE 18817 me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); 18818 me._majorUnit = determineMajorUnit(me._unit); 18819 me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); 18820 me._offsets = computeOffsets(me._table, ticks, min, max, options); 18821 me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts); 18822 18823 return ticksFromTimestamps(ticks, me._majorUnit); 18824 }, 18825 18826 getLabelForIndex: function(index, datasetIndex) { 18827 var me = this; 18828 var data = me.chart.data; 18829 var timeOpts = me.options.time; 18830 var label = data.labels && index < data.labels.length ? data.labels[index] : ''; 18831 var value = data.datasets[datasetIndex].data[index]; 18832 18833 if (helpers.isObject(value)) { 18834 label = me.getRightValue(value); 18835 } 18836 if (timeOpts.tooltipFormat) { 18837 return momentify(label, timeOpts).format(timeOpts.tooltipFormat); 18838 } 18839 if (typeof label === 'string') { 18840 return label; 18841 } 18842 18843 return momentify(label, timeOpts).format(me._labelFormat); 18844 }, 18845 18846 /** 18847 * Function to format an individual tick mark 18848 * @private 18849 */ 18850 tickFormatFunction: function(tick, index, ticks, formatOverride) { 18851 var me = this; 18852 var options = me.options; 18853 var time = tick.valueOf(); 18854 var formats = options.time.displayFormats; 18855 var minorFormat = formats[me._unit]; 18856 var majorUnit = me._majorUnit; 18857 var majorFormat = formats[majorUnit]; 18858 var majorTime = tick.clone().startOf(majorUnit).valueOf(); 18859 var majorTickOpts = options.ticks.major; 18860 var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; 18861 var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat); 18862 var tickOpts = major ? majorTickOpts : options.ticks.minor; 18863 var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); 18864 18865 return formatter ? formatter(label, index, ticks) : label; 18866 }, 18867 18868 convertTicksToLabels: function(ticks) { 18869 var labels = []; 18870 var i, ilen; 18871 18872 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 18873 labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); 18874 } 18875 18876 return labels; 18877 }, 18878 18879 /** 18880 * @private 18881 */ 18882 getPixelForOffset: function(time) { 18883 var me = this; 18884 var size = me._horizontal ? me.width : me.height; 18885 var start = me._horizontal ? me.left : me.top; 18886 var pos = interpolate(me._table, 'time', time, 'pos'); 18887 18888 return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); 18889 }, 18890 18891 getPixelForValue: function(value, index, datasetIndex) { 18892 var me = this; 18893 var time = null; 18894 18895 if (index !== undefined && datasetIndex !== undefined) { 18896 time = me._timestamps.datasets[datasetIndex][index]; 18897 } 18898 18899 if (time === null) { 18900 time = parse(value, me); 18901 } 18902 18903 if (time !== null) { 18904 return me.getPixelForOffset(time); 18905 } 18906 }, 18907 18908 getPixelForTick: function(index) { 18909 var ticks = this.getTicks(); 18910 return index >= 0 && index < ticks.length ? 18911 this.getPixelForOffset(ticks[index].value) : 18912 null; 18913 }, 18914 18915 getValueForPixel: function(pixel) { 18916 var me = this; 18917 var size = me._horizontal ? me.width : me.height; 18918 var start = me._horizontal ? me.left : me.top; 18919 var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; 18920 var time = interpolate(me._table, 'pos', pos, 'time'); 18921 18922 return moment(time); 18923 }, 18924 18925 /** 18926 * Crude approximation of what the label width might be 18927 * @private 18928 */ 18929 getLabelWidth: function(label) { 18930 var me = this; 18931 var ticksOpts = me.options.ticks; 18932 var tickLabelWidth = me.ctx.measureText(label).width; 18933 var angle = helpers.toRadians(ticksOpts.maxRotation); 18934 var cosRotation = Math.cos(angle); 18935 var sinRotation = Math.sin(angle);
18936 var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); 18937 18938 return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); 18939 }, 18940 18941 /** 18942 * @private 18943 */ 18944 getLabelCapacity: function(exampleTime) { 18945 var me = this; 18946 18947 var formatOverride = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation 18948 18949 var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride); 18950 var tickLabelWidth = me.getLabelWidth(exampleLabel); 18951 var innerWidth = me.isHorizontal() ? me.width : me.height; 18952 18953 var capacity = Math.floor(innerWidth / tickLabelWidth); 18954 return capacity > 0 ? capacity : 1; 18955 } 18956 }); 18957 18958 scaleService.registerScaleType('time', TimeScale, defaultConfig); 18959}; 18960 18961},{"26":26,"33":33,"34":34,"46":46,"6":6}]},{},[7])(7) 18962});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.