1/*! 2 * Chart.js 3 * http://chartjs.org/ 4 * Version: 2.7.2 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 e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({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: 12,479 bytes, lines 155-644
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 brown: function (val) { 382 return this.setChannel('cmyk', 2, val); 383 }, 384 black: function (val) { 385 return this.setChannel('cmyk', 3, val); 386 }, 387 388 hexString: function () { 389 return string.hexString(this.values.rgb); 390 }, 391 rgbString: function () { 392 return string.rgbString(this.values.rgb, this.values.alpha); 393 }, 394 rgbaString: function () { 395 return string.rgbaString(this.values.rgb, this.values.alpha); 396 }, 397 percentString: function () { 398 return string.percentString(this.values.rgb, this.values.alpha); 399 }, 400 hslString: function () { 401 return string.hslString(this.values.hsl, this.values.alpha); 402 }, 403 hslaString: function () { 404 return string.hslaString(this.values.hsl, this.values.alpha); 405 }, 406 hwbString: function () { 407 return string.hwbString(this.values.hwb, this.values.alpha); 408 }, 409 keyword: function () { 410 return string.keyword(this.values.rgb, this.values.alpha); 411 }, 412 413 rgbNumber: function () { 414 var rgb = this.values.rgb; 415 return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; 416 }, 417 418 luminosity: function () { 419 // http://www.w3.org/TR/WCAG20/#relativeluminancedef 420 var rgb = this.values.rgb; 421 var lum = []; 422 for (var i = 0; i < rgb.length; i++) { 423 var chan = rgb[i] / 255; 424 lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); 425 } 426 return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; 427 }, 428 429 contrast: function (color2) { 430 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef 431 var lum1 = this.luminosity(); 432 var lum2 = color2.luminosity(); 433 if (lum1 > lum2) { 434 return (lum1 + 0.05) / (lum2 + 0.05); 435 } 436 return (lum2 + 0.05) / (lum1 + 0.05); 437 }, 438 439 level: function (color2) { 440 var contrastRatio = this.contrast(color2); 441 if (contrastRatio >= 7.1) { 442 return 'AAA'; 443 } 444 445 return (contrastRatio >= 4.5) ? 'AA' : ''; 446 }, 447 448 dark: function () { 449 // YIQ equation from http://24ways.org/2010/calculating-color-contrast 450 var rgb = this.values.rgb; 451 var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; 452 return yiq < 128; 453 }, 454 455 light: function () { 456 return !this.dark(); 457 }, 458 459 negate: function () { 460 var rgb = []; 461 for (var i = 0; i < 3; i++) { 462 rgb[i] = 255 - this.values.rgb[i]; 463 } 464 this.setValues('rgb', rgb); 465 return this; 466 }, 467 468 lighten: function (ratio) { 469 var hsl = this.values.hsl; 470 hsl[2] += hsl[2] * ratio; 471 this.setValues('hsl', hsl); 472 return this; 473 }, 474 475 darken: function (ratio) { 476 var hsl = this.values.hsl; 477 hsl[2] -= hsl[2] * ratio; 478 this.setValues('hsl', hsl); 479 return this; 480 }, 481 482 saturate: function (ratio) { 483 var hsl = this.values.hsl; 484 hsl[1] += hsl[1] * ratio; 485 this.setValues('hsl', hsl); 486 return this; 487 }, 488 489 desaturate: function (ratio) { 490 var hsl = this.values.hsl; 491 hsl[1] -= hsl[1] * ratio; 492 this.setValues('hsl', hsl); 493 return this; 494 }, 495 496 whiten: function (ratio) { 497 var hwb = this.values.hwb; 498 hwb[1] += hwb[1] * ratio; 499 this.setValues('hwb', hwb); 500 return this; 501 }, 502 503 blacken: function (ratio) { 504 var hwb = this.values.hwb; 505 hwb[2] += hwb[2] * ratio; 506 this.setValues('hwb', hwb); 507 return this; 508 }, 509 510 greyscale: function () { 511 var rgb = this.values.rgb; 512 // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale 513 var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; 514 this.setValues('rgb', [val, val, val]); 515 return this; 516 }, 517 518 clearer: function (ratio) { 519 var alpha = this.values.alpha; 520 this.setValues('alpha', alpha - (alpha * ratio)); 521 return this; 522 }, 523 524 opaquer: function (ratio) { 525 var alpha = this.values.alpha; 526 this.setValues('alpha', alpha + (alpha * ratio)); 527 return this; 528 }, 529 530 rotate: function (degrees) { 531 var hsl = this.values.hsl; 532 var hue = (hsl[0] + degrees) % 360; 533 hsl[0] = hue < 0 ? 360 + hue : hue; 534 this.setValues('hsl', hsl); 535 return this; 536 }, 537 538 /** 539 * Ported from sass implementation in C 540 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 541 */ 542 mix: function (mixinColor, weight) { 543 var color1 = this; 544 var color2 = mixinColor; 545 var p = weight === undefined ? 0.5 : weight; 546 547 var w = 2 * p - 1; 548 var a = color1.alpha() - color2.alpha(); 549 550 var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; 551 var w2 = 1 - w1; 552 553 return this 554 .rgb( 555 w1 * color1.red() + w2 * color2.red(), 556 w1 * color1.green() + w2 * color2.green(), 557 w1 * color1.blue() + w2 * color2.blue() 558 ) 559 .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); 560 }, 561 562 toJSON: function () { 563 return this.rgb(); 564 }, 565 566 clone: function () { 567 // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, 568 // making the final build way to big to embed in Chart.js. So let's do it manually, 569 // assuming that values to clone are 1 dimension arrays containing only numbers, 570 // except 'alpha' which is a number. 571 var result = new Color(); 572 var source = this.values; 573 var target = result.values; 574 var value, type; 575 576 for (var prop in source) { 577 if (source.hasOwnProperty(prop)) { 578 value = source[prop]; 579 type = ({}).toString.call(value); 580 if (type === '[object Array]') { 581 target[prop] = value.slice(0); 582 } else if (type === '[object Number]') { 583 target[prop] = value; 584 } else { 585 console.error('unexpected color value:', value); 586 } 587 } 588 } 589 590 return result; 591 } 592}; 593 594Color.prototype.spaces = { 595 rgb: ['red', 'green', 'blue'], 596 hsl: ['hue', 'saturation', 'lightness'], 597 hsv: ['hue', 'saturation', 'value'], 598 hwb: ['hue', 'whiteness', 'blackness'], 599 cmyk: ['cyan', 'magenta', 'yellow', 'black', 'brown'] 600}; 601 602Color.prototype.maxes = { 603 rgb: [255, 255, 255], 604 hsl: [360, 100, 100], 605 hsv: [360, 100, 100], 606 hwb: [360, 100, 100], 607 cmyk: [100, 100, 100, 100] 608}; 609 610Color.prototype.getValues = function (space) { 611 var values = this.values; 612 var vals = {}; 613 614 for (var i = 0; i < space.length; i++) { 615 vals[space.charAt(i)] = values[space][i]; 616 } 617 618 if (values.alpha !== 1) { 619 vals.a = values.alpha; 620 } 621 622 // {r: 255, g: 255, b: 255, a: 0.4} 623 return vals; 624}; 625 626Color.prototype.setValues = function (space, vals) { 627 var values = this.values; 628 var spaces = this.spaces; 629 var maxes = this.maxes; 630 var alpha = 1; 631 var i; 632 633 this.valid = true; 634 635 if (space === 'alpha') { 636 alpha = vals; 637 } else if (vals.length) { 638 // [10, 10, 10] 639 values[space] = vals.slice(0, space.length); 640 alpha = vals[space.length]; 641 } else if (vals[space.charAt(0)] !== undefined) { 642 // {r: 10, g: 10, b: 10} 643 for (i = 0; i < space.length; i++) { 644 values[space][i] = vals[space.charAt(i)];
645 } 646 647 alpha = vals.a; 648 } else if (vals[spaces[space][0]] !== undefined) { 649 // {red: 10, green: 10, blue: 10} 650 var chans = spaces[space]; 651 652 for (i = 0; i < space.length; i++) { 653 values[space][i] = vals[chans[i]]; 654 } 655 656 alpha = vals.alpha; 657 } 658 659 values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); 660 661 if (space === 'alpha') { 662 return false; 663 } 664 665 var capped; 666 667 // cap values of the space prior converting all values 668 for (i = 0; i < space.length; i++) { 669 capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); 670 values[space][i] = Math.round(capped); 671 } 672 673 // convert to all the other color spaces 674 for (var sname in spaces) { 675 if (sname !== space) { 676 values[sname] = convert[space][sname](values[space]); 677 } 678 } 679 680 return true; 681}; 682 683Color.prototype.setSpace = function (space, args) { 684 var vals = args[0]; 685 686 if (vals === undefined) { 687 // color.rgb() 688 return this.getValues(space); 689 } 690 691 // color.rgb(10, 10, 10) 692 if (typeof vals === 'number') { 693 vals = Array.prototype.slice.call(args); 694 } 695 696 this.setValues(space, vals); 697 return this; 698}; 699 700Color.prototype.setChannel = function (space, index, val) { 701 var svalues = this.values[space]; 702 if (val === undefined) { 703 // color.red() 704 return svalues[index]; 705 } else if (val === svalues[index]) { 706 // color.red(color.red()) 707 return this; 708 } 709 710 // color.red(100) 711 svalues[index] = val; 712 this.setValues(space, svalues); 713 714 return this; 715}; 716 717if (typeof window !== 'undefined') { 718 window.Color = Color; 719} 720 721module.exports = Color; 722 723},{"1":1,"4":4}],3:[function(require,module,exports){ 724/* MIT license */ 725 726module.exports = { 727 rgb2hsl: rgb2hsl, 728 rgb2hsv: rgb2hsv, 729 rgb2hwb: rgb2hwb, 730 rgb2cmyk: rgb2cmyk, 731 rgb2keyword: rgb2keyword, 732 rgb2xyz: rgb2xyz, 733 rgb2lab: rgb2lab, 734 rgb2lch: rgb2lch, 735 736 hsl2rgb: hsl2rgb, 737 hsl2hsv: hsl2hsv, 738 hsl2hwb: hsl2hwb, 739 hsl2cmyk: hsl2cmyk, 740 hsl2keyword: hsl2keyword, 741 742 hsv2rgb: hsv2rgb, 743 hsv2hsl: hsv2hsl, 744 hsv2hwb: hsv2hwb, 745 hsv2cmyk: hsv2cmyk, 746 hsv2keyword: hsv2keyword, 747 748 hwb2rgb: hwb2rgb, 749 hwb2hsl: hwb2hsl, 750 hwb2hsv: hwb2hsv, 751 hwb2cmyk: hwb2cmyk, 752 hwb2keyword: hwb2keyword, 753 754 cmyk2rgb: cmyk2rgb, 755 cmyk2hsl: cmyk2hsl, 756 cmyk2hsv: cmyk2hsv, 757 cmyk2hwb: cmyk2hwb, 758 cmyk2keyword: cmyk2keyword, 759 760 keyword2rgb: keyword2rgb, 761 keyword2hsl: keyword2hsl, 762 keyword2hsv: keyword2hsv, 763 keyword2hwb: keyword2hwb, 764 keyword2cmyk: keyword2cmyk, 765 keyword2lab: keyword2lab, 766 keyword2xyz: keyword2xyz, 767 768 xyz2rgb: xyz2rgb, 769 xyz2lab: xyz2lab, 770 xyz2lch: xyz2lch, 771 772 lab2xyz: lab2xyz, 773 lab2rgb: lab2rgb, 774 lab2lch: lab2lch, 775 776 lch2lab: lch2lab, 777 lch2xyz: lch2xyz, 778 lch2rgb: lch2rgb 779} 780 781 782function rgb2hsl(rgb) { 783 var r = rgb[0]/255, 784 g = rgb[1]/255, 785 b = rgb[2]/255, 786 min = Math.min(r, g, b), 787 max = Math.max(r, g, b), 788 delta = max - min, 789 h, s, l; 790 791 if (max == min) 792 h = 0; 793 else if (r == max) 794 h = (g - b) / delta; 795 else if (g == max) 796 h = 2 + (b - r) / delta; 797 else if (b == max) 798 h = 4 + (r - g)/ delta; 799 800 h = Math.min(h * 60, 360); 801 802 if (h < 0) 803 h += 360; 804 805 l = (min + max) / 2; 806 807 if (max == min) 808 s = 0; 809 else if (l <= 0.5) 810 s = delta / (max + min); 811 else 812 s = delta / (2 - max - min); 813 814 return [h, s * 100, l * 100]; 815} 816 817function rgb2hsv(rgb) { 818 var r = rgb[0], 819 g = rgb[1], 820 b = rgb[2], 821 min = Math.min(r, g, b), 822 max = Math.max(r, g, b), 823 delta = max - min, 824 h, s, v; 825 826 if (max == 0) 827 s = 0; 828 else 829 s = (delta/max * 1000)/10; 830 831 if (max == min) 832 h = 0; 833 else if (r == max) 834 h = (g - b) / delta; 835 else if (g == max) 836 h = 2 + (b - r) / delta; 837 else if (b == max) 838 h = 4 + (r - g) / delta; 839 840 h = Math.min(h * 60, 360); 841 842 if (h < 0) 843 h += 360; 844 845 v = ((max / 255) * 1000) / 10; 846 847 return [h, s, v]; 848} 849 850function rgb2hwb(rgb) { 851 var r = rgb[0], 852 g = rgb[1], 853 b = rgb[2], 854 h = rgb2hsl(rgb)[0], 855 w = 1/255 * Math.min(r, Math.min(g, b)), 856 b = 1 - 1/255 * Math.max(r, Math.max(g, b)); 857 858 return [h, w * 100, b * 100]; 859} 860 861function rgb2cmyk(rgb) {
862 var r = rgb[0] / 255, 863 g = rgb[1] / 255, 864 b = rgb[2] / 255, 865 c, m, y, k; 866 867 k = Math.min(1 - r, 1 - g, 1 - b); 868 c = (1 - r - k) / (1 - k) || 0; 869 m = (1 - g - k) / (1 - k) || 0; 870 y = (1 - b - k) / (1 - k) || 0; 871 return [c * 100, m * 100, y * 100, k * 100]; 872} 873 874function rgb2keyword(rgb) { 875 return reverseKeywords[JSON.stringify(rgb)]; 876} 877 878function rgb2xyz(rgb) { 879 var r = rgb[0] / 255, 880 g = rgb[1] / 255, 881 b = rgb[2] / 255; 882 883 // assume sRGB 884 r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); 885 g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); 886 b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); 887 888 var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); 889 var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); 890 var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); 891 892 return [x * 100, y *100, z * 100]; 893} 894 895function rgb2lab(rgb) { 896 var xyz = rgb2xyz(rgb), 897 x = xyz[0], 898 y = xyz[1], 899 z = xyz[2], 900 l, a, b; 901 902 x /= 95.047; 903 y /= 100; 904 z /= 108.883; 905 906 x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); 907 y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); 908 z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); 909 910 l = (116 * y) - 16; 911 a = 500 * (x - y); 912 b = 200 * (y - z); 913 914 return [l, a, b]; 915} 916 917function rgb2lch(args) { 918 return lab2lch(rgb2lab(args)); 919} 920 921function hsl2rgb(hsl) { 922 var h = hsl[0] / 360, 923 s = hsl[1] / 100, 924 l = hsl[2] / 100, 925 t1, t2, t3, rgb, val; 926 927 if (s == 0) { 928 val = l * 255; 929 return [val, val, val]; 930 } 931 932 if (l < 0.5) 933 t2 = l * (1 + s); 934 else 935 t2 = l + s - l * s; 936 t1 = 2 * l - t2; 937 938 rgb = [0, 0, 0]; 939 for (var i = 0; i < 3; i++) { 940 t3 = h + 1 / 3 * - (i - 1); 941 t3 < 0 && t3++; 942 t3 > 1 && t3--; 943 944 if (6 * t3 < 1) 945 val = t1 + (t2 - t1) * 6 * t3; 946 else if (2 * t3 < 1) 947 val = t2; 948 else if (3 * t3 < 2) 949 val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; 950 else 951 val = t1; 952 953 rgb[i] = val * 255; 954 } 955 956 return rgb; 957} 958 959function hsl2hsv(hsl) { 960 var h = hsl[0], 961 s = hsl[1] / 100, 962 l = hsl[2] / 100, 963 sv, v; 964 965 if(l === 0) { 966 // no need to do calc on black 967 // also avoids divide by 0 error 968 return [0, 0, 0]; 969 } 970 971 l *= 2; 972 s *= (l <= 1) ? l : 2 - l; 973 v = (l + s) / 2; 974 sv = (2 * s) / (l + s); 975 return [h, sv * 100, v * 100]; 976} 977 978function hsl2hwb(args) { 979 return rgb2hwb(hsl2rgb(args)); 980} 981 982function hsl2cmyk(args) { 983 return rgb2cmyk(hsl2rgb(args)); 984} 985 986function hsl2keyword(args) { 987 return rgb2keyword(hsl2rgb(args)); 988} 989 990 991function hsv2rgb(hsv) { 992 var h = hsv[0] / 60, 993 s = hsv[1] / 100, 994 v = hsv[2] / 100, 995 hi = Math.floor(h) % 6; 996 997 var f = h - Math.floor(h), 998 p = 255 * v * (1 - s), 999 q = 255 * v * (1 - (s * f)), 1000 t = 255 * v * (1 - (s * (1 - f))), 1001 v = 255 * v; 1002 1003 switch(hi) { 1004 case 0: 1005 return [v, t, p]; 1006 case 1: 1007 return [q, v, p]; 1008 case 2: 1009 return [p, v, t]; 1010 case 3: 1011 return [p, q, v]; 1012 case 4: 1013 return [t, p, v]; 1014 case 5: 1015 return [v, p, q]; 1016 } 1017} 1018 1019function hsv2hsl(hsv) { 1020 var h = hsv[0], 1021 s = hsv[1] / 100, 1022 v = hsv[2] / 100, 1023 sl, l; 1024 1025 l = (2 - s) * v; 1026 sl = s * v; 1027 sl /= (l <= 1) ? l : 2 - l; 1028 sl = sl || 0; 1029 l /= 2; 1030 return [h, sl * 100, l * 100]; 1031} 1032 1033function hsv2hwb(args) { 1034 return rgb2hwb(hsv2rgb(args)) 1035} 1036 1037function hsv2cmyk(args) { 1038 return rgb2cmyk(hsv2rgb(args)); 1039} 1040 1041function hsv2keyword(args) { 1042 return rgb2keyword(hsv2rgb(args)); 1043} 1044 1045// http://dev.w3.org/csswg/css-color/#hwb-to-rgb 1046function hwb2rgb(hwb) { 1047 var h = hwb[0] / 360, 1048 wh = hwb[1] / 100, 1049 bl = hwb[2] / 100, 1050 ratio = wh + bl, 1051 i, v, f, n; 1052 1053 // wh + bl cant be > 1 1054 if (ratio > 1) { 1055 wh /= ratio; 1056 bl /= ratio; 1057 } 1058 1059 i = Math.floor(6 * h); 1060 v = 1 - bl; 1061 f = 6 * h - i; 1062 if ((i & 0x01) != 0) { 1063 f = 1 - f; 1064 } 1065 n = wh + f * (v - wh); // linear interpolation 1066 1067 switch (i) { 1068 default: 1069 case 6: 1070 case 0: r = v; g = n; b = wh; break; 1071 case 1: r = n; g = v; b = wh; break; 1072 case 2: r = wh; g = v; b = n; break; 1073 case 3: r = wh; g = n; b = v; break; 1074 case 4: r = n; g = wh; b = v; break; 1075 case 5: r = v; g = wh; b = n; break; 1076 } 1077 1078 return [r * 255, g * 255, b * 255]; 1079} 1080 1081function hwb2hsl(args) { 1082 return rgb2hsl(hwb2rgb(args)); 1083} 1084 1085function hwb2hsv(args) { 1086 return rgb2hsv(hwb2rgb(args)); 1087} 1088 1089function hwb2cmyk(args) { 1090 return rgb2cmyk(hwb2rgb(args)); 1091} 1092 1093function hwb2keyword(args) { 1094 return rgb2keyword(hwb2rgb(args)); 1095} 1096 1097function cmyk2rgb(cmyk) { 1098 var c = cmyk[0] / 100, 1099 m = cmyk[1] / 100, 1100 y = cmyk[2] / 100, 1101 k = cmyk[3] / 100, 1102 r, g, b; 1103 1104 r = 1 - Math.min(1, c * (1 - k) + k); 1105 g = 1 - Math.min(1, m * (1 - k) + k); 1106 b = 1 - Math.min(1, y * (1 - k) + k); 1107 return [r * 255, g * 255, b * 255]; 1108} 1109 1110function cmyk2hsl(args) { 1111 return rgb2hsl(cmyk2rgb(args)); 1112} 1113 1114function cmyk2hsv(args) { 1115 return rgb2hsv(cmyk2rgb(args)); 1116} 1117 1118function cmyk2hwb(args) { 1119 return rgb2hwb(cmyk2rgb(args)); 1120} 1121 1122function cmyk2keyword(args) { 1123 return rgb2keyword(cmyk2rgb(args)); 1124} 1125 1126 1127function xyz2rgb(xyz) { 1128 var x = xyz[0] / 100, 1129 y = xyz[1] / 100, 1130 z = xyz[2] / 100, 1131 r, g, b; 1132 1133 r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); 1134 g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); 1135 b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); 1136 1137 // assume sRGB 1138 r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) 1139 : r = (r * 12.92); 1140 1141 g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) 1142 : g = (g * 12.92); 1143 1144 b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) 1145 : b = (b * 12.92); 1146 1147 r = Math.min(Math.max(0, r), 1); 1148 g = Math.min(Math.max(0, g), 1); 1149 b = Math.min(Math.max(0, b), 1); 1150 1151 return [r * 255, g * 255, b * 255]; 1152} 1153 1154function xyz2lab(xyz) { 1155 var x = xyz[0], 1156 y = xyz[1], 1157 z = xyz[2], 1158 l, a, b; 1159 1160 x /= 95.047; 1161 y /= 100; 1162 z /= 108.883; 1163 1164 x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); 1165 y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); 1166 z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); 1167 1168 l = (116 * y) - 16; 1169 a = 500 * (x - y); 1170 b = 200 * (y - z); 1171 1172 return [l, a, b]; 1173} 1174 1175function xyz2lch(args) { 1176 return lab2lch(xyz2lab(args)); 1177} 1178 1179function lab2xyz(lab) { 1180 var l = lab[0], 1181 a = lab[1], 1182 b = lab[2], 1183 x, y, z, y2; 1184 1185 if (l <= 8) { 1186 y = (l * 100) / 903.3; 1187 y2 = (7.787 * (y / 100)) + (16 / 116); 1188 } else { 1189 y = 100 * Math.pow((l + 16) / 116, 3); 1190 y2 = Math.pow(y / 100, 1/3); 1191 } 1192 1193 x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); 1194 1195 z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); 1196 1197 return [x, y, z]; 1198} 1199 1200function lab2lch(lab) { 1201 var l = lab[0], 1202 a = lab[1], 1203 b = lab[2], 1204 hr, h, c; 1205 1206 hr = Math.atan2(b, a);
1207 h = hr * 360 / 2 / Math.PI; 1208 if (h < 0) { 1209 h += 360; 1210 } 1211 c = Math.sqrt(a * a + b * b); 1212 return [l, c, h]; 1213} 1214 1215function lab2rgb(args) { 1216 return xyz2rgb(lab2xyz(args)); 1217} 1218 1219function lch2lab(lch) { 1220 var l = lch[0], 1221 c = lch[1], 1222 h = lch[2], 1223 a, b, hr; 1224 1225 hr = h / 360 * 2 * Math.PI; 1226 a = c * Math.cos(hr); 1227 b = c * Math.sin(hr); 1228 return [l, a, b]; 1229} 1230 1231function lch2xyz(args) { 1232 return lab2xyz(lch2lab(args)); 1233} 1234 1235function lch2rgb(args) { 1236 return lab2rgb(lch2lab(args)); 1237} 1238 1239function keyword2rgb(keyword) { 1240 return cssKeywords[keyword]; 1241} 1242 1243function keyword2hsl(args) { 1244 return rgb2hsl(keyword2rgb(args)); 1245} 1246 1247function keyword2hsv(args) { 1248 return rgb2hsv(keyword2rgb(args)); 1249} 1250 1251function keyword2hwb(args) { 1252 return rgb2hwb(keyword2rgb(args)); 1253} 1254 1255function keyword2cmyk(args) { 1256 return rgb2cmyk(keyword2rgb(args)); 1257} 1258 1259function keyword2lab(args) { 1260 return rgb2lab(keyword2rgb(args)); 1261} 1262 1263function keyword2xyz(args) { 1264 return rgb2xyz(keyword2rgb(args)); 1265} 1266 1267var cssKeywords = { 1268 aliceblue: [240,248,255], 1269 antiquewhite: [250,235,215], 1270 aqua: [0,255,255], 1271 aquamarine: [127,255,212], 1272 azure: [240,255,255], 1273 beige: [245,245,220], 1274 bisque: [255,228,196], 1275 black: [0,0,0], 1276 blanchedalmond: [255,235,205], 1277 blue: [0,0,255], 1278 blueviolet: [138,43,226], 1279 brown: [165,42,42], 1280 burlywood: [222,184,135], 1281 cadetblue: [95,158,160], 1282 chartreuse: [127,255,0], 1283 chocolate: [210,105,30], 1284 coral: [255,127,80], 1285 cornflowerblue: [100,149,237], 1286 cornsilk: [255,248,220], 1287 crimson: [220,20,60], 1288 cyan: [0,255,255], 1289 darkblue: [0,0,139], 1290 darkcyan: [0,139,139], 1291 darkgoldenrod: [184,134,11], 1292 darkgray: [169,169,169], 1293 darkgreen: [0,100,0], 1294 darkgrey: [169,169,169], 1295 darkkhaki: [189,183,107], 1296 darkmagenta: [139,0,139], 1297 darkolivegreen: [85,107,47], 1298 darkorange: [255,140,0], 1299 darkorchid: [153,50,204], 1300 darkred: [139,0,0], 1301 darksalmon: [233,150,122], 1302 darkseagreen: [143,188,143], 1303 darkslateblue: [72,61,139], 1304 darkslategray: [47,79,79], 1305 darkslategrey: [47,79,79], 1306 darkturquoise: [0,206,209], 1307 darkviolet: [148,0,211], 1308 deeppink: [255,20,147], 1309 deepskyblue: [0,191,255], 1310 dimgray: [105,105,105], 1311 dimgrey: [105,105,105], 1312 dodgerblue: [30,144,255], 1313 firebrick: [178,34,34], 1314 floralwhite: [255,250,240], 1315 forestgreen: [34,139,34], 1316 fuchsia: [255,0,255], 1317 gainsboro: [220,220,220], 1318 ghostwhite: [248,248,255], 1319 gold: [255,215,0], 1320 goldenrod: [218,165,32], 1321 gray: [128,128,128], 1322 green: [0,128,0], 1323 greenyellow: [173,255,47], 1324 grey: [128,128,128], 1325 honeydew: [240,255,240], 1326 hotpink: [255,105,180], 1327 indianred: [205,92,92], 1328 indigo: [75,0,130], 1329 ivory: [255,255,240], 1330 khaki: [240,230,140], 1331 lavender: [230,230,250], 1332 lavenderblush: [255,240,245], 1333 lawngreen: [124,252,0], 1334 lemonchiffon: [255,250,205], 1335 lightblue: [173,216,230], 1336 lightcoral: [240,128,128], 1337 lightcyan: [224,255,255], 1338 lightgoldenrodyellow: [250,250,210], 1339 lightgray: [211,211,211], 1340 lightgreen: [144,238,144], 1341 lightgrey: [211,211,211], 1342 lightpink: [255,182,193], 1343 lightsalmon: [255,160,122], 1344 lightseagreen: [32,178,170], 1345 lightskyblue: [135,206,250], 1346 lightslategray: [119,136,153], 1347 lightslategrey: [119,136,153], 1348 lightsteelblue: [176,196,222], 1349 lightyellow: [255,255,224], 1350 lime: [0,255,0], 1351 limegreen: [50,205,50], 1352 linen: [250,240,230], 1353 magenta: [255,0,255], 1354 maroon: [128,0,0], 1355 mediumaquamarine: [102,205,170], 1356 mediumblue: [0,0,205], 1357 mediumorchid: [186,85,211], 1358 mediumpurple: [147,112,219], 1359 mediumseagreen: [60,179,113], 1360 mediumslateblue: [123,104,238], 1361 mediumspringgreen: [0,250,154], 1362 mediumturquoise: [72,209,204], 1363 mediumvioletred: [199,21,133], 1364 midnightblue: [25,25,112], 1365 mintcream: [245,255,250], 1366 mistyrose: [255,228,225], 1367 moccasin: [255,228,181], 1368 navajowhite: [255,222,173], 1369 navy: [0,0,128], 1370 oldlace: [253,245,230], 1371 olive: [128,128,0], 1372 olivedrab: [107,142,35], 1373 orange: [255,165,0], 1374 orangered: [255,69,0], 1375 orchid: [218,112,214], 1376 palegoldenrod: [238,232,170], 1377 palegreen: [152,251,152], 1378 paleturquoise: [175,238,238], 1379 palevioletred: [219,112,147], 1380 papayawhip: [255,239,213], 1381 peachpuff: [255,218,185], 1382 peru: [205,133,63], 1383 pink: [255,192,203], 1384 plum: [221,160,221], 1385 powderblue: [176,224,230], 1386 purple: [128,0,128], 1387 rebeccapurple: [102, 51, 153], 1388 red: [255,0,0], 1389 rosybrown: [188,143,143], 1390 royalblue: [65,105,225], 1391 saddlebrown: [139,69,19], 1392 salmon: [250,128,114], 1393 sandybrown: [244,164,96],
1394 seagreen: [46,139,87], 1395 seashell: [255,245,238], 1396 sienna: [160,82,45], 1397 silver: [192,192,192], 1398 skyblue: [135,206,235], 1399 slateblue: [106,90,205], 1400 slategray: [112,128,144], 1401 slategrey: [112,128,144], 1402 snow: [255,250,250], 1403 springgreen: [0,255,127], 1404 steelblue: [70,130,180], 1405 tan: [210,180,140], 1406 teal: [0,128,128], 1407 thistle: [216,191,216], 1408 tomato: [255,99,71], 1409 turquoise: [64,224,208], 1410 violet: [238,130,238], 1411 wheat: [245,222,179], 1412 white: [255,255,255], 1413 whitesmoke: [245,245,245], 1414 yellow: [255,255,0], 1415 yellowgreen: [154,205,50] 1416}; 1417 1418var reverseKeywords = {}; 1419for (var key in cssKeywords) { 1420 reverseKeywords[JSON.stringify(cssKeywords[key])] = key; 1421} 1422 1423},{}],4:[function(require,module,exports){ 1424var conversions = require(3); 1425 1426var convert = function() { 1427 return new Converter(); 1428} 1429 1430for (var func in conversions) { 1431 // export Raw versions 1432 convert[func + "Raw"] = (function(func) { 1433 // accept array or plain args 1434 return function(arg) { 1435 if (typeof arg == "number") 1436 arg = Array.prototype.slice.call(arguments); 1437 return conversions[func](arg); 1438 } 1439 })(func); 1440 1441 var pair = /(\w+)2(\w+)/.exec(func), 1442 from = pair[1], 1443 to = pair[2]; 1444 1445 // export rgb2hsl and ["rgb"]["hsl"] 1446 convert[from] = convert[from] || {}; 1447 1448 convert[from][to] = convert[func] = (function(func) { 1449 return function(arg) { 1450 if (typeof arg == "number") 1451 arg = Array.prototype.slice.call(arguments); 1452 1453 var val = conversions[func](arg); 1454 if (typeof val == "string" || val === undefined) 1455 return val; // keyword 1456 1457 for (var i = 0; i < val.length; i++) 1458 val[i] = Math.round(val[i]); 1459 return val; 1460 } 1461 })(func); 1462} 1463 1464 1465/* Converter does lazy conversion and caching */ 1466var Converter = function() { 1467 this.convs = {}; 1468}; 1469 1470/* Either get the values for a space or 1471 set the values for a space, depending on args */ 1472Converter.prototype.routeSpace = function(space, args) { 1473 var values = args[0]; 1474 if (values === undefined) { 1475 // color.rgb() 1476 return this.getValues(space); 1477 } 1478 // color.rgb(10, 10, 10) 1479 if (typeof values == "number") { 1480 values = Array.prototype.slice.call(args); 1481 } 1482 1483 return this.setValues(space, values); 1484}; 1485 1486/* Set the values for a space, invalidating cache */ 1487Converter.prototype.setValues = function(space, values) { 1488 this.space = space; 1489 this.convs = {}; 1490 this.convs[space] = values; 1491 return this; 1492}; 1493 1494/* Get the values for a space. If there's already 1495 a conversion for the space, fetch it, otherwise 1496 compute it */ 1497Converter.prototype.getValues = function(space) { 1498 var vals = this.convs[space]; 1499 if (!vals) { 1500 var fspace = this.space, 1501 from = this.convs[fspace]; 1502 vals = convert[fspace][space](from); 1503 1504 this.convs[space] = vals; 1505 } 1506 return vals; 1507}; 1508 1509["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { 1510 Converter.prototype[space] = function(vals) { 1511 return this.routeSpace(space, arguments); 1512 } 1513}); 1514 1515module.exports = convert; 1516},{"3":3}],5:[function(require,module,exports){ 1517'use strict' 1518 1519module.exports = { 1520 "aliceblue": [240, 248, 255], 1521 "antiquewhite": [250, 235, 215], 1522 "aqua": [0, 255, 255], 1523 "aquamarine": [127, 255, 212], 1524 "azure": [240, 255, 255], 1525 "beige": [245, 245, 220], 1526 "bisque": [255, 228, 196], 1527 "black": [0, 0, 0], 1528 "blanchedalmond": [255, 235, 205], 1529 "blue": [0, 0, 255], 1530 "blueviolet": [138, 43, 226], 1531 "brown": [165, 42, 42], 1532 "burlywood": [222, 184, 135], 1533 "cadetblue": [95, 158, 160], 1534 "chartreuse": [127, 255, 0], 1535 "chocolate": [210, 105, 30], 1536 "coral": [255, 127, 80], 1537 "cornflowerblue": [100, 149, 237], 1538 "cornsilk": [255, 248, 220], 1539 "crimson": [220, 20, 60], 1540 "cyan": [0, 255, 255], 1541 "darkblue": [0, 0, 139], 1542 "darkcyan": [0, 139, 139], 1543 "darkgoldenrod": [184, 134, 11], 1544 "darkgray": [169, 169, 169], 1545 "darkgreen": [0, 100, 0], 1546 "darkgrey": [169, 169, 169], 1547 "darkkhaki": [189, 183, 107], 1548 "darkmagenta": [139, 0, 139], 1549 "darkolivegreen": [85, 107, 47], 1550 "darkorange": [255, 140, 0], 1551 "darkorchid": [153, 50, 204], 1552 "darkred": [139, 0, 0], 1553 "darksalmon": [233, 150, 122], 1554 "darkseagreen": [143, 188, 143], 1555 "darkslateblue": [72, 61, 139], 1556 "darkslategray": [47, 79, 79], 1557 "darkslategrey": [47, 79, 79],
vendor: 5,044 bytes, lines 1558-1727
1558 "darkturquoise": [0, 206, 209], 1559 "darkviolet": [148, 0, 211], 1560 "deeppink": [255, 20, 147], 1561 "deepskyblue": [0, 191, 255], 1562 "dimgray": [105, 105, 105], 1563 "dimgrey": [105, 105, 105], 1564 "dodgerblue": [30, 144, 255], 1565 "firebrick": [178, 34, 34], 1566 "floralwhite": [255, 250, 240], 1567 "forestgreen": [34, 139, 34], 1568 "fuchsia": [255, 0, 255], 1569 "gainsboro": [220, 220, 220], 1570 "ghostwhite": [248, 248, 255], 1571 "gold": [255, 215, 0], 1572 "goldenrod": [218, 165, 32], 1573 "gray": [128, 128, 128], 1574 "green": [0, 128, 0], 1575 "greenyellow": [173, 255, 47], 1576 "grey": [128, 128, 128], 1577 "honeydew": [240, 255, 240], 1578 "hotpink": [255, 105, 180], 1579 "indianred": [205, 92, 92], 1580 "indigo": [75, 0, 130], 1581 "ivory": [255, 255, 240], 1582 "khaki": [240, 230, 140], 1583 "lavender": [230, 230, 250], 1584 "lavenderblush": [255, 240, 245], 1585 "lawngreen": [124, 252, 0], 1586 "lemonchiffon": [255, 250, 205], 1587 "lightblue": [173, 216, 230], 1588 "lightcoral": [240, 128, 128], 1589 "lightcyan": [224, 255, 255], 1590 "lightgoldenrodyellow": [250, 250, 210], 1591 "lightgray": [211, 211, 211], 1592 "lightgreen": [144, 238, 144], 1593 "lightgrey": [211, 211, 211], 1594 "lightpink": [255, 182, 193], 1595 "lightsalmon": [255, 160, 122], 1596 "lightseagreen": [32, 178, 170], 1597 "lightskyblue": [135, 206, 250], 1598 "lightslategray": [119, 136, 153], 1599 "lightslategrey": [119, 136, 153], 1600 "lightsteelblue": [176, 196, 222], 1601 "lightyellow": [255, 255, 224], 1602 "lime": [0, 255, 0], 1603 "limegreen": [50, 205, 50], 1604 "linen": [250, 240, 230], 1605 "magenta": [255, 0, 255], 1606 "maroon": [128, 0, 0], 1607 "mediumaquamarine": [102, 205, 170], 1608 "mediumblue": [0, 0, 205], 1609 "mediumorchid": [186, 85, 211], 1610 "mediumpurple": [147, 112, 219], 1611 "mediumseagreen": [60, 179, 113], 1612 "mediumslateblue": [123, 104, 238], 1613 "mediumspringgreen": [0, 250, 154], 1614 "mediumturquoise": [72, 209, 204], 1615 "mediumvioletred": [199, 21, 133], 1616 "midnightblue": [25, 25, 112], 1617 "mintcream": [245, 255, 250], 1618 "mistyrose": [255, 228, 225], 1619 "moccasin": [255, 228, 181], 1620 "navajowhite": [255, 222, 173], 1621 "navy": [0, 0, 128], 1622 "oldlace": [253, 245, 230], 1623 "olive": [128, 128, 0], 1624 "olivedrab": [107, 142, 35], 1625 "orange": [255, 165, 0], 1626 "orangered": [255, 69, 0], 1627 "orchid": [218, 112, 214], 1628 "palegoldenrod": [238, 232, 170], 1629 "palegreen": [152, 251, 152], 1630 "paleturquoise": [175, 238, 238], 1631 "palevioletred": [219, 112, 147], 1632 "papayawhip": [255, 239, 213], 1633 "peachpuff": [255, 218, 185], 1634 "peru": [205, 133, 63], 1635 "pink": [255, 192, 203], 1636 "plum": [221, 160, 221], 1637 "powderblue": [176, 224, 230], 1638 "purple": [128, 0, 128], 1639 "rebeccapurple": [102, 51, 153], 1640 "red": [255, 0, 0], 1641 "rosybrown": [188, 143, 143], 1642 "royalblue": [65, 105, 225], 1643 "saddlebrown": [139, 69, 19], 1644 "salmon": [250, 128, 114], 1645 "sandybrown": [244, 164, 96], 1646 "seagreen": [46, 139, 87], 1647 "seashell": [255, 245, 238], 1648 "sienna": [160, 82, 45], 1649 "silver": [192, 192, 192], 1650 "skyblue": [135, 206, 235], 1651 "slateblue": [106, 90, 205], 1652 "slategray": [112, 128, 144], 1653 "slategrey": [112, 128, 144], 1654 "snow": [255, 250, 250], 1655 "springgreen": [0, 255, 127], 1656 "steelblue": [70, 130, 180], 1657 "tan": [210, 180, 140], 1658 "teal": [0, 128, 128], 1659 "thistle": [216, 191, 216], 1660 "tomato": [255, 99, 71], 1661 "turquoise": [64, 224, 208], 1662 "violet": [238, 130, 238], 1663 "wheat": [245, 222, 179], 1664 "white": [255, 255, 255], 1665 "whitesmoke": [245, 245, 245], 1666 "yellow": [255, 255, 0], 1667 "yellowgreen": [154, 205, 50] 1668}; 1669 1670},{}],6:[function(require,module,exports){ 1671//! moment.js 1672//! version : 2.20.1 1673//! authors : Tim Wood, Iskren Chernev, Moment.js contributors 1674//! license : MIT 1675//! momentjs.com 1676 1677;(function (global, factory) { 1678 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 1679 typeof define === 'function' && define.amd ? define(factory) : 1680 global.moment = factory() 1681}(this, (function () { 'use strict'; 1682 1683var hookCallback; 1684 1685function hooks () { 1686 return hookCallback.apply(null, arguments); 1687} 1688 1689// This is done to register the method called with moment() 1690// without creating circular dependencies. 1691function setHookCallback (callback) { 1692 hookCallback = callback; 1693} 1694 1695function isArray(input) { 1696 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; 1697} 1698 1699function isObject(input) { 1700 // IE8 will treat undefined and null as object if it wasn't for 1701 // input != null 1702 return input != null && Object.prototype.toString.call(input) === '[object Object]'; 1703} 1704 1705function isObjectEmpty(obj) { 1706 if (Object.getOwnPropertyNames) { 1707 return (Object.getOwnPropertyNames(obj).length === 0); 1708 } else { 1709 var k; 1710 for (k in obj) { 1711 if (obj.hasOwnProperty(k)) { 1712 return false; 1713 } 1714 } 1715 return true; 1716 } 1717} 1718 1719function isUndefined(input) { 1720 return input === void 0; 1721} 1722 1723function isNumber(input) { 1724 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; 1725} 1726 1727function isDate(input) {
vendor: 22,993 bytes, lines 1728-2568
1728 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; 1729} 1730 1731function map(arr, fn) { 1732 var res = [], i; 1733 for (i = 0; i < arr.length; ++i) { 1734 res.push(fn(arr[i], i)); 1735 } 1736 return res; 1737} 1738 1739function hasOwnProp(a, b) { 1740 return Object.prototype.hasOwnProperty.call(a, b); 1741} 1742 1743function extend(a, b) { 1744 for (var i in b) { 1745 if (hasOwnProp(b, i)) { 1746 a[i] = b[i]; 1747 } 1748 } 1749 1750 if (hasOwnProp(b, 'toString')) { 1751 a.toString = b.toString; 1752 } 1753 1754 if (hasOwnProp(b, 'valueOf')) { 1755 a.valueOf = b.valueOf; 1756 } 1757 1758 return a; 1759} 1760 1761function createUTC (input, format, locale, strict) { 1762 return createLocalOrUTC(input, format, locale, strict, true).utc(); 1763} 1764 1765function defaultParsingFlags() { 1766 // We need to deep clone this object. 1767 return { 1768 empty : false, 1769 unusedTokens : [], 1770 unusedInput : [], 1771 overflow : -2, 1772 charsLeftOver : 0, 1773 nullInput : false, 1774 invalidMonth : null, 1775 invalidFormat : false, 1776 userInvalidated : false, 1777 iso : false, 1778 parsedDateParts : [], 1779 meridiem : null, 1780 rfc2822 : false, 1781 weekdayMismatch : false 1782 }; 1783} 1784 1785function getParsingFlags(m) { 1786 if (m._pf == null) { 1787 m._pf = defaultParsingFlags(); 1788 } 1789 return m._pf; 1790} 1791 1792var some; 1793if (Array.prototype.some) { 1794 some = Array.prototype.some; 1795} else { 1796 some = function (fun) { 1797 var t = Object(this); 1798 var len = t.length >>> 0; 1799 1800 for (var i = 0; i < len; i++) { 1801 if (i in t && fun.call(this, t[i], i, t)) { 1802 return true; 1803 } 1804 } 1805 1806 return false; 1807 }; 1808} 1809 1810function isValid(m) { 1811 if (m._isValid == null) { 1812 var flags = getParsingFlags(m); 1813 var parsedParts = some.call(flags.parsedDateParts, function (i) { 1814 return i != null; 1815 }); 1816 var isNowValid = !isNaN(m._d.getTime()) && 1817 flags.overflow < 0 && 1818 !flags.empty && 1819 !flags.invalidMonth && 1820 !flags.invalidWeekday && 1821 !flags.weekdayMismatch && 1822 !flags.nullInput && 1823 !flags.invalidFormat && 1824 !flags.userInvalidated && 1825 (!flags.meridiem || (flags.meridiem && parsedParts)); 1826 1827 if (m._strict) { 1828 isNowValid = isNowValid && 1829 flags.charsLeftOver === 0 && 1830 flags.unusedTokens.length === 0 && 1831 flags.bigHour === undefined; 1832 } 1833 1834 if (Object.isFrozen == null || !Object.isFrozen(m)) { 1835 m._isValid = isNowValid; 1836 } 1837 else { 1838 return isNowValid; 1839 } 1840 } 1841 return m._isValid; 1842} 1843 1844function createInvalid (flags) { 1845 var m = createUTC(NaN); 1846 if (flags != null) { 1847 extend(getParsingFlags(m), flags); 1848 } 1849 else { 1850 getParsingFlags(m).userInvalidated = true; 1851 } 1852 1853 return m; 1854} 1855 1856// Plugins that add properties should also add the key here (null value), 1857// so we can properly clone ourselves. 1858var momentProperties = hooks.momentProperties = []; 1859 1860function copyConfig(to, from) { 1861 var i, prop, val; 1862 1863 if (!isUndefined(from._isAMomentObject)) { 1864 to._isAMomentObject = from._isAMomentObject; 1865 } 1866 if (!isUndefined(from._i)) { 1867 to._i = from._i; 1868 } 1869 if (!isUndefined(from._f)) { 1870 to._f = from._f; 1871 } 1872 if (!isUndefined(from._l)) { 1873 to._l = from._l; 1874 } 1875 if (!isUndefined(from._strict)) { 1876 to._strict = from._strict; 1877 } 1878 if (!isUndefined(from._tzm)) { 1879 to._tzm = from._tzm; 1880 } 1881 if (!isUndefined(from._isUTC)) { 1882 to._isUTC = from._isUTC; 1883 } 1884 if (!isUndefined(from._offset)) { 1885 to._offset = from._offset; 1886 } 1887 if (!isUndefined(from._pf)) { 1888 to._pf = getParsingFlags(from); 1889 } 1890 if (!isUndefined(from._locale)) { 1891 to._locale = from._locale; 1892 } 1893 1894 if (momentProperties.length > 0) { 1895 for (i = 0; i < momentProperties.length; i++) { 1896 prop = momentProperties[i]; 1897 val = from[prop]; 1898 if (!isUndefined(val)) { 1899 to[prop] = val; 1900 } 1901 } 1902 } 1903 1904 return to; 1905} 1906 1907var updateInProgress = false; 1908 1909// Moment prototype object 1910function Moment(config) { 1911 copyConfig(this, config); 1912 this._d = new Date(config._d != null ? config._d.getTime() : NaN); 1913 if (!this.isValid()) { 1914 this._d = new Date(NaN); 1915 } 1916 // Prevent infinite loop in case updateOffset creates new moment 1917 // objects. 1918 if (updateInProgress === false) { 1919 updateInProgress = true; 1920 hooks.updateOffset(this); 1921 updateInProgress = false; 1922 } 1923} 1924 1925function isMoment (obj) { 1926 return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); 1927} 1928 1929function absFloor (number) { 1930 if (number < 0) { 1931 // -0 -> 0 1932 return Math.ceil(number) || 0; 1933 } else { 1934 return Math.floor(number); 1935 } 1936} 1937 1938function toInt(argumentForCoercion) { 1939 var coercedNumber = +argumentForCoercion, 1940 value = 0; 1941 1942 if (coercedNumber !== 0 && isFinite(coercedNumber)) { 1943 value = absFloor(coercedNumber); 1944 } 1945 1946 return value; 1947} 1948 1949// compare two arrays, return the number of differences 1950function compareArrays(array1, array2, dontConvert) { 1951 var len = Math.min(array1.length, array2.length), 1952 lengthDiff = Math.abs(array1.length - array2.length), 1953 diffs = 0, 1954 i; 1955 for (i = 0; i < len; i++) { 1956 if ((dontConvert && array1[i] !== array2[i]) || 1957 (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { 1958 diffs++; 1959 } 1960 } 1961 return diffs + lengthDiff; 1962} 1963 1964function warn(msg) { 1965 if (hooks.suppressDeprecationWarnings === false && 1966 (typeof console !== 'undefined') && console.warn) { 1967 console.warn('Deprecation warning: ' + msg); 1968 } 1969} 1970 1971function deprecate(msg, fn) { 1972 var firstTime = true; 1973 1974 return extend(function () { 1975 if (hooks.deprecationHandler != null) { 1976 hooks.deprecationHandler(null, msg); 1977 } 1978 if (firstTime) { 1979 var args = []; 1980 var arg; 1981 for (var i = 0; i < arguments.length; i++) { 1982 arg = ''; 1983 if (typeof arguments[i] === 'object') { 1984 arg += '\n[' + i + '] '; 1985 for (var key in arguments[0]) { 1986 arg += key + ': ' + arguments[0][key] + ', '; 1987 } 1988 arg = arg.slice(0, -2); // Remove trailing comma and space 1989 } else { 1990 arg = arguments[i]; 1991 } 1992 args.push(arg); 1993 } 1994 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); 1995 firstTime = false; 1996 } 1997 return fn.apply(this, arguments); 1998 }, fn); 1999} 2000 2001var deprecations = {}; 2002 2003function deprecateSimple(name, msg) { 2004 if (hooks.deprecationHandler != null) { 2005 hooks.deprecationHandler(name, msg); 2006 } 2007 if (!deprecations[name]) { 2008 warn(msg); 2009 deprecations[name] = true; 2010 } 2011} 2012 2013hooks.suppressDeprecationWarnings = false; 2014hooks.deprecationHandler = null; 2015 2016function isFunction(input) { 2017 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; 2018} 2019 2020function set (config) { 2021 var prop, i; 2022 for (i in config) { 2023 prop = config[i]; 2024 if (isFunction(prop)) { 2025 this[i] = prop; 2026 } else { 2027 this['_' + i] = prop; 2028 } 2029 } 2030 this._config = config; 2031 // Lenient ordinal parsing accepts just a number in addition to 2032 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. 2033 // TODO: Remove "ordinalParse" fallback in next major release. 2034 this._dayOfMonthOrdinalParseLenient = new RegExp( 2035 (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + 2036 '|' + (/\d{1,2}/).source); 2037} 2038 2039function mergeConfigs(parentConfig, childConfig) { 2040 var res = extend({}, parentConfig), prop; 2041 for (prop in childConfig) { 2042 if (hasOwnProp(childConfig, prop)) { 2043 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { 2044 res[prop] = {}; 2045 extend(res[prop], parentConfig[prop]); 2046 extend(res[prop], childConfig[prop]); 2047 } else if (childConfig[prop] != null) { 2048 res[prop] = childConfig[prop]; 2049 } else { 2050 delete res[prop]; 2051 } 2052 } 2053 } 2054 for (prop in parentConfig) { 2055 if (hasOwnProp(parentConfig, prop) && 2056 !hasOwnProp(childConfig, prop) && 2057 isObject(parentConfig[prop])) { 2058 // make sure changes to properties don't modify parent config 2059 res[prop] = extend({}, res[prop]); 2060 } 2061 } 2062 return res; 2063} 2064 2065function Locale(config) { 2066 if (config != null) { 2067 this.set(config); 2068 } 2069} 2070 2071var keys; 2072 2073if (Object.keys) { 2074 keys = Object.keys; 2075} else { 2076 keys = function (obj) { 2077 var i, res = []; 2078 for (i in obj) { 2079 if (hasOwnProp(obj, i)) { 2080 res.push(i); 2081 } 2082 } 2083 return res; 2084 }; 2085} 2086 2087var defaultCalendar = { 2088 sameDay : '[Today at] LT', 2089 nextDay : '[Tomorrow at] LT', 2090 nextWeek : 'dddd [at] LT', 2091 lastDay : '[Yesterday at] LT', 2092 lastWeek : '[Last] dddd [at] LT', 2093 sameElse : 'L' 2094}; 2095 2096function calendar (key, mom, now) { 2097 var output = this._calendar[key] || this._calendar['sameElse']; 2098 return isFunction(output) ? output.call(mom, now) : output; 2099} 2100 2101var defaultLongDateFormat = { 2102 LTS : 'h:mm:ss A', 2103 LT : 'h:mm A', 2104 L : 'MM/DD/YYYY', 2105 LL : 'MMMM D, YYYY', 2106 LLL : 'MMMM D, YYYY h:mm A', 2107 LLLL : 'dddd, MMMM D, YYYY h:mm A' 2108}; 2109 2110function longDateFormat (key) { 2111 var format = this._longDateFormat[key], 2112 formatUpper = this._longDateFormat[key.toUpperCase()]; 2113 2114 if (format || !formatUpper) { 2115 return format; 2116 } 2117 2118 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { 2119 return val.slice(1); 2120 }); 2121 2122 return this._longDateFormat[key]; 2123} 2124 2125var defaultInvalidDate = 'Invalid date'; 2126 2127function invalidDate () { 2128 return this._invalidDate; 2129} 2130 2131var defaultOrdinal = '%d'; 2132var defaultDayOfMonthOrdinalParse = /\d{1,2}/; 2133 2134function ordinal (number) { 2135 return this._ordinal.replace('%d', number); 2136} 2137 2138var defaultRelativeTime = { 2139 future : 'in %s', 2140 past : '%s ago', 2141 s : 'a few seconds', 2142 ss : '%d seconds', 2143 m : 'a minute', 2144 mm : '%d minutes', 2145 h : 'an hour', 2146 hh : '%d hours', 2147 d : 'a day', 2148 dd : '%d days', 2149 M : 'a month', 2150 MM : '%d months', 2151 y : 'a year', 2152 yy : '%d years' 2153}; 2154 2155function relativeTime (number, withoutSuffix, string, isFuture) { 2156 var output = this._relativeTime[string]; 2157 return (isFunction(output)) ? 2158 output(number, withoutSuffix, string, isFuture) : 2159 output.replace(/%d/i, number); 2160} 2161 2162function pastFuture (diff, output) { 2163 var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 2164 return isFunction(format) ? format(output) : format.replace(/%s/i, output); 2165} 2166 2167var aliases = {}; 2168 2169function addUnitAlias (unit, shorthand) { 2170 var lowerCase = unit.toLowerCase(); 2171 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; 2172} 2173 2174function normalizeUnits(units) { 2175 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; 2176} 2177 2178function normalizeObjectUnits(inputObject) { 2179 var normalizedInput = {}, 2180 normalizedProp, 2181 prop; 2182 2183 for (prop in inputObject) { 2184 if (hasOwnProp(inputObject, prop)) { 2185 normalizedProp = normalizeUnits(prop); 2186 if (normalizedProp) { 2187 normalizedInput[normalizedProp] = inputObject[prop]; 2188 } 2189 } 2190 } 2191 2192 return normalizedInput; 2193} 2194 2195var priorities = {}; 2196 2197function addUnitPriority(unit, priority) { 2198 priorities[unit] = priority; 2199} 2200 2201function getPrioritizedUnits(unitsObj) { 2202 var units = []; 2203 for (var u in unitsObj) { 2204 units.push({unit: u, priority: priorities[u]}); 2205 } 2206 units.sort(function (a, b) { 2207 return a.priority - b.priority; 2208 }); 2209 return units; 2210} 2211 2212function zeroFill(number, targetLength, forceSign) { 2213 var absNumber = '' + Math.abs(number), 2214 zerosToFill = targetLength - absNumber.length, 2215 sign = number >= 0; 2216 return (sign ? (forceSign ? '+' : '') : '-') + 2217 Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; 2218} 2219 2220var 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; 2221 2222var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; 2223 2224var formatFunctions = {}; 2225 2226var formatTokenFunctions = {}; 2227 2228// token: 'M' 2229// padded: ['MM', 2] 2230// ordinal: 'Mo' 2231// callback: function () { this.month() + 1 } 2232function addFormatToken (token, padded, ordinal, callback) { 2233 var func = callback; 2234 if (typeof callback === 'string') { 2235 func = function () { 2236 return this[callback](); 2237 }; 2238 } 2239 if (token) { 2240 formatTokenFunctions[token] = func; 2241 } 2242 if (padded) { 2243 formatTokenFunctions[padded[0]] = function () { 2244 return zeroFill(func.apply(this, arguments), padded[1], padded[2]); 2245 }; 2246 } 2247 if (ordinal) { 2248 formatTokenFunctions[ordinal] = function () { 2249 return this.localeData().ordinal(func.apply(this, arguments), token); 2250 }; 2251 } 2252} 2253 2254function removeFormattingTokens(input) { 2255 if (input.match(/\[[\s\S]/)) { 2256 return input.replace(/^\[|\]$/g, ''); 2257 } 2258 return input.replace(/\\/g, ''); 2259} 2260 2261function makeFormatFunction(format) { 2262 var array = format.match(formattingTokens), i, length; 2263 2264 for (i = 0, length = array.length; i < length; i++) { 2265 if (formatTokenFunctions[array[i]]) { 2266 array[i] = formatTokenFunctions[array[i]]; 2267 } else { 2268 array[i] = removeFormattingTokens(array[i]); 2269 } 2270 } 2271 2272 return function (mom) { 2273 var output = '', i; 2274 for (i = 0; i < length; i++) { 2275 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; 2276 } 2277 return output; 2278 }; 2279} 2280 2281// format date using native date object 2282function formatMoment(m, format) { 2283 if (!m.isValid()) { 2284 return m.localeData().invalidDate(); 2285 } 2286 2287 format = expandFormat(format, m.localeData()); 2288 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); 2289 2290 return formatFunctions[format](m); 2291} 2292 2293function expandFormat(format, locale) { 2294 var i = 5; 2295 2296 function replaceLongDateFormatTokens(input) { 2297 return locale.longDateFormat(input) || input; 2298 } 2299 2300 localFormattingTokens.lastIndex = 0; 2301 while (i >= 0 && localFormattingTokens.test(format)) { 2302 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 2303 localFormattingTokens.lastIndex = 0; 2304 i -= 1; 2305 } 2306 2307 return format; 2308} 2309 2310var match1 = /\d/; // 0 - 9 2311var match2 = /\d\d/; // 00 - 99 2312var match3 = /\d{3}/; // 000 - 999 2313var match4 = /\d{4}/; // 0000 - 9999 2314var match6 = /[+-]?\d{6}/; // -999999 - 999999 2315var match1to2 = /\d\d?/; // 0 - 99 2316var match3to4 = /\d\d\d\d?/; // 999 - 9999 2317var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 2318var match1to3 = /\d{1,3}/; // 0 - 999 2319var match1to4 = /\d{1,4}/; // 0 - 9999 2320var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 2321 2322var matchUnsigned = /\d+/; // 0 - inf 2323var matchSigned = /[+-]?\d+/; // -inf - inf 2324 2325var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z 2326var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z 2327 2328var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 2329 2330// any word (or two) characters or numbers including two/three word month in arabic. 2331// includes scottish gaelic two word and hyphenated months 2332var 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; 2333 2334 2335var regexes = {}; 2336 2337function addRegexToken (token, regex, strictRegex) { 2338 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { 2339 return (isStrict && strictRegex) ? strictRegex : regex; 2340 }; 2341} 2342 2343function getParseRegexForToken (token, config) { 2344 if (!hasOwnProp(regexes, token)) { 2345 return new RegExp(unescapeFormat(token)); 2346 } 2347 2348 return regexes[token](config._strict, config._locale); 2349} 2350 2351// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript 2352function unescapeFormat(s) { 2353 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { 2354 return p1 || p2 || p3 || p4; 2355 })); 2356} 2357 2358function regexEscape(s) { 2359 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 2360} 2361 2362var tokens = {}; 2363 2364function addParseToken (token, callback) { 2365 var i, func = callback; 2366 if (typeof token === 'string') { 2367 token = [token]; 2368 } 2369 if (isNumber(callback)) { 2370 func = function (input, array) { 2371 array[callback] = toInt(input); 2372 }; 2373 } 2374 for (i = 0; i < token.length; i++) { 2375 tokens[token[i]] = func; 2376 } 2377} 2378 2379function addWeekParseToken (token, callback) { 2380 addParseToken(token, function (input, array, config, token) { 2381 config._w = config._w || {}; 2382 callback(input, config._w, config, token); 2383 }); 2384} 2385 2386function addTimeToArrayFromToken(token, input, config) { 2387 if (input != null && hasOwnProp(tokens, token)) { 2388 tokens[token](input, config._a, config, token); 2389 } 2390} 2391 2392var YEAR = 0; 2393var MONTH = 1; 2394var DATE = 2; 2395var HOUR = 3; 2396var MINUTE = 4; 2397var SECOND = 5; 2398var MILLISECOND = 6; 2399var WEEK = 7; 2400var WEEKDAY = 8; 2401 2402// FORMATTING 2403 2404addFormatToken('Y', 0, 0, function () { 2405 var y = this.year(); 2406 return y <= 9999 ? '' + y : '+' + y; 2407}); 2408 2409addFormatToken(0, ['YY', 2], 0, function () { 2410 return this.year() % 100; 2411}); 2412 2413addFormatToken(0, ['YYYY', 4], 0, 'year'); 2414addFormatToken(0, ['YYYYY', 5], 0, 'year'); 2415addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); 2416 2417// ALIASES 2418 2419addUnitAlias('year', 'y'); 2420 2421// PRIORITIES 2422 2423addUnitPriority('year', 1); 2424 2425// PARSING 2426 2427addRegexToken('Y', matchSigned); 2428addRegexToken('YY', match1to2, match2); 2429addRegexToken('YYYY', match1to4, match4); 2430addRegexToken('YYYYY', match1to6, match6); 2431addRegexToken('YYYYYY', match1to6, match6); 2432 2433addParseToken(['YYYYY', 'YYYYYY'], YEAR); 2434addParseToken('YYYY', function (input, array) { 2435 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); 2436}); 2437addParseToken('YY', function (input, array) { 2438 array[YEAR] = hooks.parseTwoDigitYear(input); 2439}); 2440addParseToken('Y', function (input, array) { 2441 array[YEAR] = parseInt(input, 10); 2442}); 2443 2444// HELPERS 2445 2446function daysInYear(year) { 2447 return isLeapYear(year) ? 366 : 365; 2448} 2449 2450function isLeapYear(year) { 2451 return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 2452} 2453 2454// HOOKS 2455 2456hooks.parseTwoDigitYear = function (input) { 2457 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); 2458}; 2459 2460// MOMENTS 2461 2462var getSetYear = makeGetSet('FullYear', true); 2463 2464function getIsLeapYear () { 2465 return isLeapYear(this.year()); 2466} 2467 2468function makeGetSet (unit, keepTime) { 2469 return function (value) { 2470 if (value != null) { 2471 set$1(this, unit, value); 2472 hooks.updateOffset(this, keepTime); 2473 return this; 2474 } else { 2475 return get(this, unit); 2476 } 2477 }; 2478} 2479 2480function get (mom, unit) { 2481 return mom.isValid() ? 2482 mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; 2483} 2484 2485function set$1 (mom, unit, value) { 2486 if (mom.isValid() && !isNaN(value)) { 2487 if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { 2488 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); 2489 } 2490 else { 2491 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); 2492 } 2493 } 2494} 2495 2496// MOMENTS 2497 2498function stringGet (units) { 2499 units = normalizeUnits(units); 2500 if (isFunction(this[units])) { 2501 return this[units](); 2502 } 2503 return this; 2504} 2505 2506 2507function stringSet (units, value) { 2508 if (typeof units === 'object') { 2509 units = normalizeObjectUnits(units); 2510 var prioritized = getPrioritizedUnits(units); 2511 for (var i = 0; i < prioritized.length; i++) { 2512 this[prioritized[i].unit](units[prioritized[i].unit]); 2513 } 2514 } else { 2515 units = normalizeUnits(units); 2516 if (isFunction(this[units])) { 2517 return this[units](value); 2518 } 2519 } 2520 return this; 2521} 2522 2523function mod(n, x) { 2524 return ((n % x) + x) % x; 2525} 2526 2527var indexOf; 2528 2529if (Array.prototype.indexOf) { 2530 indexOf = Array.prototype.indexOf; 2531} else { 2532 indexOf = function (o) { 2533 // I know 2534 var i; 2535 for (i = 0; i < this.length; ++i) { 2536 if (this[i] === o) { 2537 return i; 2538 } 2539 } 2540 return -1; 2541 }; 2542} 2543 2544function daysInMonth(year, month) { 2545 if (isNaN(year) || isNaN(month)) { 2546 return NaN; 2547 } 2548 var modMonth = mod(month, 12); 2549 year += (month - modMonth) / 12; 2550 return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); 2551} 2552 2553// FORMATTING 2554 2555addFormatToken('M', ['MM', 2], 'Mo', function () { 2556 return this.month() + 1; 2557}); 2558 2559addFormatToken('MMM', 0, 0, function (format) { 2560 return this.localeData().monthsShort(this, format); 2561}); 2562 2563addFormatToken('MMMM', 0, 0, function (format) { 2564 return this.localeData().months(this, format); 2565}); 2566 2567// ALIASES 2568
vendor: 15,495 bytes, lines 2569-3076
2569addUnitAlias('month', 'M'); 2570 2571// PRIORITY 2572 2573addUnitPriority('month', 8); 2574 2575// PARSING 2576 2577addRegexToken('M', match1to2); 2578addRegexToken('MM', match1to2, match2); 2579addRegexToken('MMM', function (isStrict, locale) { 2580 return locale.monthsShortRegex(isStrict); 2581}); 2582addRegexToken('MMMM', function (isStrict, locale) { 2583 return locale.monthsRegex(isStrict); 2584}); 2585 2586addParseToken(['M', 'MM'], function (input, array) { 2587 array[MONTH] = toInt(input) - 1; 2588}); 2589 2590addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { 2591 var month = config._locale.monthsParse(input, token, config._strict); 2592 // if we didn't find a month name, mark the date as invalid. 2593 if (month != null) { 2594 array[MONTH] = month; 2595 } else { 2596 getParsingFlags(config).invalidMonth = input; 2597 } 2598}); 2599 2600// LOCALES 2601 2602var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; 2603var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); 2604function localeMonths (m, format) { 2605 if (!m) { 2606 return isArray(this._months) ? this._months : 2607 this._months['standalone']; 2608 } 2609 return isArray(this._months) ? this._months[m.month()] : 2610 this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; 2611} 2612 2613var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); 2614function localeMonthsShort (m, format) { 2615 if (!m) { 2616 return isArray(this._monthsShort) ? this._monthsShort : 2617 this._monthsShort['standalone']; 2618 } 2619 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : 2620 this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; 2621} 2622 2623function handleStrictParse(monthName, format, strict) { 2624 var i, ii, mom, llc = monthName.toLocaleLowerCase(); 2625 if (!this._monthsParse) { 2626 // this is not used 2627 this._monthsParse = []; 2628 this._longMonthsParse = []; 2629 this._shortMonthsParse = []; 2630 for (i = 0; i < 12; ++i) { 2631 mom = createUTC([2000, i]); 2632 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); 2633 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); 2634 } 2635 } 2636 2637 if (strict) { 2638 if (format === 'MMM') { 2639 ii = indexOf.call(this._shortMonthsParse, llc); 2640 return ii !== -1 ? ii : null; 2641 } else { 2642 ii = indexOf.call(this._longMonthsParse, llc); 2643 return ii !== -1 ? ii : null; 2644 } 2645 } else { 2646 if (format === 'MMM') { 2647 ii = indexOf.call(this._shortMonthsParse, llc); 2648 if (ii !== -1) { 2649 return ii; 2650 } 2651 ii = indexOf.call(this._longMonthsParse, llc); 2652 return ii !== -1 ? ii : null; 2653 } else { 2654 ii = indexOf.call(this._longMonthsParse, llc); 2655 if (ii !== -1) { 2656 return ii; 2657 } 2658 ii = indexOf.call(this._shortMonthsParse, llc); 2659 return ii !== -1 ? ii : null; 2660 } 2661 } 2662} 2663 2664function localeMonthsParse (monthName, format, strict) { 2665 var i, mom, regex; 2666 2667 if (this._monthsParseExact) { 2668 return handleStrictParse.call(this, monthName, format, strict); 2669 } 2670 2671 if (!this._monthsParse) { 2672 this._monthsParse = []; 2673 this._longMonthsParse = []; 2674 this._shortMonthsParse = []; 2675 } 2676 2677 // TODO: add sorting 2678 // Sorting makes sure if one month (or abbr) is a prefix of another 2679 // see sorting in computeMonthsParse 2680 for (i = 0; i < 12; i++) { 2681 // make the regex if we don't have it already 2682 mom = createUTC([2000, i]); 2683 if (strict && !this._longMonthsParse[i]) { 2684 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); 2685 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); 2686 } 2687 if (!strict && !this._monthsParse[i]) { 2688 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 2689 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 2690 } 2691 // test the regex 2692 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { 2693 return i; 2694 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { 2695 return i; 2696 } else if (!strict && this._monthsParse[i].test(monthName)) { 2697 return i; 2698 } 2699 } 2700} 2701 2702// MOMENTS 2703 2704function setMonth (mom, value) { 2705 var dayOfMonth; 2706 2707 if (!mom.isValid()) { 2708 // No op 2709 return mom; 2710 } 2711 2712 if (typeof value === 'string') { 2713 if (/^\d+$/.test(value)) { 2714 value = toInt(value); 2715 } else { 2716 value = mom.localeData().monthsParse(value); 2717 // TODO: Another silent failure? 2718 if (!isNumber(value)) { 2719 return mom; 2720 } 2721 } 2722 } 2723 2724 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); 2725 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); 2726 return mom; 2727} 2728 2729function getSetMonth (value) { 2730 if (value != null) { 2731 setMonth(this, value); 2732 hooks.updateOffset(this, true); 2733 return this; 2734 } else { 2735 return get(this, 'Month'); 2736 } 2737} 2738 2739function getDaysInMonth () { 2740 return daysInMonth(this.year(), this.month()); 2741} 2742 2743var defaultMonthsShortRegex = matchWord; 2744function monthsShortRegex (isStrict) { 2745 if (this._monthsParseExact) { 2746 if (!hasOwnProp(this, '_monthsRegex')) { 2747 computeMonthsParse.call(this); 2748 } 2749 if (isStrict) { 2750 return this._monthsShortStrictRegex; 2751 } else { 2752 return this._monthsShortRegex; 2753 } 2754 } else { 2755 if (!hasOwnProp(this, '_monthsShortRegex')) { 2756 this._monthsShortRegex = defaultMonthsShortRegex; 2757 } 2758 return this._monthsShortStrictRegex && isStrict ? 2759 this._monthsShortStrictRegex : this._monthsShortRegex; 2760 } 2761} 2762 2763var defaultMonthsRegex = matchWord; 2764function monthsRegex (isStrict) { 2765 if (this._monthsParseExact) { 2766 if (!hasOwnProp(this, '_monthsRegex')) { 2767 computeMonthsParse.call(this); 2768 } 2769 if (isStrict) { 2770 return this._monthsStrictRegex; 2771 } else { 2772 return this._monthsRegex; 2773 } 2774 } else { 2775 if (!hasOwnProp(this, '_monthsRegex')) { 2776 this._monthsRegex = defaultMonthsRegex; 2777 } 2778 return this._monthsStrictRegex && isStrict ? 2779 this._monthsStrictRegex : this._monthsRegex; 2780 } 2781} 2782 2783function computeMonthsParse () { 2784 function cmpLenRev(a, b) { 2785 return b.length - a.length; 2786 } 2787 2788 var shortPieces = [], longPieces = [], mixedPieces = [], 2789 i, mom; 2790 for (i = 0; i < 12; i++) { 2791 // make the regex if we don't have it already 2792 mom = createUTC([2000, i]); 2793 shortPieces.push(this.monthsShort(mom, '')); 2794 longPieces.push(this.months(mom, '')); 2795 mixedPieces.push(this.months(mom, '')); 2796 mixedPieces.push(this.monthsShort(mom, '')); 2797 } 2798 // Sorting makes sure if one month (or abbr) is a prefix of another it 2799 // will match the longer piece. 2800 shortPieces.sort(cmpLenRev); 2801 longPieces.sort(cmpLenRev); 2802 mixedPieces.sort(cmpLenRev); 2803 for (i = 0; i < 12; i++) { 2804 shortPieces[i] = regexEscape(shortPieces[i]); 2805 longPieces[i] = regexEscape(longPieces[i]); 2806 } 2807 for (i = 0; i < 24; i++) { 2808 mixedPieces[i] = regexEscape(mixedPieces[i]); 2809 } 2810 2811 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 2812 this._monthsShortRegex = this._monthsRegex; 2813 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 2814 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 2815} 2816 2817function createDate (y, m, d, h, M, s, ms) { 2818 // can't just apply() to create a date: 2819 // https://stackoverflow.com/q/181348 2820 var date = new Date(y, m, d, h, M, s, ms); 2821 2822 // the date constructor remaps years 0-99 to 1900-1999 2823 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { 2824 date.setFullYear(y); 2825 } 2826 return date; 2827} 2828 2829function createUTCDate (y) { 2830 var date = new Date(Date.UTC.apply(null, arguments)); 2831 2832 // the Date.UTC function remaps years 0-99 to 1900-1999 2833 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { 2834 date.setUTCFullYear(y); 2835 } 2836 return date; 2837} 2838 2839// start-of-first-week - start-of-year 2840function firstWeekOffset(year, dow, doy) { 2841 var // first-week day -- which january is always in the first week (4 for iso, 1 for other) 2842 fwd = 7 + dow - doy, 2843 // first-week day local weekday -- which local weekday is fwd 2844 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; 2845 2846 return -fwdlw + fwd - 1; 2847} 2848 2849// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday 2850function dayOfYearFromWeeks(year, week, weekday, dow, doy) { 2851 var localWeekday = (7 + weekday - dow) % 7, 2852 weekOffset = firstWeekOffset(year, dow, doy), 2853 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, 2854 resYear, resDayOfYear; 2855 2856 if (dayOfYear <= 0) { 2857 resYear = year - 1; 2858 resDayOfYear = daysInYear(resYear) + dayOfYear; 2859 } else if (dayOfYear > daysInYear(year)) { 2860 resYear = year + 1; 2861 resDayOfYear = dayOfYear - daysInYear(year); 2862 } else { 2863 resYear = year; 2864 resDayOfYear = dayOfYear; 2865 } 2866 2867 return { 2868 year: resYear, 2869 dayOfYear: resDayOfYear 2870 }; 2871} 2872 2873function weekOfYear(mom, dow, doy) { 2874 var weekOffset = firstWeekOffset(mom.year(), dow, doy), 2875 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, 2876 resWeek, resYear; 2877 2878 if (week < 1) { 2879 resYear = mom.year() - 1; 2880 resWeek = week + weeksInYear(resYear, dow, doy); 2881 } else if (week > weeksInYear(mom.year(), dow, doy)) { 2882 resWeek = week - weeksInYear(mom.year(), dow, doy); 2883 resYear = mom.year() + 1; 2884 } else { 2885 resYear = mom.year(); 2886 resWeek = week; 2887 } 2888 2889 return { 2890 week: resWeek, 2891 year: resYear 2892 }; 2893} 2894 2895function weeksInYear(year, dow, doy) { 2896 var weekOffset = firstWeekOffset(year, dow, doy), 2897 weekOffsetNext = firstWeekOffset(year + 1, dow, doy); 2898 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; 2899} 2900 2901// FORMATTING 2902 2903addFormatToken('w', ['ww', 2], 'wo', 'week'); 2904addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); 2905 2906// ALIASES 2907 2908addUnitAlias('week', 'w'); 2909addUnitAlias('isoWeek', 'W'); 2910 2911// PRIORITIES 2912 2913addUnitPriority('week', 5); 2914addUnitPriority('isoWeek', 5); 2915 2916// PARSING 2917 2918addRegexToken('w', match1to2); 2919addRegexToken('ww', match1to2, match2); 2920addRegexToken('W', match1to2); 2921addRegexToken('WW', match1to2, match2); 2922 2923addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { 2924 week[token.substr(0, 1)] = toInt(input); 2925}); 2926 2927// HELPERS 2928 2929// LOCALES 2930 2931function localeWeek (mom) { 2932 return weekOfYear(mom, this._week.dow, this._week.doy).week; 2933} 2934 2935var defaultLocaleWeek = { 2936 dow : 0, // Sunday is the first day of the week. 2937 doy : 6 // The week that contains Jan 1st is the first week of the year. 2938}; 2939 2940function localeFirstDayOfWeek () { 2941 return this._week.dow; 2942} 2943 2944 2945function localeFirstDayOfYear () { 2946 return this._week.doy; 2947} 2948 2949// MOMENTS 2950 2951function getSetWeek (input) { 2952 var week = this.localeData().week(this); 2953 return input == null ? week : this.add((input - week) * 7, 'd'); 2954} 2955 2956function getSetISOWeek (input) { 2957 var week = weekOfYear(this, 1, 4).week; 2958 return input == null ? week : this.add((input - week) * 7, 'd'); 2959} 2960 2961// FORMATTING 2962 2963addFormatToken('d', 0, 'do', 'day'); 2964 2965addFormatToken('dd', 0, 0, function (format) { 2966 return this.localeData().weekdaysMin(this, format); 2967}); 2968 2969addFormatToken('ddd', 0, 0, function (format) { 2970 return this.localeData().weekdaysShort(this, format); 2971}); 2972 2973addFormatToken('dddd', 0, 0, function (format) { 2974 return this.localeData().weekdays(this, format); 2975}); 2976 2977addFormatToken('e', 0, 0, 'weekday'); 2978addFormatToken('E', 0, 0, 'isoWeekday'); 2979 2980// ALIASES 2981 2982addUnitAlias('day', 'd'); 2983addUnitAlias('weekday', 'e'); 2984addUnitAlias('isoWeekday', 'E'); 2985 2986// PRIORITY 2987addUnitPriority('day', 11); 2988addUnitPriority('weekday', 11); 2989addUnitPriority('isoWeekday', 11); 2990 2991// PARSING 2992 2993addRegexToken('d', match1to2); 2994addRegexToken('e', match1to2); 2995addRegexToken('E', match1to2); 2996addRegexToken('dd', function (isStrict, locale) { 2997 return locale.weekdaysMinRegex(isStrict); 2998}); 2999addRegexToken('ddd', function (isStrict, locale) { 3000 return locale.weekdaysShortRegex(isStrict); 3001}); 3002addRegexToken('dddd', function (isStrict, locale) { 3003 return locale.weekdaysRegex(isStrict); 3004}); 3005 3006addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { 3007 var weekday = config._locale.weekdaysParse(input, token, config._strict); 3008 // if we didn't get a weekday name, mark the date as invalid 3009 if (weekday != null) { 3010 week.d = weekday; 3011 } else { 3012 getParsingFlags(config).invalidWeekday = input; 3013 } 3014}); 3015 3016addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { 3017 week[token] = toInt(input); 3018}); 3019 3020// HELPERS 3021 3022function parseWeekday(input, locale) { 3023 if (typeof input !== 'string') { 3024 return input; 3025 } 3026 3027 if (!isNaN(input)) { 3028 return parseInt(input, 10); 3029 } 3030 3031 input = locale.weekdaysParse(input); 3032 if (typeof input === 'number') { 3033 return input; 3034 } 3035 3036 return null; 3037} 3038 3039function parseIsoWeekday(input, locale) { 3040 if (typeof input === 'string') { 3041 return locale.weekdaysParse(input) % 7 || 7; 3042 } 3043 return isNaN(input) ? null : input; 3044} 3045 3046// LOCALES 3047 3048var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); 3049function localeWeekdays (m, format) { 3050 if (!m) { 3051 return isArray(this._weekdays) ? this._weekdays : 3052 this._weekdays['standalone']; 3053 } 3054 return isArray(this._weekdays) ? this._weekdays[m.day()] : 3055 this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; 3056} 3057 3058var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); 3059function localeWeekdaysShort (m) { 3060 return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; 3061} 3062 3063var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); 3064function localeWeekdaysMin (m) { 3065 return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; 3066} 3067 3068function handleStrictParse$1(weekdayName, format, strict) { 3069 var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); 3070 if (!this._weekdaysParse) { 3071 this._weekdaysParse = []; 3072 this._shortWeekdaysParse = []; 3073 this._minWeekdaysParse = []; 3074 3075 for (i = 0; i < 7; ++i) { 3076 mom = createUTC([2000, 1]).day(i);
vendor: 48,501 bytes, lines 3077-4606
3077 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); 3078 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); 3079 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); 3080 } 3081 } 3082 3083 if (strict) { 3084 if (format === 'dddd') { 3085 ii = indexOf.call(this._weekdaysParse, llc); 3086 return ii !== -1 ? ii : null; 3087 } else if (format === 'ddd') { 3088 ii = indexOf.call(this._shortWeekdaysParse, llc); 3089 return ii !== -1 ? ii : null; 3090 } else { 3091 ii = indexOf.call(this._minWeekdaysParse, llc); 3092 return ii !== -1 ? ii : null; 3093 } 3094 } else { 3095 if (format === 'dddd') { 3096 ii = indexOf.call(this._weekdaysParse, llc); 3097 if (ii !== -1) { 3098 return ii; 3099 } 3100 ii = indexOf.call(this._shortWeekdaysParse, llc); 3101 if (ii !== -1) { 3102 return ii; 3103 } 3104 ii = indexOf.call(this._minWeekdaysParse, llc); 3105 return ii !== -1 ? ii : null; 3106 } else if (format === 'ddd') { 3107 ii = indexOf.call(this._shortWeekdaysParse, llc); 3108 if (ii !== -1) { 3109 return ii; 3110 } 3111 ii = indexOf.call(this._weekdaysParse, llc); 3112 if (ii !== -1) { 3113 return ii; 3114 } 3115 ii = indexOf.call(this._minWeekdaysParse, llc); 3116 return ii !== -1 ? ii : null; 3117 } else { 3118 ii = indexOf.call(this._minWeekdaysParse, llc); 3119 if (ii !== -1) { 3120 return ii; 3121 } 3122 ii = indexOf.call(this._weekdaysParse, llc); 3123 if (ii !== -1) { 3124 return ii; 3125 } 3126 ii = indexOf.call(this._shortWeekdaysParse, llc); 3127 return ii !== -1 ? ii : null; 3128 } 3129 } 3130} 3131 3132function localeWeekdaysParse (weekdayName, format, strict) { 3133 var i, mom, regex; 3134 3135 if (this._weekdaysParseExact) { 3136 return handleStrictParse$1.call(this, weekdayName, format, strict); 3137 } 3138 3139 if (!this._weekdaysParse) { 3140 this._weekdaysParse = []; 3141 this._minWeekdaysParse = []; 3142 this._shortWeekdaysParse = []; 3143 this._fullWeekdaysParse = []; 3144 } 3145 3146 for (i = 0; i < 7; i++) { 3147 // make the regex if we don't have it already 3148 3149 mom = createUTC([2000, 1]).day(i); 3150 if (strict && !this._fullWeekdaysParse[i]) { 3151 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); 3152 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); 3153 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); 3154 } 3155 if (!this._weekdaysParse[i]) { 3156 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); 3157 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); 3158 } 3159 // test the regex 3160 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { 3161 return i; 3162 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { 3163 return i; 3164 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { 3165 return i; 3166 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { 3167 return i; 3168 } 3169 } 3170} 3171 3172// MOMENTS 3173 3174function getSetDayOfWeek (input) { 3175 if (!this.isValid()) { 3176 return input != null ? this : NaN; 3177 } 3178 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 3179 if (input != null) { 3180 input = parseWeekday(input, this.localeData()); 3181 return this.add(input - day, 'd'); 3182 } else { 3183 return day; 3184 } 3185} 3186 3187function getSetLocaleDayOfWeek (input) { 3188 if (!this.isValid()) { 3189 return input != null ? this : NaN; 3190 } 3191 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; 3192 return input == null ? weekday : this.add(input - weekday, 'd'); 3193} 3194 3195function getSetISODayOfWeek (input) { 3196 if (!this.isValid()) { 3197 return input != null ? this : NaN; 3198 } 3199 3200 // behaves the same as moment#day except 3201 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) 3202 // as a setter, sunday should belong to the previous week. 3203 3204 if (input != null) { 3205 var weekday = parseIsoWeekday(input, this.localeData()); 3206 return this.day(this.day() % 7 ? weekday : weekday - 7); 3207 } else { 3208 return this.day() || 7; 3209 } 3210} 3211 3212var defaultWeekdaysRegex = matchWord; 3213function weekdaysRegex (isStrict) { 3214 if (this._weekdaysParseExact) { 3215 if (!hasOwnProp(this, '_weekdaysRegex')) { 3216 computeWeekdaysParse.call(this); 3217 } 3218 if (isStrict) { 3219 return this._weekdaysStrictRegex; 3220 } else { 3221 return this._weekdaysRegex; 3222 } 3223 } else { 3224 if (!hasOwnProp(this, '_weekdaysRegex')) { 3225 this._weekdaysRegex = defaultWeekdaysRegex; 3226 } 3227 return this._weekdaysStrictRegex && isStrict ? 3228 this._weekdaysStrictRegex : this._weekdaysRegex; 3229 } 3230} 3231 3232var defaultWeekdaysShortRegex = matchWord; 3233function weekdaysShortRegex (isStrict) { 3234 if (this._weekdaysParseExact) { 3235 if (!hasOwnProp(this, '_weekdaysRegex')) { 3236 computeWeekdaysParse.call(this); 3237 } 3238 if (isStrict) { 3239 return this._weekdaysShortStrictRegex; 3240 } else { 3241 return this._weekdaysShortRegex; 3242 } 3243 } else { 3244 if (!hasOwnProp(this, '_weekdaysShortRegex')) { 3245 this._weekdaysShortRegex = defaultWeekdaysShortRegex; 3246 } 3247 return this._weekdaysShortStrictRegex && isStrict ? 3248 this._weekdaysShortStrictRegex : this._weekdaysShortRegex; 3249 } 3250} 3251 3252var defaultWeekdaysMinRegex = matchWord; 3253function weekdaysMinRegex (isStrict) { 3254 if (this._weekdaysParseExact) { 3255 if (!hasOwnProp(this, '_weekdaysRegex')) { 3256 computeWeekdaysParse.call(this); 3257 } 3258 if (isStrict) { 3259 return this._weekdaysMinStrictRegex; 3260 } else { 3261 return this._weekdaysMinRegex; 3262 } 3263 } else { 3264 if (!hasOwnProp(this, '_weekdaysMinRegex')) { 3265 this._weekdaysMinRegex = defaultWeekdaysMinRegex; 3266 } 3267 return this._weekdaysMinStrictRegex && isStrict ? 3268 this._weekdaysMinStrictRegex : this._weekdaysMinRegex; 3269 } 3270} 3271 3272 3273function computeWeekdaysParse () { 3274 function cmpLenRev(a, b) { 3275 return b.length - a.length; 3276 } 3277 3278 var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], 3279 i, mom, minp, shortp, longp; 3280 for (i = 0; i < 7; i++) { 3281 // make the regex if we don't have it already 3282 mom = createUTC([2000, 1]).day(i); 3283 minp = this.weekdaysMin(mom, ''); 3284 shortp = this.weekdaysShort(mom, ''); 3285 longp = this.weekdays(mom, ''); 3286 minPieces.push(minp); 3287 shortPieces.push(shortp); 3288 longPieces.push(longp); 3289 mixedPieces.push(minp); 3290 mixedPieces.push(shortp); 3291 mixedPieces.push(longp); 3292 } 3293 // Sorting makes sure if one weekday (or abbr) is a prefix of another it 3294 // will match the longer piece. 3295 minPieces.sort(cmpLenRev); 3296 shortPieces.sort(cmpLenRev); 3297 longPieces.sort(cmpLenRev); 3298 mixedPieces.sort(cmpLenRev); 3299 for (i = 0; i < 7; i++) { 3300 shortPieces[i] = regexEscape(shortPieces[i]); 3301 longPieces[i] = regexEscape(longPieces[i]); 3302 mixedPieces[i] = regexEscape(mixedPieces[i]); 3303 } 3304 3305 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 3306 this._weekdaysShortRegex = this._weekdaysRegex; 3307 this._weekdaysMinRegex = this._weekdaysRegex; 3308 3309 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 3310 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 3311 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); 3312} 3313 3314// FORMATTING 3315 3316function hFormat() { 3317 return this.hours() % 12 || 12; 3318} 3319 3320function kFormat() { 3321 return this.hours() || 24; 3322} 3323 3324addFormatToken('H', ['HH', 2], 0, 'hour'); 3325addFormatToken('h', ['hh', 2], 0, hFormat); 3326addFormatToken('k', ['kk', 2], 0, kFormat); 3327 3328addFormatToken('hmm', 0, 0, function () { 3329 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); 3330}); 3331 3332addFormatToken('hmmss', 0, 0, function () { 3333 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + 3334 zeroFill(this.seconds(), 2); 3335}); 3336 3337addFormatToken('Hmm', 0, 0, function () { 3338 return '' + this.hours() + zeroFill(this.minutes(), 2); 3339}); 3340 3341addFormatToken('Hmmss', 0, 0, function () { 3342 return '' + this.hours() + zeroFill(this.minutes(), 2) + 3343 zeroFill(this.seconds(), 2); 3344}); 3345 3346function meridiem (token, lowercase) { 3347 addFormatToken(token, 0, 0, function () { 3348 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); 3349 }); 3350} 3351 3352meridiem('a', true); 3353meridiem('A', false); 3354 3355// ALIASES 3356 3357addUnitAlias('hour', 'h'); 3358 3359// PRIORITY 3360addUnitPriority('hour', 13); 3361 3362// PARSING 3363 3364function matchMeridiem (isStrict, locale) { 3365 return locale._meridiemParse; 3366} 3367 3368addRegexToken('a', matchMeridiem); 3369addRegexToken('A', matchMeridiem); 3370addRegexToken('H', match1to2); 3371addRegexToken('h', match1to2); 3372addRegexToken('k', match1to2); 3373addRegexToken('HH', match1to2, match2); 3374addRegexToken('hh', match1to2, match2); 3375addRegexToken('kk', match1to2, match2); 3376 3377addRegexToken('hmm', match3to4); 3378addRegexToken('hmmss', match5to6); 3379addRegexToken('Hmm', match3to4); 3380addRegexToken('Hmmss', match5to6); 3381 3382addParseToken(['H', 'HH'], HOUR); 3383addParseToken(['k', 'kk'], function (input, array, config) { 3384 var kInput = toInt(input); 3385 array[HOUR] = kInput === 24 ? 0 : kInput; 3386}); 3387addParseToken(['a', 'A'], function (input, array, config) { 3388 config._isPm = config._locale.isPM(input); 3389 config._meridiem = input; 3390}); 3391addParseToken(['h', 'hh'], function (input, array, config) { 3392 array[HOUR] = toInt(input); 3393 getParsingFlags(config).bigHour = true; 3394}); 3395addParseToken('hmm', function (input, array, config) { 3396 var pos = input.length - 2; 3397 array[HOUR] = toInt(input.substr(0, pos)); 3398 array[MINUTE] = toInt(input.substr(pos)); 3399 getParsingFlags(config).bigHour = true; 3400}); 3401addParseToken('hmmss', function (input, array, config) { 3402 var pos1 = input.length - 4; 3403 var pos2 = input.length - 2; 3404 array[HOUR] = toInt(input.substr(0, pos1)); 3405 array[MINUTE] = toInt(input.substr(pos1, 2)); 3406 array[SECOND] = toInt(input.substr(pos2)); 3407 getParsingFlags(config).bigHour = true; 3408}); 3409addParseToken('Hmm', function (input, array, config) { 3410 var pos = input.length - 2; 3411 array[HOUR] = toInt(input.substr(0, pos)); 3412 array[MINUTE] = toInt(input.substr(pos)); 3413}); 3414addParseToken('Hmmss', function (input, array, config) { 3415 var pos1 = input.length - 4; 3416 var pos2 = input.length - 2; 3417 array[HOUR] = toInt(input.substr(0, pos1)); 3418 array[MINUTE] = toInt(input.substr(pos1, 2)); 3419 array[SECOND] = toInt(input.substr(pos2)); 3420}); 3421 3422// LOCALES 3423 3424function localeIsPM (input) { 3425 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays 3426 // Using charAt should be more compatible. 3427 return ((input + '').toLowerCase().charAt(0) === 'p'); 3428} 3429 3430var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; 3431function localeMeridiem (hours, minutes, isLower) { 3432 if (hours > 11) { 3433 return isLower ? 'pm' : 'PM'; 3434 } else { 3435 return isLower ? 'am' : 'AM'; 3436 } 3437} 3438 3439 3440// MOMENTS 3441 3442// Setting the hour should keep the time, because the user explicitly 3443// specified which hour he wants. So trying to maintain the same hour (in 3444// a new timezone) makes sense. Adding/subtracting hours does not follow 3445// this rule. 3446var getSetHour = makeGetSet('Hours', true); 3447 3448// months 3449// week 3450// weekdays 3451// meridiem 3452var baseConfig = { 3453 calendar: defaultCalendar, 3454 longDateFormat: defaultLongDateFormat, 3455 invalidDate: defaultInvalidDate, 3456 ordinal: defaultOrdinal, 3457 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, 3458 relativeTime: defaultRelativeTime, 3459 3460 months: defaultLocaleMonths, 3461 monthsShort: defaultLocaleMonthsShort, 3462 3463 week: defaultLocaleWeek, 3464 3465 weekdays: defaultLocaleWeekdays, 3466 weekdaysMin: defaultLocaleWeekdaysMin, 3467 weekdaysShort: defaultLocaleWeekdaysShort, 3468 3469 meridiemParse: defaultLocaleMeridiemParse 3470}; 3471 3472// internal storage for locale config files 3473var locales = {}; 3474var localeFamilies = {}; 3475var globalLocale; 3476 3477function normalizeLocale(key) { 3478 return key ? key.toLowerCase().replace('_', '-') : key; 3479} 3480 3481// pick the locale from the array 3482// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each 3483// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root 3484function chooseLocale(names) { 3485 var i = 0, j, next, locale, split; 3486 3487 while (i < names.length) { 3488 split = normalizeLocale(names[i]).split('-'); 3489 j = split.length; 3490 next = normalizeLocale(names[i + 1]); 3491 next = next ? next.split('-') : null; 3492 while (j > 0) { 3493 locale = loadLocale(split.slice(0, j).join('-')); 3494 if (locale) { 3495 return locale; 3496 } 3497 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { 3498 //the next array item is better than a shallower substring of this one 3499 break; 3500 } 3501 j--; 3502 } 3503 i++; 3504 } 3505 return null; 3506} 3507 3508function loadLocale(name) { 3509 var oldLocale = null; 3510 // TODO: Find a better way to register and load all the locales in Node 3511 if (!locales[name] && (typeof module !== 'undefined') && 3512 module && module.exports) { 3513 try { 3514 oldLocale = globalLocale._abbr; 3515 var aliasedRequire = require; 3516 aliasedRequire('./locale/' + name); 3517 getSetGlobalLocale(oldLocale); 3518 } catch (e) {} 3519 } 3520 return locales[name]; 3521} 3522 3523// This function will load locale and then set the global locale. If 3524// no arguments are passed in, it will simply return the current global 3525// locale key. 3526function getSetGlobalLocale (key, values) { 3527 var data; 3528 if (key) { 3529 if (isUndefined(values)) { 3530 data = getLocale(key); 3531 } 3532 else { 3533 data = defineLocale(key, values); 3534 } 3535 3536 if (data) { 3537 // moment.duration._locale = moment._locale = data; 3538 globalLocale = data; 3539 } 3540 } 3541 3542 return globalLocale._abbr; 3543} 3544 3545function defineLocale (name, config) { 3546 if (config !== null) { 3547 var parentConfig = baseConfig; 3548 config.abbr = name; 3549 if (locales[name] != null) { 3550 deprecateSimple('defineLocaleOverride', 3551 'use moment.updateLocale(localeName, config) to change ' + 3552 'an existing locale. moment.defineLocale(localeName, ' + 3553 'config) should only be used for creating a new locale ' + 3554 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); 3555 parentConfig = locales[name]._config; 3556 } else if (config.parentLocale != null) { 3557 if (locales[config.parentLocale] != null) { 3558 parentConfig = locales[config.parentLocale]._config; 3559 } else { 3560 if (!localeFamilies[config.parentLocale]) { 3561 localeFamilies[config.parentLocale] = []; 3562 } 3563 localeFamilies[config.parentLocale].push({ 3564 name: name, 3565 config: config 3566 }); 3567 return null; 3568 } 3569 } 3570 locales[name] = new Locale(mergeConfigs(parentConfig, config)); 3571 3572 if (localeFamilies[name]) { 3573 localeFamilies[name].forEach(function (x) { 3574 defineLocale(x.name, x.config); 3575 }); 3576 } 3577 3578 // backwards compat for now: also set the locale 3579 // make sure we set the locale AFTER all child locales have been 3580 // created, so we won't end up with the child locale set. 3581 getSetGlobalLocale(name); 3582 3583 3584 return locales[name]; 3585 } else { 3586 // useful for testing 3587 delete locales[name]; 3588 return null; 3589 } 3590} 3591 3592function updateLocale(name, config) { 3593 if (config != null) { 3594 var locale, tmpLocale, parentConfig = baseConfig; 3595 // MERGE 3596 tmpLocale = loadLocale(name); 3597 if (tmpLocale != null) { 3598 parentConfig = tmpLocale._config; 3599 } 3600 config = mergeConfigs(parentConfig, config); 3601 locale = new Locale(config); 3602 locale.parentLocale = locales[name]; 3603 locales[name] = locale; 3604 3605 // backwards compat for now: also set the locale 3606 getSetGlobalLocale(name); 3607 } else { 3608 // pass null for config to unupdate, useful for tests 3609 if (locales[name] != null) { 3610 if (locales[name].parentLocale != null) { 3611 locales[name] = locales[name].parentLocale; 3612 } else if (locales[name] != null) { 3613 delete locales[name]; 3614 } 3615 } 3616 } 3617 return locales[name]; 3618} 3619 3620// returns locale data 3621function getLocale (key) { 3622 var locale; 3623 3624 if (key && key._locale && key._locale._abbr) { 3625 key = key._locale._abbr; 3626 } 3627 3628 if (!key) { 3629 return globalLocale; 3630 } 3631 3632 if (!isArray(key)) { 3633 //short-circuit everything else 3634 locale = loadLocale(key); 3635 if (locale) { 3636 return locale; 3637 } 3638 key = [key]; 3639 } 3640 3641 return chooseLocale(key); 3642} 3643 3644function listLocales() { 3645 return keys(locales); 3646} 3647 3648function checkOverflow (m) { 3649 var overflow; 3650 var a = m._a; 3651 3652 if (a && getParsingFlags(m).overflow === -2) { 3653 overflow = 3654 a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : 3655 a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : 3656 a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : 3657 a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : 3658 a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : 3659 a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : 3660 -1; 3661 3662 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { 3663 overflow = DATE; 3664 } 3665 if (getParsingFlags(m)._overflowWeeks && overflow === -1) { 3666 overflow = WEEK; 3667 } 3668 if (getParsingFlags(m)._overflowWeekday && overflow === -1) { 3669 overflow = WEEKDAY; 3670 } 3671 3672 getParsingFlags(m).overflow = overflow; 3673 } 3674 3675 return m; 3676} 3677 3678// Pick the first defined of two or three arguments. 3679function defaults(a, b, c) { 3680 if (a != null) { 3681 return a; 3682 } 3683 if (b != null) { 3684 return b; 3685 } 3686 return c; 3687} 3688 3689function currentDateArray(config) { 3690 // hooks is actually the exported moment object 3691 var nowValue = new Date(hooks.now()); 3692 if (config._useUTC) { 3693 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; 3694 } 3695 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; 3696} 3697 3698// convert an array to a date. 3699// the array should mirror the parameters below 3700// note: all values past the year are optional and will default to the lowest possible value. 3701// [year, month, day , hour, minute, second, millisecond] 3702function configFromArray (config) { 3703 var i, date, input = [], currentDate, expectedWeekday, yearToUse; 3704 3705 if (config._d) { 3706 return; 3707 } 3708 3709 currentDate = currentDateArray(config); 3710 3711 //compute day of the year from weeks and weekdays 3712 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { 3713 dayOfYearFromWeekInfo(config); 3714 } 3715 3716 //if the day of the year is set, figure out what it is 3717 if (config._dayOfYear != null) { 3718 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); 3719 3720 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { 3721 getParsingFlags(config)._overflowDayOfYear = true; 3722 } 3723 3724 date = createUTCDate(yearToUse, 0, config._dayOfYear); 3725 config._a[MONTH] = date.getUTCMonth(); 3726 config._a[DATE] = date.getUTCDate(); 3727 } 3728 3729 // Default to current date. 3730 // * if no year, month, day of month are given, default to today 3731 // * if day of month is given, default month and year 3732 // * if month is given, default only year 3733 // * if year is given, don't default anything 3734 for (i = 0; i < 3 && config._a[i] == null; ++i) { 3735 config._a[i] = input[i] = currentDate[i]; 3736 } 3737 3738 // Zero out whatever was not defaulted, including time 3739 for (; i < 7; i++) { 3740 config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 3741 } 3742 3743 // Check for 24:00:00.000 3744 if (config._a[HOUR] === 24 && 3745 config._a[MINUTE] === 0 && 3746 config._a[SECOND] === 0 && 3747 config._a[MILLISECOND] === 0) { 3748 config._nextDay = true; 3749 config._a[HOUR] = 0; 3750 } 3751 3752 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); 3753 expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); 3754 3755 // Apply timezone offset from input. The actual utcOffset can be changed 3756 // with parseZone. 3757 if (config._tzm != null) { 3758 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 3759 } 3760 3761 if (config._nextDay) { 3762 config._a[HOUR] = 24; 3763 } 3764 3765 // check for mismatching day of week 3766 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { 3767 getParsingFlags(config).weekdayMismatch = true; 3768 } 3769} 3770 3771function dayOfYearFromWeekInfo(config) { 3772 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; 3773 3774 w = config._w; 3775 if (w.GG != null || w.W != null || w.E != null) { 3776 dow = 1; 3777 doy = 4; 3778 3779 // TODO: We need to take the current isoWeekYear, but that depends on 3780 // how we interpret now (local, utc, fixed offset). So create 3781 // a now version of current config (take local/utc/offset flags, and 3782 // create now). 3783 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); 3784 week = defaults(w.W, 1); 3785 weekday = defaults(w.E, 1); 3786 if (weekday < 1 || weekday > 7) { 3787 weekdayOverflow = true; 3788 } 3789 } else { 3790 dow = config._locale._week.dow; 3791 doy = config._locale._week.doy; 3792 3793 var curWeek = weekOfYear(createLocal(), dow, doy); 3794 3795 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); 3796 3797 // Default to current week. 3798 week = defaults(w.w, curWeek.week); 3799 3800 if (w.d != null) { 3801 // weekday -- low day numbers are considered next week 3802 weekday = w.d; 3803 if (weekday < 0 || weekday > 6) { 3804 weekdayOverflow = true; 3805 } 3806 } else if (w.e != null) { 3807 // local weekday -- counting starts from begining of week 3808 weekday = w.e + dow; 3809 if (w.e < 0 || w.e > 6) { 3810 weekdayOverflow = true; 3811 } 3812 } else { 3813 // default to begining of week 3814 weekday = dow; 3815 } 3816 } 3817 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { 3818 getParsingFlags(config)._overflowWeeks = true; 3819 } else if (weekdayOverflow != null) { 3820 getParsingFlags(config)._overflowWeekday = true; 3821 } else { 3822 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); 3823 config._a[YEAR] = temp.year; 3824 config._dayOfYear = temp.dayOfYear; 3825 } 3826} 3827 3828// iso 8601 regex 3829// 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) 3830var 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)?)?$/; 3831var 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)?)?$/; 3832 3833var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; 3834 3835var isoDates = [ 3836 ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], 3837 ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], 3838 ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], 3839 ['GGGG-[W]WW', /\d{4}-W\d\d/, false], 3840 ['YYYY-DDD', /\d{4}-\d{3}/], 3841 ['YYYY-MM', /\d{4}-\d\d/, false], 3842 ['YYYYYYMMDD', /[+-]\d{10}/], 3843 ['YYYYMMDD', /\d{8}/], 3844 // YYYYMM is NOT allowed by the standard 3845 ['GGGG[W]WWE', /\d{4}W\d{3}/], 3846 ['GGGG[W]WW', /\d{4}W\d{2}/, false], 3847 ['YYYYDDD', /\d{7}/] 3848]; 3849 3850// iso time formats and regexes 3851var isoTimes = [ 3852 ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], 3853 ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], 3854 ['HH:mm:ss', /\d\d:\d\d:\d\d/], 3855 ['HH:mm', /\d\d:\d\d/], 3856 ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], 3857 ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], 3858 ['HHmmss', /\d\d\d\d\d\d/], 3859 ['HHmm', /\d\d\d\d/], 3860 ['HH', /\d\d/] 3861]; 3862 3863var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; 3864 3865// date from iso format 3866function configFromISO(config) { 3867 var i, l, 3868 string = config._i, 3869 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), 3870 allowTime, dateFormat, timeFormat, tzFormat; 3871 3872 if (match) { 3873 getParsingFlags(config).iso = true; 3874 3875 for (i = 0, l = isoDates.length; i < l; i++) { 3876 if (isoDates[i][1].exec(match[1])) { 3877 dateFormat = isoDates[i][0]; 3878 allowTime = isoDates[i][2] !== false; 3879 break; 3880 } 3881 } 3882 if (dateFormat == null) { 3883 config._isValid = false; 3884 return; 3885 } 3886 if (match[3]) { 3887 for (i = 0, l = isoTimes.length; i < l; i++) { 3888 if (isoTimes[i][1].exec(match[3])) { 3889 // match[2] should be 'T' or space 3890 timeFormat = (match[2] || ' ') + isoTimes[i][0]; 3891 break; 3892 } 3893 } 3894 if (timeFormat == null) { 3895 config._isValid = false; 3896 return; 3897 } 3898 } 3899 if (!allowTime && timeFormat != null) { 3900 config._isValid = false; 3901 return; 3902 } 3903 if (match[4]) { 3904 if (tzRegex.exec(match[4])) { 3905 tzFormat = 'Z'; 3906 } else { 3907 config._isValid = false; 3908 return; 3909 } 3910 } 3911 config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); 3912 configFromStringAndFormat(config); 3913 } else { 3914 config._isValid = false; 3915 } 3916} 3917 3918// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 3919var 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}))$/; 3920 3921function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { 3922 var result = [ 3923 untruncateYear(yearStr), 3924 defaultLocaleMonthsShort.indexOf(monthStr), 3925 parseInt(dayStr, 10), 3926 parseInt(hourStr, 10), 3927 parseInt(minuteStr, 10) 3928 ]; 3929 3930 if (secondStr) { 3931 result.push(parseInt(secondStr, 10)); 3932 } 3933 3934 return result; 3935} 3936 3937function untruncateYear(yearStr) { 3938 var year = parseInt(yearStr, 10); 3939 if (year <= 49) { 3940 return 2000 + year; 3941 } else if (year <= 999) { 3942 return 1900 + year; 3943 } 3944 return year; 3945} 3946 3947function preprocessRFC2822(s) { 3948 // Remove comments and folding whitespace and replace multiple-spaces with a single space 3949 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim(); 3950} 3951 3952function checkWeekday(weekdayStr, parsedInput, config) { 3953 if (weekdayStr) { 3954 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. 3955 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), 3956 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); 3957 if (weekdayProvided !== weekdayActual) { 3958 getParsingFlags(config).weekdayMismatch = true; 3959 config._isValid = false; 3960 return false; 3961 } 3962 } 3963 return true; 3964} 3965 3966var obsOffsets = { 3967 UT: 0, 3968 GMT: 0, 3969 EDT: -4 * 60, 3970 EST: -5 * 60, 3971 CDT: -5 * 60, 3972 CST: -6 * 60, 3973 MDT: -6 * 60, 3974 MST: -7 * 60, 3975 PDT: -7 * 60, 3976 PST: -8 * 60 3977}; 3978 3979function calculateOffset(obsOffset, militaryOffset, numOffset) { 3980 if (obsOffset) { 3981 return obsOffsets[obsOffset]; 3982 } else if (militaryOffset) { 3983 // the only allowed military tz is Z 3984 return 0; 3985 } else { 3986 var hm = parseInt(numOffset, 10); 3987 var m = hm % 100, h = (hm - m) / 100; 3988 return h * 60 + m; 3989 } 3990} 3991 3992// date and time from ref 2822 format 3993function configFromRFC2822(config) { 3994 var match = rfc2822.exec(preprocessRFC2822(config._i)); 3995 if (match) { 3996 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); 3997 if (!checkWeekday(match[1], parsedArray, config)) { 3998 return; 3999 } 4000 4001 config._a = parsedArray; 4002 config._tzm = calculateOffset(match[8], match[9], match[10]); 4003 4004 config._d = createUTCDate.apply(null, config._a); 4005 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 4006 4007 getParsingFlags(config).rfc2822 = true; 4008 } else { 4009 config._isValid = false; 4010 } 4011} 4012 4013// date from iso format or fallback 4014function configFromString(config) { 4015 var matched = aspNetJsonRegex.exec(config._i); 4016 4017 if (matched !== null) { 4018 config._d = new Date(+matched[1]); 4019 return; 4020 } 4021 4022 configFromISO(config); 4023 if (config._isValid === false) { 4024 delete config._isValid; 4025 } else { 4026 return; 4027 } 4028 4029 configFromRFC2822(config); 4030 if (config._isValid === false) { 4031 delete config._isValid; 4032 } else { 4033 return; 4034 } 4035 4036 // Final attempt, use Input Fallback 4037 hooks.createFromInputFallback(config); 4038} 4039 4040hooks.createFromInputFallback = deprecate( 4041 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 4042 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 4043 'discouraged and will be removed in an upcoming major release. Please refer to ' + 4044 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', 4045 function (config) { 4046 config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); 4047 } 4048); 4049 4050// constant that refers to the ISO standard 4051hooks.ISO_8601 = function () {}; 4052 4053// constant that refers to the RFC 2822 form 4054hooks.RFC_2822 = function () {}; 4055 4056// date from string and format string 4057function configFromStringAndFormat(config) { 4058 // TODO: Move this to another part of the creation flow to prevent circular deps 4059 if (config._f === hooks.ISO_8601) { 4060 configFromISO(config); 4061 return; 4062 } 4063 if (config._f === hooks.RFC_2822) { 4064 configFromRFC2822(config); 4065 return; 4066 } 4067 config._a = []; 4068 getParsingFlags(config).empty = true; 4069 4070 // This array is used to make a Date, either with `new Date` or `Date.UTC` 4071 var string = '' + config._i, 4072 i, parsedInput, tokens, token, skipped, 4073 stringLength = string.length, 4074 totalParsedInputLength = 0; 4075 4076 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; 4077 4078 for (i = 0; i < tokens.length; i++) { 4079 token = tokens[i]; 4080 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; 4081 // console.log('token', token, 'parsedInput', parsedInput, 4082 // 'regex', getParseRegexForToken(token, config)); 4083 if (parsedInput) { 4084 skipped = string.substr(0, string.indexOf(parsedInput)); 4085 if (skipped.length > 0) { 4086 getParsingFlags(config).unusedInput.push(skipped); 4087 } 4088 string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 4089 totalParsedInputLength += parsedInput.length; 4090 } 4091 // don't parse if it's not a known token 4092 if (formatTokenFunctions[token]) { 4093 if (parsedInput) { 4094 getParsingFlags(config).empty = false; 4095 } 4096 else { 4097 getParsingFlags(config).unusedTokens.push(token); 4098 } 4099 addTimeToArrayFromToken(token, parsedInput, config); 4100 } 4101 else if (config._strict && !parsedInput) { 4102 getParsingFlags(config).unusedTokens.push(token); 4103 } 4104 } 4105 4106 // add remaining unparsed input length to the string 4107 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; 4108 if (string.length > 0) { 4109 getParsingFlags(config).unusedInput.push(string); 4110 } 4111 4112 // clear _12h flag if hour is <= 12 4113 if (config._a[HOUR] <= 12 && 4114 getParsingFlags(config).bigHour === true && 4115 config._a[HOUR] > 0) { 4116 getParsingFlags(config).bigHour = undefined; 4117 } 4118 4119 getParsingFlags(config).parsedDateParts = config._a.slice(0); 4120 getParsingFlags(config).meridiem = config._meridiem; 4121 // handle meridiem 4122 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); 4123 4124 configFromArray(config); 4125 checkOverflow(config); 4126} 4127 4128 4129function meridiemFixWrap (locale, hour, meridiem) { 4130 var isPm; 4131 4132 if (meridiem == null) { 4133 // nothing to do 4134 return hour; 4135 } 4136 if (locale.meridiemHour != null) { 4137 return locale.meridiemHour(hour, meridiem); 4138 } else if (locale.isPM != null) { 4139 // Fallback 4140 isPm = locale.isPM(meridiem); 4141 if (isPm && hour < 12) { 4142 hour += 12; 4143 } 4144 if (!isPm && hour === 12) { 4145 hour = 0; 4146 } 4147 return hour; 4148 } else { 4149 // this is not supposed to happen 4150 return hour; 4151 } 4152} 4153 4154// date from string and array of format strings 4155function configFromStringAndArray(config) { 4156 var tempConfig, 4157 bestMoment, 4158 4159 scoreToBeat, 4160 i, 4161 currentScore; 4162 4163 if (config._f.length === 0) { 4164 getParsingFlags(config).invalidFormat = true; 4165 config._d = new Date(NaN); 4166 return; 4167 } 4168 4169 for (i = 0; i < config._f.length; i++) { 4170 currentScore = 0; 4171 tempConfig = copyConfig({}, config); 4172 if (config._useUTC != null) { 4173 tempConfig._useUTC = config._useUTC; 4174 } 4175 tempConfig._f = config._f[i]; 4176 configFromStringAndFormat(tempConfig); 4177 4178 if (!isValid(tempConfig)) { 4179 continue; 4180 } 4181 4182 // if there is any input that was not parsed add a penalty for that format 4183 currentScore += getParsingFlags(tempConfig).charsLeftOver; 4184 4185 //or tokens 4186 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; 4187 4188 getParsingFlags(tempConfig).score = currentScore; 4189 4190 if (scoreToBeat == null || currentScore < scoreToBeat) { 4191 scoreToBeat = currentScore; 4192 bestMoment = tempConfig; 4193 } 4194 } 4195 4196 extend(config, bestMoment || tempConfig); 4197} 4198 4199function configFromObject(config) { 4200 if (config._d) { 4201 return; 4202 } 4203 4204 var i = normalizeObjectUnits(config._i); 4205 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { 4206 return obj && parseInt(obj, 10); 4207 }); 4208 4209 configFromArray(config); 4210} 4211 4212function createFromConfig (config) { 4213 var res = new Moment(checkOverflow(prepareConfig(config))); 4214 if (res._nextDay) { 4215 // Adding is smart enough around DST 4216 res.add(1, 'd'); 4217 res._nextDay = undefined; 4218 } 4219 4220 return res; 4221} 4222 4223function prepareConfig (config) { 4224 var input = config._i, 4225 format = config._f; 4226 4227 config._locale = config._locale || getLocale(config._l); 4228 4229 if (input === null || (format === undefined && input === '')) { 4230 return createInvalid({nullInput: true}); 4231 } 4232 4233 if (typeof input === 'string') { 4234 config._i = input = config._locale.preparse(input); 4235 } 4236 4237 if (isMoment(input)) { 4238 return new Moment(checkOverflow(input)); 4239 } else if (isDate(input)) { 4240 config._d = input; 4241 } else if (isArray(format)) { 4242 configFromStringAndArray(config); 4243 } else if (format) { 4244 configFromStringAndFormat(config); 4245 } else { 4246 configFromInput(config); 4247 } 4248 4249 if (!isValid(config)) { 4250 config._d = null; 4251 } 4252 4253 return config; 4254} 4255 4256function configFromInput(config) { 4257 var input = config._i; 4258 if (isUndefined(input)) { 4259 config._d = new Date(hooks.now()); 4260 } else if (isDate(input)) { 4261 config._d = new Date(input.valueOf()); 4262 } else if (typeof input === 'string') { 4263 configFromString(config); 4264 } else if (isArray(input)) { 4265 config._a = map(input.slice(0), function (obj) { 4266 return parseInt(obj, 10); 4267 }); 4268 configFromArray(config); 4269 } else if (isObject(input)) { 4270 configFromObject(config); 4271 } else if (isNumber(input)) { 4272 // from milliseconds 4273 config._d = new Date(input); 4274 } else { 4275 hooks.createFromInputFallback(config); 4276 } 4277} 4278 4279function createLocalOrUTC (input, format, locale, strict, isUTC) { 4280 var c = {}; 4281 4282 if (locale === true || locale === false) { 4283 strict = locale; 4284 locale = undefined; 4285 } 4286 4287 if ((isObject(input) && isObjectEmpty(input)) || 4288 (isArray(input) && input.length === 0)) { 4289 input = undefined; 4290 } 4291 // object construction must be done this way. 4292 // https://github.com/moment/moment/issues/1423 4293 c._isAMomentObject = true; 4294 c._useUTC = c._isUTC = isUTC; 4295 c._l = locale; 4296 c._i = input; 4297 c._f = format; 4298 c._strict = strict; 4299 4300 return createFromConfig(c); 4301} 4302 4303function createLocal (input, format, locale, strict) { 4304 return createLocalOrUTC(input, format, locale, strict, false); 4305} 4306 4307var prototypeMin = deprecate( 4308 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', 4309 function () { 4310 var other = createLocal.apply(null, arguments); 4311 if (this.isValid() && other.isValid()) { 4312 return other < this ? this : other; 4313 } else { 4314 return createInvalid(); 4315 } 4316 } 4317); 4318 4319var prototypeMax = deprecate( 4320 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', 4321 function () { 4322 var other = createLocal.apply(null, arguments); 4323 if (this.isValid() && other.isValid()) { 4324 return other > this ? this : other; 4325 } else { 4326 return createInvalid(); 4327 } 4328 } 4329); 4330 4331// Pick a moment m from moments so that m[fn](other) is true for all 4332// other. This relies on the function fn to be transitive. 4333// 4334// moments should either be an array of moment objects or an array, whose 4335// first element is an array of moment objects. 4336function pickBy(fn, moments) { 4337 var res, i; 4338 if (moments.length === 1 && isArray(moments[0])) { 4339 moments = moments[0]; 4340 } 4341 if (!moments.length) { 4342 return createLocal(); 4343 } 4344 res = moments[0]; 4345 for (i = 1; i < moments.length; ++i) { 4346 if (!moments[i].isValid() || moments[i][fn](res)) { 4347 res = moments[i]; 4348 } 4349 } 4350 return res; 4351} 4352 4353// TODO: Use [].sort instead? 4354function min () { 4355 var args = [].slice.call(arguments, 0); 4356 4357 return pickBy('isBefore', args); 4358} 4359 4360function max () { 4361 var args = [].slice.call(arguments, 0); 4362 4363 return pickBy('isAfter', args); 4364} 4365 4366var now = function () { 4367 return Date.now ? Date.now() : +(new Date()); 4368}; 4369 4370var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; 4371 4372function isDurationValid(m) { 4373 for (var key in m) { 4374 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { 4375 return false; 4376 } 4377 } 4378 4379 var unitHasDecimal = false; 4380 for (var i = 0; i < ordering.length; ++i) { 4381 if (m[ordering[i]]) { 4382 if (unitHasDecimal) { 4383 return false; // only allow non-integers for smallest unit 4384 } 4385 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { 4386 unitHasDecimal = true; 4387 } 4388 } 4389 } 4390 4391 return true; 4392} 4393 4394function isValid$1() { 4395 return this._isValid; 4396} 4397 4398function createInvalid$1() { 4399 return createDuration(NaN); 4400} 4401 4402function Duration (duration) { 4403 var normalizedInput = normalizeObjectUnits(duration), 4404 years = normalizedInput.year || 0, 4405 quarters = normalizedInput.quarter || 0, 4406 months = normalizedInput.month || 0, 4407 weeks = normalizedInput.week || 0, 4408 days = normalizedInput.day || 0, 4409 hours = normalizedInput.hour || 0, 4410 minutes = normalizedInput.minute || 0, 4411 seconds = normalizedInput.second || 0, 4412 milliseconds = normalizedInput.millisecond || 0; 4413 4414 this._isValid = isDurationValid(normalizedInput); 4415 4416 // representation for dateAddRemove 4417 this._milliseconds = +milliseconds + 4418 seconds * 1e3 + // 1000 4419 minutes * 6e4 + // 1000 * 60 4420 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 4421 // Because of dateAddRemove treats 24 hours as different from a 4422 // day when working around DST, we need to store them separately 4423 this._days = +days + 4424 weeks * 7; 4425 // It is impossible to translate months into days without knowing 4426 // which months you are are talking about, so we have to store 4427 // it separately. 4428 this._months = +months + 4429 quarters * 3 + 4430 years * 12; 4431 4432 this._data = {}; 4433 4434 this._locale = getLocale(); 4435 4436 this._bubble(); 4437} 4438 4439function isDuration (obj) { 4440 return obj instanceof Duration; 4441} 4442 4443function absRound (number) { 4444 if (number < 0) { 4445 return Math.round(-1 * number) * -1; 4446 } else { 4447 return Math.round(number); 4448 } 4449} 4450 4451// FORMATTING 4452 4453function offset (token, separator) { 4454 addFormatToken(token, 0, 0, function () { 4455 var offset = this.utcOffset(); 4456 var sign = '+'; 4457 if (offset < 0) { 4458 offset = -offset; 4459 sign = '-'; 4460 } 4461 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); 4462 }); 4463} 4464 4465offset('Z', ':'); 4466offset('ZZ', ''); 4467 4468// PARSING 4469 4470addRegexToken('Z', matchShortOffset); 4471addRegexToken('ZZ', matchShortOffset); 4472addParseToken(['Z', 'ZZ'], function (input, array, config) { 4473 config._useUTC = true; 4474 config._tzm = offsetFromString(matchShortOffset, input); 4475}); 4476 4477// HELPERS 4478 4479// timezone chunker 4480// '+10:00' > ['10', '00'] 4481// '-1530' > ['-15', '30'] 4482var chunkOffset = /([\+\-]|\d\d)/gi; 4483 4484function offsetFromString(matcher, string) { 4485 var matches = (string || '').match(matcher); 4486 4487 if (matches === null) { 4488 return null; 4489 } 4490 4491 var chunk = matches[matches.length - 1] || []; 4492 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; 4493 var minutes = +(parts[1] * 60) + toInt(parts[2]); 4494 4495 return minutes === 0 ? 4496 0 : 4497 parts[0] === '+' ? minutes : -minutes; 4498} 4499 4500// Return a moment from input, that is local/utc/zone equivalent to model. 4501function cloneWithOffset(input, model) { 4502 var res, diff; 4503 if (model._isUTC) { 4504 res = model.clone(); 4505 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); 4506 // Use low-level api, because this fn is low-level api. 4507 res._d.setTime(res._d.valueOf() + diff); 4508 hooks.updateOffset(res, false); 4509 return res; 4510 } else { 4511 return createLocal(input).local(); 4512 } 4513} 4514 4515function getDateOffset (m) { 4516 // On Firefox.24 Date#getTimezoneOffset returns a floating point. 4517 // https://github.com/moment/moment/pull/1871 4518 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; 4519} 4520 4521// HOOKS 4522 4523// This function will be called whenever a moment is mutated. 4524// It is intended to keep the offset in sync with the timezone. 4525hooks.updateOffset = function () {}; 4526 4527// MOMENTS 4528 4529// keepLocalTime = true means only change the timezone, without 4530// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> 4531// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset 4532// +0200, so we adjust the time as needed, to be valid. 4533// 4534// Keeping the time actually adds/subtracts (one hour) 4535// from the actual represented time. That is why we call updateOffset 4536// a second time. In case it wants us to change the offset again 4537// _changeInProgress == true case, then we have to adjust, because 4538// there is no such time in the given timezone. 4539function getSetOffset (input, keepLocalTime, keepMinutes) { 4540 var offset = this._offset || 0, 4541 localAdjust; 4542 if (!this.isValid()) { 4543 return input != null ? this : NaN; 4544 } 4545 if (input != null) { 4546 if (typeof input === 'string') { 4547 input = offsetFromString(matchShortOffset, input); 4548 if (input === null) { 4549 return this; 4550 } 4551 } else if (Math.abs(input) < 16 && !keepMinutes) { 4552 input = input * 60; 4553 } 4554 if (!this._isUTC && keepLocalTime) { 4555 localAdjust = getDateOffset(this); 4556 } 4557 this._offset = input; 4558 this._isUTC = true; 4559 if (localAdjust != null) { 4560 this.add(localAdjust, 'm'); 4561 } 4562 if (offset !== input) { 4563 if (!keepLocalTime || this._changeInProgress) { 4564 addSubtract(this, createDuration(input - offset, 'm'), 1, false); 4565 } else if (!this._changeInProgress) { 4566 this._changeInProgress = true; 4567 hooks.updateOffset(this, true); 4568 this._changeInProgress = null; 4569 } 4570 } 4571 return this; 4572 } else { 4573 return this._isUTC ? offset : getDateOffset(this); 4574 } 4575} 4576 4577function getSetZone (input, keepLocalTime) { 4578 if (input != null) { 4579 if (typeof input !== 'string') { 4580 input = -input; 4581 } 4582 4583 this.utcOffset(input, keepLocalTime); 4584 4585 return this; 4586 } else { 4587 return -this.utcOffset(); 4588 } 4589} 4590 4591function setOffsetToUTC (keepLocalTime) { 4592 return this.utcOffset(0, keepLocalTime); 4593} 4594 4595function setOffsetToLocal (keepLocalTime) { 4596 if (this._isUTC) { 4597 this.utcOffset(0, keepLocalTime); 4598 this._isUTC = false; 4599 4600 if (keepLocalTime) { 4601 this.subtract(getDateOffset(this), 'm'); 4602 } 4603 } 4604 return this; 4605} 4606
vendor: 17,969 bytes, lines 4607-5186
4607function setOffsetToParsedOffset () { 4608 if (this._tzm != null) { 4609 this.utcOffset(this._tzm, false, true); 4610 } else if (typeof this._i === 'string') { 4611 var tZone = offsetFromString(matchOffset, this._i); 4612 if (tZone != null) { 4613 this.utcOffset(tZone); 4614 } 4615 else { 4616 this.utcOffset(0, true); 4617 } 4618 } 4619 return this; 4620} 4621 4622function hasAlignedHourOffset (input) { 4623 if (!this.isValid()) { 4624 return false; 4625 } 4626 input = input ? createLocal(input).utcOffset() : 0; 4627 4628 return (this.utcOffset() - input) % 60 === 0; 4629} 4630 4631function isDaylightSavingTime () { 4632 return ( 4633 this.utcOffset() > this.clone().month(0).utcOffset() || 4634 this.utcOffset() > this.clone().month(5).utcOffset() 4635 ); 4636} 4637 4638function isDaylightSavingTimeShifted () { 4639 if (!isUndefined(this._isDSTShifted)) { 4640 return this._isDSTShifted; 4641 } 4642 4643 var c = {}; 4644 4645 copyConfig(c, this); 4646 c = prepareConfig(c); 4647 4648 if (c._a) { 4649 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); 4650 this._isDSTShifted = this.isValid() && 4651 compareArrays(c._a, other.toArray()) > 0; 4652 } else { 4653 this._isDSTShifted = false; 4654 } 4655 4656 return this._isDSTShifted; 4657} 4658 4659function isLocal () { 4660 return this.isValid() ? !this._isUTC : false; 4661} 4662 4663function isUtcOffset () { 4664 return this.isValid() ? this._isUTC : false; 4665} 4666 4667function isUtc () { 4668 return this.isValid() ? this._isUTC && this._offset === 0 : false; 4669} 4670 4671// ASP.NET json date format regex 4672var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; 4673 4674// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html 4675// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere 4676// and further modified to allow for strings containing both week and day 4677var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; 4678 4679function createDuration (input, key) { 4680 var duration = input, 4681 // matching against regexp is expensive, do it on demand 4682 match = null, 4683 sign, 4684 ret, 4685 diffRes; 4686 4687 if (isDuration(input)) { 4688 duration = { 4689 ms : input._milliseconds, 4690 d : input._days, 4691 M : input._months 4692 }; 4693 } else if (isNumber(input)) { 4694 duration = {}; 4695 if (key) { 4696 duration[key] = input; 4697 } else { 4698 duration.milliseconds = input; 4699 } 4700 } else if (!!(match = aspNetRegex.exec(input))) { 4701 sign = (match[1] === '-') ? -1 : 1; 4702 duration = { 4703 y : 0, 4704 d : toInt(match[DATE]) * sign, 4705 h : toInt(match[HOUR]) * sign, 4706 m : toInt(match[MINUTE]) * sign, 4707 s : toInt(match[SECOND]) * sign, 4708 ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match 4709 }; 4710 } else if (!!(match = isoRegex.exec(input))) { 4711 sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1; 4712 duration = { 4713 y : parseIso(match[2], sign), 4714 M : parseIso(match[3], sign), 4715 w : parseIso(match[4], sign), 4716 d : parseIso(match[5], sign), 4717 h : parseIso(match[6], sign), 4718 m : parseIso(match[7], sign), 4719 s : parseIso(match[8], sign) 4720 }; 4721 } else if (duration == null) {// checks for null or undefined 4722 duration = {}; 4723 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { 4724 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); 4725 4726 duration = {}; 4727 duration.ms = diffRes.milliseconds; 4728 duration.M = diffRes.months; 4729 } 4730 4731 ret = new Duration(duration); 4732 4733 if (isDuration(input) && hasOwnProp(input, '_locale')) { 4734 ret._locale = input._locale; 4735 } 4736 4737 return ret; 4738} 4739 4740createDuration.fn = Duration.prototype; 4741createDuration.invalid = createInvalid$1; 4742 4743function parseIso (inp, sign) { 4744 // We'd normally use ~~inp for this, but unfortunately it also 4745 // converts floats to ints. 4746 // inp may be undefined, so careful calling replace on it. 4747 var res = inp && parseFloat(inp.replace(',', '.')); 4748 // apply sign while we're at it 4749 return (isNaN(res) ? 0 : res) * sign; 4750} 4751 4752function positiveMomentsDifference(base, other) { 4753 var res = {milliseconds: 0, months: 0}; 4754 4755 res.months = other.month() - base.month() + 4756 (other.year() - base.year()) * 12; 4757 if (base.clone().add(res.months, 'M').isAfter(other)) { 4758 --res.months; 4759 } 4760 4761 res.milliseconds = +other - +(base.clone().add(res.months, 'M')); 4762 4763 return res; 4764} 4765 4766function momentsDifference(base, other) { 4767 var res; 4768 if (!(base.isValid() && other.isValid())) { 4769 return {milliseconds: 0, months: 0}; 4770 } 4771 4772 other = cloneWithOffset(other, base); 4773 if (base.isBefore(other)) { 4774 res = positiveMomentsDifference(base, other); 4775 } else { 4776 res = positiveMomentsDifference(other, base); 4777 res.milliseconds = -res.milliseconds; 4778 res.months = -res.months; 4779 } 4780 4781 return res; 4782} 4783 4784// TODO: remove 'name' arg after deprecation is removed 4785function createAdder(direction, name) { 4786 return function (val, period) { 4787 var dur, tmp; 4788 //invert the arguments, but complain about it 4789 if (period !== null && !isNaN(+period)) { 4790 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 4791 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); 4792 tmp = val; val = period; period = tmp; 4793 } 4794 4795 val = typeof val === 'string' ? +val : val; 4796 dur = createDuration(val, period); 4797 addSubtract(this, dur, direction); 4798 return this; 4799 }; 4800} 4801 4802function addSubtract (mom, duration, isAdding, updateOffset) { 4803 var milliseconds = duration._milliseconds, 4804 days = absRound(duration._days), 4805 months = absRound(duration._months); 4806 4807 if (!mom.isValid()) { 4808 // No op 4809 return; 4810 } 4811 4812 updateOffset = updateOffset == null ? true : updateOffset; 4813 4814 if (months) { 4815 setMonth(mom, get(mom, 'Month') + months * isAdding); 4816 } 4817 if (days) { 4818 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); 4819 } 4820 if (milliseconds) { 4821 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); 4822 } 4823 if (updateOffset) { 4824 hooks.updateOffset(mom, days || months); 4825 } 4826} 4827 4828var add = createAdder(1, 'add'); 4829var subtract = createAdder(-1, 'subtract'); 4830 4831function getCalendarFormat(myMoment, now) { 4832 var diff = myMoment.diff(now, 'days', true); 4833 return diff < -6 ? 'sameElse' : 4834 diff < -1 ? 'lastWeek' : 4835 diff < 0 ? 'lastDay' : 4836 diff < 1 ? 'sameDay' : 4837 diff < 2 ? 'nextDay' : 4838 diff < 7 ? 'nextWeek' : 'sameElse'; 4839} 4840 4841function calendar$1 (time, formats) { 4842 // We want to compare the start of today, vs this. 4843 // Getting start-of-today depends on whether we're local/utc/offset or not. 4844 var now = time || createLocal(), 4845 sod = cloneWithOffset(now, this).startOf('day'), 4846 format = hooks.calendarFormat(this, sod) || 'sameElse'; 4847 4848 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); 4849 4850 return this.format(output || this.localeData().calendar(format, this, createLocal(now))); 4851} 4852 4853function clone () { 4854 return new Moment(this); 4855} 4856 4857function isAfter (input, units) { 4858 var localInput = isMoment(input) ? input : createLocal(input); 4859 if (!(this.isValid() && localInput.isValid())) { 4860 return false; 4861 } 4862 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 4863 if (units === 'millisecond') { 4864 return this.valueOf() > localInput.valueOf(); 4865 } else { 4866 return localInput.valueOf() < this.clone().startOf(units).valueOf(); 4867 } 4868} 4869 4870function isBefore (input, units) { 4871 var localInput = isMoment(input) ? input : createLocal(input); 4872 if (!(this.isValid() && localInput.isValid())) { 4873 return false; 4874 } 4875 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 4876 if (units === 'millisecond') { 4877 return this.valueOf() < localInput.valueOf(); 4878 } else { 4879 return this.clone().endOf(units).valueOf() < localInput.valueOf(); 4880 } 4881} 4882 4883function isBetween (from, to, units, inclusivity) { 4884 inclusivity = inclusivity || '()'; 4885 return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && 4886 (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); 4887} 4888 4889function isSame (input, units) { 4890 var localInput = isMoment(input) ? input : createLocal(input), 4891 inputMs; 4892 if (!(this.isValid() && localInput.isValid())) { 4893 return false; 4894 } 4895 units = normalizeUnits(units || 'millisecond'); 4896 if (units === 'millisecond') { 4897 return this.valueOf() === localInput.valueOf(); 4898 } else { 4899 inputMs = localInput.valueOf(); 4900 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); 4901 } 4902} 4903 4904function isSameOrAfter (input, units) { 4905 return this.isSame(input, units) || this.isAfter(input,units); 4906} 4907 4908function isSameOrBefore (input, units) { 4909 return this.isSame(input, units) || this.isBefore(input,units); 4910} 4911 4912function diff (input, units, asFloat) { 4913 var that, 4914 zoneDelta, 4915 delta, output; 4916 4917 if (!this.isValid()) { 4918 return NaN; 4919 } 4920 4921 that = cloneWithOffset(input, this); 4922 4923 if (!that.isValid()) { 4924 return NaN; 4925 } 4926 4927 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; 4928 4929 units = normalizeUnits(units); 4930 4931 switch (units) { 4932 case 'year': output = monthDiff(this, that) / 12; break; 4933 case 'month': output = monthDiff(this, that); break; 4934 case 'quarter': output = monthDiff(this, that) / 3; break; 4935 case 'second': output = (this - that) / 1e3; break; // 1000 4936 case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 4937 case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 4938 case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst 4939 case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst 4940 default: output = this - that; 4941 } 4942 4943 return asFloat ? output : absFloor(output); 4944} 4945 4946function monthDiff (a, b) { 4947 // difference in months 4948 var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), 4949 // b is in (anchor - 1 month, anchor + 1 month) 4950 anchor = a.clone().add(wholeMonthDiff, 'months'), 4951 anchor2, adjust; 4952 4953 if (b - anchor < 0) { 4954 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); 4955 // linear across the month 4956 adjust = (b - anchor) / (anchor - anchor2); 4957 } else { 4958 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); 4959 // linear across the month 4960 adjust = (b - anchor) / (anchor2 - anchor); 4961 } 4962 4963 //check for negative zero, return zero if negative zero 4964 return -(wholeMonthDiff + adjust) || 0; 4965} 4966 4967hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; 4968hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; 4969 4970function toString () { 4971 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); 4972} 4973 4974function toISOString(keepOffset) { 4975 if (!this.isValid()) { 4976 return null; 4977 } 4978 var utc = keepOffset !== true; 4979 var m = utc ? this.clone().utc() : this; 4980 if (m.year() < 0 || m.year() > 9999) { 4981 return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); 4982 } 4983 if (isFunction(Date.prototype.toISOString)) { 4984 // native implementation is ~50x faster, use it when we can 4985 if (utc) { 4986 return this.toDate().toISOString(); 4987 } else { 4988 return new Date(this._d.valueOf()).toISOString().replace('Z', formatMoment(m, 'Z')); 4989 } 4990 } 4991 return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); 4992} 4993 4994/** 4995 * Return a human readable representation of a moment that can 4996 * also be evaluated to get a new moment which is the same 4997 * 4998 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects 4999 */ 5000function inspect () { 5001 if (!this.isValid()) { 5002 return 'moment.invalid(/* ' + this._i + ' */)'; 5003 } 5004 var func = 'moment'; 5005 var zone = ''; 5006 if (!this.isLocal()) { 5007 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; 5008 zone = 'Z'; 5009 } 5010 var prefix = '[' + func + '("]'; 5011 var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; 5012 var datetime = '-MM-DD[T]HH:mm:ss.SSS'; 5013 var suffix = zone + '[")]'; 5014 5015 return this.format(prefix + year + datetime + suffix); 5016} 5017 5018function format (inputString) { 5019 if (!inputString) { 5020 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; 5021 } 5022 var output = formatMoment(this, inputString); 5023 return this.localeData().postformat(output); 5024} 5025 5026function from (time, withoutSuffix) { 5027 if (this.isValid() && 5028 ((isMoment(time) && time.isValid()) || 5029 createLocal(time).isValid())) { 5030 return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); 5031 } else { 5032 return this.localeData().invalidDate(); 5033 } 5034} 5035 5036function fromNow (withoutSuffix) { 5037 return this.from(createLocal(), withoutSuffix); 5038} 5039 5040function to (time, withoutSuffix) { 5041 if (this.isValid() && 5042 ((isMoment(time) && time.isValid()) || 5043 createLocal(time).isValid())) { 5044 return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); 5045 } else { 5046 return this.localeData().invalidDate(); 5047 } 5048} 5049 5050function toNow (withoutSuffix) { 5051 return this.to(createLocal(), withoutSuffix); 5052} 5053 5054// If passed a locale key, it will set the locale for this 5055// instance. Otherwise, it will return the locale configuration 5056// variables for this instance. 5057function locale (key) { 5058 var newLocaleData; 5059 5060 if (key === undefined) { 5061 return this._locale._abbr; 5062 } else { 5063 newLocaleData = getLocale(key); 5064 if (newLocaleData != null) { 5065 this._locale = newLocaleData; 5066 } 5067 return this; 5068 } 5069} 5070 5071var lang = deprecate( 5072 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', 5073 function (key) { 5074 if (key === undefined) { 5075 return this.localeData(); 5076 } else { 5077 return this.locale(key); 5078 } 5079 } 5080); 5081 5082function localeData () { 5083 return this._locale; 5084} 5085 5086function startOf (units) { 5087 units = normalizeUnits(units); 5088 // the following switch intentionally omits break keywords 5089 // to utilize falling through the cases. 5090 switch (units) { 5091 case 'year': 5092 this.month(0); 5093 /* falls through */ 5094 case 'quarter': 5095 case 'month': 5096 this.date(1); 5097 /* falls through */ 5098 case 'week': 5099 case 'isoWeek': 5100 case 'day': 5101 case 'date': 5102 this.hours(0); 5103 /* falls through */ 5104 case 'hour': 5105 this.minutes(0); 5106 /* falls through */ 5107 case 'minute': 5108 this.seconds(0); 5109 /* falls through */ 5110 case 'second': 5111 this.milliseconds(0); 5112 } 5113 5114 // weeks are a special case 5115 if (units === 'week') { 5116 this.weekday(0); 5117 } 5118 if (units === 'isoWeek') { 5119 this.isoWeekday(1); 5120 } 5121 5122 // quarters are also special 5123 if (units === 'quarter') { 5124 this.month(Math.floor(this.month() / 3) * 3); 5125 } 5126 5127 return this; 5128} 5129 5130function endOf (units) { 5131 units = normalizeUnits(units); 5132 if (units === undefined || units === 'millisecond') { 5133 return this; 5134 } 5135 5136 // 'date' is an alias for 'day', so it should be considered as such. 5137 if (units === 'date') { 5138 units = 'day'; 5139 } 5140 5141 return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); 5142} 5143 5144function valueOf () { 5145 return this._d.valueOf() - ((this._offset || 0) * 60000); 5146} 5147 5148function unix () { 5149 return Math.floor(this.valueOf() / 1000); 5150} 5151 5152function toDate () { 5153 return new Date(this.valueOf()); 5154} 5155 5156function toArray () { 5157 var m = this; 5158 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; 5159} 5160 5161function toObject () { 5162 var m = this; 5163 return { 5164 years: m.year(), 5165 months: m.month(), 5166 date: m.date(), 5167 hours: m.hours(), 5168 minutes: m.minutes(), 5169 seconds: m.seconds(), 5170 milliseconds: m.milliseconds() 5171 }; 5172} 5173 5174function toJSON () { 5175 // new Date(NaN).toJSON() === null 5176 return this.isValid() ? this.toISOString() : null; 5177} 5178 5179function isValid$2 () { 5180 return isValid(this); 5181} 5182 5183function parsingFlags () { 5184 return extend({}, getParsingFlags(this)); 5185} 5186
vendor: 12,433 bytes, lines 5187-5658
5187function invalidAt () { 5188 return getParsingFlags(this).overflow; 5189} 5190 5191function creationData() { 5192 return { 5193 input: this._i, 5194 format: this._f, 5195 locale: this._locale, 5196 isUTC: this._isUTC, 5197 strict: this._strict 5198 }; 5199} 5200 5201// FORMATTING 5202 5203addFormatToken(0, ['gg', 2], 0, function () { 5204 return this.weekYear() % 100; 5205}); 5206 5207addFormatToken(0, ['GG', 2], 0, function () { 5208 return this.isoWeekYear() % 100; 5209}); 5210 5211function addWeekYearFormatToken (token, getter) { 5212 addFormatToken(0, [token, token.length], 0, getter); 5213} 5214 5215addWeekYearFormatToken('gggg', 'weekYear'); 5216addWeekYearFormatToken('ggggg', 'weekYear'); 5217addWeekYearFormatToken('GGGG', 'isoWeekYear'); 5218addWeekYearFormatToken('GGGGG', 'isoWeekYear'); 5219 5220// ALIASES 5221 5222addUnitAlias('weekYear', 'gg'); 5223addUnitAlias('isoWeekYear', 'GG'); 5224 5225// PRIORITY 5226 5227addUnitPriority('weekYear', 1); 5228addUnitPriority('isoWeekYear', 1); 5229 5230 5231// PARSING 5232 5233addRegexToken('G', matchSigned); 5234addRegexToken('g', matchSigned); 5235addRegexToken('GG', match1to2, match2); 5236addRegexToken('gg', match1to2, match2); 5237addRegexToken('GGGG', match1to4, match4); 5238addRegexToken('gggg', match1to4, match4); 5239addRegexToken('GGGGG', match1to6, match6); 5240addRegexToken('ggggg', match1to6, match6); 5241 5242addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { 5243 week[token.substr(0, 2)] = toInt(input); 5244}); 5245 5246addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { 5247 week[token] = hooks.parseTwoDigitYear(input); 5248}); 5249 5250// MOMENTS 5251 5252function getSetWeekYear (input) { 5253 return getSetWeekYearHelper.call(this, 5254 input, 5255 this.week(), 5256 this.weekday(), 5257 this.localeData()._week.dow, 5258 this.localeData()._week.doy); 5259} 5260 5261function getSetISOWeekYear (input) { 5262 return getSetWeekYearHelper.call(this, 5263 input, this.isoWeek(), this.isoWeekday(), 1, 4); 5264} 5265 5266function getISOWeeksInYear () { 5267 return weeksInYear(this.year(), 1, 4); 5268} 5269 5270function getWeeksInYear () { 5271 var weekInfo = this.localeData()._week; 5272 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); 5273} 5274 5275function getSetWeekYearHelper(input, week, weekday, dow, doy) { 5276 var weeksTarget; 5277 if (input == null) { 5278 return weekOfYear(this, dow, doy).year; 5279 } else { 5280 weeksTarget = weeksInYear(input, dow, doy); 5281 if (week > weeksTarget) { 5282 week = weeksTarget; 5283 } 5284 return setWeekAll.call(this, input, week, weekday, dow, doy); 5285 } 5286} 5287 5288function setWeekAll(weekYear, week, weekday, dow, doy) { 5289 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), 5290 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); 5291 5292 this.year(date.getUTCFullYear()); 5293 this.month(date.getUTCMonth()); 5294 this.date(date.getUTCDate()); 5295 return this; 5296} 5297 5298// FORMATTING 5299 5300addFormatToken('Q', 0, 'Qo', 'quarter'); 5301 5302// ALIASES 5303 5304addUnitAlias('quarter', 'Q'); 5305 5306// PRIORITY 5307 5308addUnitPriority('quarter', 7); 5309 5310// PARSING 5311 5312addRegexToken('Q', match1); 5313addParseToken('Q', function (input, array) { 5314 array[MONTH] = (toInt(input) - 1) * 3; 5315}); 5316 5317// MOMENTS 5318 5319function getSetQuarter (input) { 5320 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); 5321} 5322 5323// FORMATTING 5324 5325addFormatToken('D', ['DD', 2], 'Do', 'date'); 5326 5327// ALIASES 5328 5329addUnitAlias('date', 'D'); 5330 5331// PRIOROITY 5332addUnitPriority('date', 9); 5333 5334// PARSING 5335 5336addRegexToken('D', match1to2); 5337addRegexToken('DD', match1to2, match2); 5338addRegexToken('Do', function (isStrict, locale) { 5339 // TODO: Remove "ordinalParse" fallback in next major release. 5340 return isStrict ? 5341 (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : 5342 locale._dayOfMonthOrdinalParseLenient; 5343}); 5344 5345addParseToken(['D', 'DD'], DATE); 5346addParseToken('Do', function (input, array) { 5347 array[DATE] = toInt(input.match(match1to2)[0]); 5348}); 5349 5350// MOMENTS 5351 5352var getSetDayOfMonth = makeGetSet('Date', true); 5353 5354// FORMATTING 5355 5356addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); 5357 5358// ALIASES 5359 5360addUnitAlias('dayOfYear', 'DDD'); 5361 5362// PRIORITY 5363addUnitPriority('dayOfYear', 4); 5364 5365// PARSING 5366 5367addRegexToken('DDD', match1to3); 5368addRegexToken('DDDD', match3); 5369addParseToken(['DDD', 'DDDD'], function (input, array, config) { 5370 config._dayOfYear = toInt(input); 5371}); 5372 5373// HELPERS 5374 5375// MOMENTS 5376 5377function getSetDayOfYear (input) { 5378 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; 5379 return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); 5380} 5381 5382// FORMATTING 5383 5384addFormatToken('m', ['mm', 2], 0, 'minute'); 5385 5386// ALIASES 5387 5388addUnitAlias('minute', 'm'); 5389 5390// PRIORITY 5391 5392addUnitPriority('minute', 14); 5393 5394// PARSING 5395 5396addRegexToken('m', match1to2); 5397addRegexToken('mm', match1to2, match2); 5398addParseToken(['m', 'mm'], MINUTE); 5399 5400// MOMENTS 5401 5402var getSetMinute = makeGetSet('Minutes', false); 5403 5404// FORMATTING 5405 5406addFormatToken('s', ['ss', 2], 0, 'second'); 5407 5408// ALIASES 5409 5410addUnitAlias('second', 's'); 5411 5412// PRIORITY 5413 5414addUnitPriority('second', 15); 5415 5416// PARSING 5417 5418addRegexToken('s', match1to2); 5419addRegexToken('ss', match1to2, match2); 5420addParseToken(['s', 'ss'], SECOND); 5421 5422// MOMENTS 5423 5424var getSetSecond = makeGetSet('Seconds', false); 5425 5426// FORMATTING 5427 5428addFormatToken('S', 0, 0, function () { 5429 return ~~(this.millisecond() / 100); 5430}); 5431 5432addFormatToken(0, ['SS', 2], 0, function () { 5433 return ~~(this.millisecond() / 10); 5434}); 5435 5436addFormatToken(0, ['SSS', 3], 0, 'millisecond'); 5437addFormatToken(0, ['SSSS', 4], 0, function () { 5438 return this.millisecond() * 10; 5439}); 5440addFormatToken(0, ['SSSSS', 5], 0, function () { 5441 return this.millisecond() * 100; 5442}); 5443addFormatToken(0, ['SSSSSS', 6], 0, function () { 5444 return this.millisecond() * 1000; 5445}); 5446addFormatToken(0, ['SSSSSSS', 7], 0, function () { 5447 return this.millisecond() * 10000; 5448}); 5449addFormatToken(0, ['SSSSSSSS', 8], 0, function () { 5450 return this.millisecond() * 100000; 5451}); 5452addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { 5453 return this.millisecond() * 1000000; 5454}); 5455 5456 5457// ALIASES 5458 5459addUnitAlias('millisecond', 'ms'); 5460 5461// PRIORITY 5462 5463addUnitPriority('millisecond', 16); 5464 5465// PARSING 5466 5467addRegexToken('S', match1to3, match1); 5468addRegexToken('SS', match1to3, match2); 5469addRegexToken('SSS', match1to3, match3); 5470 5471var token; 5472for (token = 'SSSS'; token.length <= 9; token += 'S') { 5473 addRegexToken(token, matchUnsigned); 5474} 5475 5476function parseMs(input, array) { 5477 array[MILLISECOND] = toInt(('0.' + input) * 1000); 5478} 5479 5480for (token = 'S'; token.length <= 9; token += 'S') { 5481 addParseToken(token, parseMs); 5482} 5483// MOMENTS 5484 5485var getSetMillisecond = makeGetSet('Milliseconds', false); 5486 5487// FORMATTING 5488 5489addFormatToken('z', 0, 0, 'zoneAbbr'); 5490addFormatToken('zz', 0, 0, 'zoneName'); 5491 5492// MOMENTS 5493 5494function getZoneAbbr () { 5495 return this._isUTC ? 'UTC' : ''; 5496} 5497 5498function getZoneName () { 5499 return this._isUTC ? 'Coordinated Universal Time' : ''; 5500} 5501 5502var proto = Moment.prototype; 5503 5504proto.add = add; 5505proto.calendar = calendar$1; 5506proto.clone = clone; 5507proto.diff = diff; 5508proto.endOf = endOf; 5509proto.format = format; 5510proto.from = from; 5511proto.fromNow = fromNow; 5512proto.to = to; 5513proto.toNow = toNow; 5514proto.get = stringGet; 5515proto.invalidAt = invalidAt; 5516proto.isAfter = isAfter; 5517proto.isBefore = isBefore; 5518proto.isBetween = isBetween; 5519proto.isSame = isSame; 5520proto.isSameOrAfter = isSameOrAfter; 5521proto.isSameOrBefore = isSameOrBefore; 5522proto.isValid = isValid$2; 5523proto.lang = lang; 5524proto.locale = locale; 5525proto.localeData = localeData; 5526proto.max = prototypeMax; 5527proto.min = prototypeMin; 5528proto.parsingFlags = parsingFlags; 5529proto.set = stringSet; 5530proto.startOf = startOf; 5531proto.subtract = subtract; 5532proto.toArray = toArray; 5533proto.toObject = toObject; 5534proto.toDate = toDate; 5535proto.toISOString = toISOString; 5536proto.inspect = inspect; 5537proto.toJSON = toJSON; 5538proto.toString = toString; 5539proto.unix = unix; 5540proto.valueOf = valueOf; 5541proto.creationData = creationData; 5542 5543// Year 5544proto.year = getSetYear; 5545proto.isLeapYear = getIsLeapYear; 5546 5547// Week Year 5548proto.weekYear = getSetWeekYear; 5549proto.isoWeekYear = getSetISOWeekYear; 5550 5551// Quarter 5552proto.quarter = proto.quarters = getSetQuarter; 5553 5554// Month 5555proto.month = getSetMonth; 5556proto.daysInMonth = getDaysInMonth; 5557 5558// Week 5559proto.week = proto.weeks = getSetWeek; 5560proto.isoWeek = proto.isoWeeks = getSetISOWeek; 5561proto.weeksInYear = getWeeksInYear; 5562proto.isoWeeksInYear = getISOWeeksInYear; 5563 5564// Day 5565proto.date = getSetDayOfMonth; 5566proto.day = proto.days = getSetDayOfWeek; 5567proto.weekday = getSetLocaleDayOfWeek; 5568proto.isoWeekday = getSetISODayOfWeek; 5569proto.dayOfYear = getSetDayOfYear; 5570 5571// Hour 5572proto.hour = proto.hours = getSetHour; 5573 5574// Minute 5575proto.minute = proto.minutes = getSetMinute; 5576 5577// Second 5578proto.second = proto.seconds = getSetSecond; 5579 5580// Millisecond 5581proto.millisecond = proto.milliseconds = getSetMillisecond; 5582 5583// Offset 5584proto.utcOffset = getSetOffset; 5585proto.utc = setOffsetToUTC; 5586proto.local = setOffsetToLocal; 5587proto.parseZone = setOffsetToParsedOffset; 5588proto.hasAlignedHourOffset = hasAlignedHourOffset; 5589proto.isDST = isDaylightSavingTime; 5590proto.isLocal = isLocal; 5591proto.isUtcOffset = isUtcOffset; 5592proto.isUtc = isUtc; 5593proto.isUTC = isUtc; 5594 5595// Timezone 5596proto.zoneAbbr = getZoneAbbr; 5597proto.zoneName = getZoneName; 5598 5599// Deprecations 5600proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); 5601proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); 5602proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); 5603proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); 5604proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); 5605 5606function createUnix (input) { 5607 return createLocal(input * 1000); 5608} 5609 5610function createInZone () { 5611 return createLocal.apply(null, arguments).parseZone(); 5612} 5613 5614function preParsePostFormat (string) { 5615 return string; 5616} 5617 5618var proto$1 = Locale.prototype; 5619 5620proto$1.calendar = calendar; 5621proto$1.longDateFormat = longDateFormat; 5622proto$1.invalidDate = invalidDate; 5623proto$1.ordinal = ordinal; 5624proto$1.preparse = preParsePostFormat; 5625proto$1.postformat = preParsePostFormat; 5626proto$1.relativeTime = relativeTime; 5627proto$1.pastFuture = pastFuture; 5628proto$1.set = set; 5629 5630// Month 5631proto$1.months = localeMonths; 5632proto$1.monthsShort = localeMonthsShort; 5633proto$1.monthsParse = localeMonthsParse; 5634proto$1.monthsRegex = monthsRegex; 5635proto$1.monthsShortRegex = monthsShortRegex; 5636 5637// Week 5638proto$1.week = localeWeek; 5639proto$1.firstDayOfYear = localeFirstDayOfYear; 5640proto$1.firstDayOfWeek = localeFirstDayOfWeek; 5641 5642// Day of Week 5643proto$1.weekdays = localeWeekdays; 5644proto$1.weekdaysMin = localeWeekdaysMin; 5645proto$1.weekdaysShort = localeWeekdaysShort; 5646proto$1.weekdaysParse = localeWeekdaysParse; 5647 5648proto$1.weekdaysRegex = weekdaysRegex; 5649proto$1.weekdaysShortRegex = weekdaysShortRegex; 5650proto$1.weekdaysMinRegex = weekdaysMinRegex; 5651 5652// Hours 5653proto$1.isPM = localeIsPM; 5654proto$1.meridiem = localeMeridiem; 5655 5656function get$1 (format, index, field, setter) { 5657 var locale = getLocale(); 5658 var utc = createUTC().set(setter, index);
vendor: 8,224 bytes, lines 5659-5951
5659 return locale[field](utc, format); 5660} 5661 5662function listMonthsImpl (format, index, field) { 5663 if (isNumber(format)) { 5664 index = format; 5665 format = undefined; 5666 } 5667 5668 format = format || ''; 5669 5670 if (index != null) { 5671 return get$1(format, index, field, 'month'); 5672 } 5673 5674 var i; 5675 var out = []; 5676 for (i = 0; i < 12; i++) { 5677 out[i] = get$1(format, i, field, 'month'); 5678 } 5679 return out; 5680} 5681 5682// () 5683// (5) 5684// (fmt, 5) 5685// (fmt) 5686// (true) 5687// (true, 5) 5688// (true, fmt, 5) 5689// (true, fmt) 5690function listWeekdaysImpl (localeSorted, format, index, field) { 5691 if (typeof localeSorted === 'boolean') { 5692 if (isNumber(format)) { 5693 index = format; 5694 format = undefined; 5695 } 5696 5697 format = format || ''; 5698 } else { 5699 format = localeSorted; 5700 index = format; 5701 localeSorted = false; 5702 5703 if (isNumber(format)) { 5704 index = format; 5705 format = undefined; 5706 } 5707 5708 format = format || ''; 5709 } 5710 5711 var locale = getLocale(), 5712 shift = localeSorted ? locale._week.dow : 0; 5713 5714 if (index != null) { 5715 return get$1(format, (index + shift) % 7, field, 'day'); 5716 } 5717 5718 var i; 5719 var out = []; 5720 for (i = 0; i < 7; i++) { 5721 out[i] = get$1(format, (i + shift) % 7, field, 'day'); 5722 } 5723 return out; 5724} 5725 5726function listMonths (format, index) { 5727 return listMonthsImpl(format, index, 'months'); 5728} 5729 5730function listMonthsShort (format, index) { 5731 return listMonthsImpl(format, index, 'monthsShort'); 5732} 5733 5734function listWeekdays (localeSorted, format, index) { 5735 return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); 5736} 5737 5738function listWeekdaysShort (localeSorted, format, index) { 5739 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); 5740} 5741 5742function listWeekdaysMin (localeSorted, format, index) { 5743 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); 5744} 5745 5746getSetGlobalLocale('en', { 5747 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, 5748 ordinal : function (number) { 5749 var b = number % 10, 5750 output = (toInt(number % 100 / 10) === 1) ? 'th' : 5751 (b === 1) ? 'st' : 5752 (b === 2) ? 'nd' : 5753 (b === 3) ? 'rd' : 'th'; 5754 return number + output; 5755 } 5756}); 5757 5758// Side effect imports 5759hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); 5760hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); 5761 5762var mathAbs = Math.abs; 5763 5764function abs () { 5765 var data = this._data; 5766 5767 this._milliseconds = mathAbs(this._milliseconds); 5768 this._days = mathAbs(this._days); 5769 this._months = mathAbs(this._months); 5770 5771 data.milliseconds = mathAbs(data.milliseconds); 5772 data.seconds = mathAbs(data.seconds); 5773 data.minutes = mathAbs(data.minutes); 5774 data.hours = mathAbs(data.hours); 5775 data.months = mathAbs(data.months); 5776 data.years = mathAbs(data.years); 5777 5778 return this; 5779} 5780 5781function addSubtract$1 (duration, input, value, direction) { 5782 var other = createDuration(input, value); 5783 5784 duration._milliseconds += direction * other._milliseconds; 5785 duration._days += direction * other._days; 5786 duration._months += direction * other._months; 5787 5788 return duration._bubble(); 5789} 5790 5791// supports only 2.0-style add(1, 's') or add(duration) 5792function add$1 (input, value) { 5793 return addSubtract$1(this, input, value, 1); 5794} 5795 5796// supports only 2.0-style subtract(1, 's') or subtract(duration) 5797function subtract$1 (input, value) { 5798 return addSubtract$1(this, input, value, -1); 5799} 5800 5801function absCeil (number) { 5802 if (number < 0) { 5803 return Math.floor(number); 5804 } else { 5805 return Math.ceil(number); 5806 } 5807} 5808 5809function bubble () { 5810 var milliseconds = this._milliseconds; 5811 var days = this._days; 5812 var months = this._months; 5813 var data = this._data; 5814 var seconds, minutes, hours, years, monthsFromDays; 5815 5816 // if we have a mix of positive and negative values, bubble down first 5817 // check: https://github.com/moment/moment/issues/2166 5818 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || 5819 (milliseconds <= 0 && days <= 0 && months <= 0))) { 5820 milliseconds += absCeil(monthsToDays(months) + days) * 864e5; 5821 days = 0; 5822 months = 0; 5823 } 5824 5825 // The following code bubbles up values, see the tests for 5826 // examples of what that means. 5827 data.milliseconds = milliseconds % 1000; 5828 5829 seconds = absFloor(milliseconds / 1000); 5830 data.seconds = seconds % 60; 5831 5832 minutes = absFloor(seconds / 60); 5833 data.minutes = minutes % 60; 5834 5835 hours = absFloor(minutes / 60); 5836 data.hours = hours % 24; 5837 5838 days += absFloor(hours / 24); 5839 5840 // convert days to months 5841 monthsFromDays = absFloor(daysToMonths(days)); 5842 months += monthsFromDays; 5843 days -= absCeil(monthsToDays(monthsFromDays)); 5844 5845 // 12 months -> 1 year 5846 years = absFloor(months / 12); 5847 months %= 12; 5848 5849 data.days = days; 5850 data.months = months; 5851 data.years = years; 5852 5853 return this; 5854} 5855 5856function daysToMonths (days) { 5857 // 400 years have 146097 days (taking into account leap year rules) 5858 // 400 years have 12 months === 4800 5859 return days * 4800 / 146097; 5860} 5861 5862function monthsToDays (months) { 5863 // the reverse of daysToMonths 5864 return months * 146097 / 4800; 5865} 5866 5867function as (units) { 5868 if (!this.isValid()) { 5869 return NaN; 5870 } 5871 var days; 5872 var months; 5873 var milliseconds = this._milliseconds; 5874 5875 units = normalizeUnits(units); 5876 5877 if (units === 'month' || units === 'year') { 5878 days = this._days + milliseconds / 864e5; 5879 months = this._months + daysToMonths(days); 5880 return units === 'month' ? months : months / 12; 5881 } else { 5882 // handle milliseconds separately because of floating point math errors (issue #1867) 5883 days = this._days + Math.round(monthsToDays(this._months)); 5884 switch (units) { 5885 case 'week' : return days / 7 + milliseconds / 6048e5; 5886 case 'day' : return days + milliseconds / 864e5; 5887 case 'hour' : return days * 24 + milliseconds / 36e5; 5888 case 'minute' : return days * 1440 + milliseconds / 6e4; 5889 case 'second' : return days * 86400 + milliseconds / 1000; 5890 // Math.floor prevents floating point math errors here 5891 case 'millisecond': return Math.floor(days * 864e5) + milliseconds; 5892 default: throw new Error('Unknown unit ' + units); 5893 } 5894 } 5895} 5896 5897// TODO: Use this.as('ms')? 5898function valueOf$1 () { 5899 if (!this.isValid()) { 5900 return NaN; 5901 } 5902 return ( 5903 this._milliseconds + 5904 this._days * 864e5 + 5905 (this._months % 12) * 2592e6 + 5906 toInt(this._months / 12) * 31536e6 5907 ); 5908} 5909 5910function makeAs (alias) { 5911 return function () { 5912 return this.as(alias); 5913 }; 5914} 5915 5916var asMilliseconds = makeAs('ms'); 5917var asSeconds = makeAs('s'); 5918var asMinutes = makeAs('m'); 5919var asHours = makeAs('h'); 5920var asDays = makeAs('d'); 5921var asWeeks = makeAs('w'); 5922var asMonths = makeAs('M'); 5923var asYears = makeAs('y'); 5924 5925function clone$1 () { 5926 return createDuration(this); 5927} 5928 5929function get$2 (units) { 5930 units = normalizeUnits(units); 5931 return this.isValid() ? this[units + 's']() : NaN; 5932} 5933 5934function makeGetter(name) { 5935 return function () { 5936 return this.isValid() ? this._data[name] : NaN; 5937 }; 5938} 5939 5940var milliseconds = makeGetter('milliseconds'); 5941var seconds = makeGetter('seconds'); 5942var minutes = makeGetter('minutes'); 5943var hours = makeGetter('hours'); 5944var days = makeGetter('days'); 5945var months = makeGetter('months'); 5946var years = makeGetter('years'); 5947 5948function weeks () { 5949 return absFloor(this.days() / 7); 5950} 5951
vendor: 6,445 bytes, lines 5952-6147
5952var round = Math.round; 5953var thresholds = { 5954 ss: 44, // a few seconds to seconds 5955 s : 45, // seconds to minute 5956 m : 45, // minutes to hour 5957 h : 22, // hours to day 5958 d : 26, // days to month 5959 M : 11 // months to year 5960}; 5961 5962// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 5963function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { 5964 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 5965} 5966 5967function relativeTime$1 (posNegDuration, withoutSuffix, locale) { 5968 var duration = createDuration(posNegDuration).abs(); 5969 var seconds = round(duration.as('s')); 5970 var minutes = round(duration.as('m')); 5971 var hours = round(duration.as('h')); 5972 var days = round(duration.as('d')); 5973 var months = round(duration.as('M')); 5974 var years = round(duration.as('y')); 5975 5976 var a = seconds <= thresholds.ss && ['s', seconds] || 5977 seconds < thresholds.s && ['ss', seconds] || 5978 minutes <= 1 && ['m'] || 5979 minutes < thresholds.m && ['mm', minutes] || 5980 hours <= 1 && ['h'] || 5981 hours < thresholds.h && ['hh', hours] || 5982 days <= 1 && ['d'] || 5983 days < thresholds.d && ['dd', days] || 5984 months <= 1 && ['M'] || 5985 months < thresholds.M && ['MM', months] || 5986 years <= 1 && ['y'] || ['yy', years]; 5987 5988 a[2] = withoutSuffix; 5989 a[3] = +posNegDuration > 0; 5990 a[4] = locale; 5991 return substituteTimeAgo.apply(null, a); 5992} 5993 5994// This function allows you to set the rounding function for relative time strings 5995function getSetRelativeTimeRounding (roundingFunction) { 5996 if (roundingFunction === undefined) { 5997 return round; 5998 } 5999 if (typeof(roundingFunction) === 'function') { 6000 round = roundingFunction; 6001 return true; 6002 } 6003 return false; 6004} 6005 6006// This function allows you to set a threshold for relative time strings 6007function getSetRelativeTimeThreshold (threshold, limit) { 6008 if (thresholds[threshold] === undefined) { 6009 return false; 6010 } 6011 if (limit === undefined) { 6012 return thresholds[threshold]; 6013 } 6014 thresholds[threshold] = limit; 6015 if (threshold === 's') { 6016 thresholds.ss = limit - 1; 6017 } 6018 return true; 6019} 6020 6021function humanize (withSuffix) { 6022 if (!this.isValid()) { 6023 return this.localeData().invalidDate(); 6024 } 6025 6026 var locale = this.localeData(); 6027 var output = relativeTime$1(this, !withSuffix, locale); 6028 6029 if (withSuffix) { 6030 output = locale.pastFuture(+this, output); 6031 } 6032 6033 return locale.postformat(output); 6034} 6035 6036var abs$1 = Math.abs; 6037 6038function sign(x) { 6039 return ((x > 0) - (x < 0)) || +x; 6040} 6041 6042function toISOString$1() { 6043 // for ISO strings we do not use the normal bubbling rules: 6044 // * milliseconds bubble up until they become hours 6045 // * days do not bubble at all 6046 // * months bubble up until they become years 6047 // This is because there is no context-free conversion between hours and days 6048 // (think of clock changes) 6049 // and also not between days and months (28-31 days per month) 6050 if (!this.isValid()) { 6051 return this.localeData().invalidDate(); 6052 } 6053 6054 var seconds = abs$1(this._milliseconds) / 1000; 6055 var days = abs$1(this._days); 6056 var months = abs$1(this._months); 6057 var minutes, hours, years; 6058 6059 // 3600 seconds -> 60 minutes -> 1 hour 6060 minutes = absFloor(seconds / 60); 6061 hours = absFloor(minutes / 60); 6062 seconds %= 60; 6063 minutes %= 60; 6064 6065 // 12 months -> 1 year 6066 years = absFloor(months / 12); 6067 months %= 12; 6068 6069 6070 // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js 6071 var Y = years; 6072 var M = months; 6073 var D = days; 6074 var h = hours; 6075 var m = minutes; 6076 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; 6077 var total = this.asSeconds(); 6078 6079 if (!total) { 6080 // this is the same as C#'s (Noda) and python (isodate)... 6081 // but not other JS (goog.date) 6082 return 'P0D'; 6083 } 6084 6085 var totalSign = total < 0 ? '-' : ''; 6086 var ymSign = sign(this._months) !== sign(total) ? '-' : ''; 6087 var daysSign = sign(this._days) !== sign(total) ? '-' : ''; 6088 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; 6089 6090 return totalSign + 'P' + 6091 (Y ? ymSign + Y + 'Y' : '') + 6092 (M ? ymSign + M + 'M' : '') + 6093 (D ? daysSign + D + 'D' : '') + 6094 ((h || m || s) ? 'T' : '') + 6095 (h ? hmsSign + h + 'H' : '') + 6096 (m ? hmsSign + m + 'M' : '') + 6097 (s ? hmsSign + s + 'S' : ''); 6098} 6099 6100var proto$2 = Duration.prototype; 6101 6102proto$2.isValid = isValid$1; 6103proto$2.abs = abs; 6104proto$2.add = add$1; 6105proto$2.subtract = subtract$1; 6106proto$2.as = as; 6107proto$2.asMilliseconds = asMilliseconds; 6108proto$2.asSeconds = asSeconds; 6109proto$2.asMinutes = asMinutes; 6110proto$2.asHours = asHours; 6111proto$2.asDays = asDays; 6112proto$2.asWeeks = asWeeks; 6113proto$2.asMonths = asMonths; 6114proto$2.asYears = asYears; 6115proto$2.valueOf = valueOf$1; 6116proto$2._bubble = bubble; 6117proto$2.clone = clone$1; 6118proto$2.get = get$2; 6119proto$2.milliseconds = milliseconds; 6120proto$2.seconds = seconds; 6121proto$2.minutes = minutes; 6122proto$2.hours = hours; 6123proto$2.days = days; 6124proto$2.weeks = weeks; 6125proto$2.months = months; 6126proto$2.years = years; 6127proto$2.humanize = humanize; 6128proto$2.toISOString = toISOString$1; 6129proto$2.toString = toISOString$1; 6130proto$2.toJSON = toISOString$1; 6131proto$2.locale = locale; 6132proto$2.localeData = localeData; 6133 6134// Deprecations 6135proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); 6136proto$2.lang = lang; 6137 6138// Side effect imports 6139 6140// FORMATTING 6141 6142addFormatToken('X', 0, 0, 'unix'); 6143addFormatToken('x', 0, 0, 'valueOf'); 6144 6145// PARSING 6146 6147addRegexToken('x', matchSigned);
6148addRegexToken('X', matchTimestamp); 6149addParseToken('X', function (input, array, config) { 6150 config._d = new Date(parseFloat(input, 10) * 1000); 6151}); 6152addParseToken('x', function (input, array, config) { 6153 config._d = new Date(toInt(input)); 6154}); 6155 6156// Side effect imports 6157 6158 6159hooks.version = '2.20.1'; 6160 6161setHookCallback(createLocal); 6162 6163hooks.fn = proto; 6164hooks.min = min; 6165hooks.max = max; 6166hooks.now = now; 6167hooks.utc = createUTC; 6168hooks.unix = createUnix; 6169hooks.months = listMonths; 6170hooks.isDate = isDate; 6171hooks.locale = getSetGlobalLocale; 6172hooks.invalid = createInvalid; 6173hooks.duration = createDuration; 6174hooks.isMoment = isMoment; 6175hooks.weekdays = listWeekdays; 6176hooks.parseZone = createInZone; 6177hooks.localeData = getLocale; 6178hooks.isDuration = isDuration; 6179hooks.monthsShort = listMonthsShort; 6180hooks.weekdaysMin = listWeekdaysMin; 6181hooks.defineLocale = defineLocale; 6182hooks.updateLocale = updateLocale; 6183hooks.locales = listLocales; 6184hooks.weekdaysShort = listWeekdaysShort; 6185hooks.normalizeUnits = normalizeUnits; 6186hooks.relativeTimeRounding = getSetRelativeTimeRounding; 6187hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; 6188hooks.calendarFormat = getCalendarFormat; 6189hooks.prototype = proto; 6190 6191// currently HTML5 input type only supports 24-hour formats 6192hooks.HTML5_FMT = { 6193 DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // <input type="datetime-local" /> 6194 DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // <input type="datetime-local" step="1" /> 6195 DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // <input type="datetime-local" step="0.001" /> 6196 DATE: 'YYYY-MM-DD', // <input type="date" /> 6197 TIME: 'HH:mm', // <input type="time" /> 6198 TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" /> 6199 TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" /> 6200 WEEK: 'YYYY-[W]WW', // <input type="week" /> 6201 MONTH: 'YYYY-MM' // <input type="month" /> 6202}; 6203 6204return hooks; 6205 6206}))); 6207 6208},{}],7:[function(require,module,exports){ 6209/** 6210 * @namespace Chart 6211 */ 6212var Chart = require(29)(); 6213 6214Chart.helpers = require(45); 6215 6216// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! 6217require(27)(Chart); 6218 6219Chart.defaults = require(25); 6220Chart.Element = require(26); 6221Chart.elements = require(40); 6222Chart.Interaction = require(28); 6223Chart.layouts = require(30); 6224Chart.platform = require(48); 6225Chart.plugins = require(31); 6226Chart.Ticks = require(34); 6227 6228require(22)(Chart); 6229require(23)(Chart); 6230require(24)(Chart); 6231require(33)(Chart); 6232require(32)(Chart); 6233require(35)(Chart); 6234 6235require(55)(Chart); 6236require(53)(Chart); 6237require(54)(Chart); 6238require(56)(Chart); 6239require(57)(Chart); 6240require(58)(Chart); 6241 6242// Controllers must be loaded after elements 6243// See Chart.core.datasetController.dataElementType 6244require(15)(Chart); 6245require(16)(Chart); 6246require(17)(Chart); 6247require(18)(Chart); 6248require(19)(Chart); 6249require(20)(Chart); 6250require(21)(Chart); 6251 6252require(8)(Chart); 6253require(9)(Chart); 6254require(10)(Chart); 6255require(11)(Chart); 6256require(12)(Chart); 6257require(13)(Chart); 6258require(14)(Chart); 6259 6260// Loading built-it plugins 6261var plugins = require(49); 6262for (var k in plugins) { 6263 if (plugins.hasOwnProperty(k)) { 6264 Chart.plugins.register(plugins[k]); 6265 } 6266} 6267 6268Chart.platform.initialize(); 6269 6270module.exports = Chart; 6271if (typeof window !== 'undefined') { 6272 window.Chart = Chart; 6273} 6274 6275// DEPRECATIONS 6276 6277/** 6278 * Provided for backward compatibility, not available anymore 6279 * @namespace Chart.Legend 6280 * @deprecated since version 2.1.5 6281 * @todo remove at version 3 6282 * @private 6283 */
6284Chart.Legend = plugins.legend._element; 6285 6286/** 6287 * Provided for backward compatibility, not available anymore 6288 * @namespace Chart.Title 6289 * @deprecated since version 2.1.5 6290 * @todo remove at version 3 6291 * @private 6292 */ 6293Chart.Title = plugins.title._element; 6294 6295/** 6296 * Provided for backward compatibility, use Chart.plugins instead 6297 * @namespace Chart.pluginService 6298 * @deprecated since version 2.1.5 6299 * @todo remove at version 3 6300 * @private 6301 */ 6302Chart.pluginService = Chart.plugins; 6303 6304/** 6305 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no 6306 * effect, instead simply create/register plugins via plain JavaScript objects. 6307 * @interface Chart.PluginBase 6308 * @deprecated since version 2.5.0 6309 * @todo remove at version 3 6310 * @private 6311 */ 6312Chart.PluginBase = Chart.Element.extend({}); 6313 6314/** 6315 * Provided for backward compatibility, use Chart.helpers.canvas instead. 6316 * @namespace Chart.canvasHelpers 6317 * @deprecated since version 2.6.0 6318 * @todo remove at version 3 6319 * @private 6320 */ 6321Chart.canvasHelpers = Chart.helpers.canvas; 6322 6323/** 6324 * Provided for backward compatibility, use Chart.layouts instead. 6325 * @namespace Chart.layoutService 6326 * @deprecated since version 2.8.0 6327 * @todo remove at version 3 6328 * @private 6329 */
6330Chart.layoutService = Chart.layouts; 6331 6332},{"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,"40":40,"45":45,"48":48,"49":49,"53":53,"54":54,"55":55,"56":56,"57":57,"58":58,"8":8,"9":9}],8:[function(require,module,exports){ 6333'use strict'; 6334 6335module.exports = function(Chart) { 6336 6337 Chart.Bar = function(context, config) { 6338 config.type = 'bar'; 6339 6340 return new Chart(context, config); 6341 }; 6342 6343}; 6344 6345},{}],9:[function(require,module,exports){ 6346'use strict'; 6347 6348module.exports = function(Chart) { 6349 6350 Chart.Bubble = function(context, config) { 6351 config.type = 'bubble'; 6352 return new Chart(context, config); 6353 }; 6354 6355}; 6356 6357},{}],10:[function(require,module,exports){ 6358'use strict'; 6359 6360module.exports = function(Chart) { 6361 6362 Chart.Doughnut = function(context, config) { 6363 config.type = 'doughnut'; 6364 6365 return new Chart(context, config); 6366 }; 6367 6368}; 6369 6370},{}],11:[function(require,module,exports){ 6371'use strict'; 6372 6373module.exports = function(Chart) { 6374 6375 Chart.Line = function(context, config) { 6376 config.type = 'line'; 6377 6378 return new Chart(context, config); 6379 }; 6380 6381}; 6382 6383},{}],12:[function(require,module,exports){ 6384'use strict'; 6385 6386module.exports = function(Chart) { 6387 6388 Chart.PolarArea = function(context, config) { 6389 config.type = 'polarArea'; 6390 6391 return new Chart(context, config); 6392 }; 6393 6394}; 6395 6396},{}],13:[function(require,module,exports){ 6397'use strict'; 6398 6399module.exports = function(Chart) { 6400 6401 Chart.Radar = function(context, config) { 6402 config.type = 'radar'; 6403 6404 return new Chart(context, config); 6405 }; 6406 6407}; 6408 6409},{}],14:[function(require,module,exports){ 6410'use strict'; 6411 6412module.exports = function(Chart) { 6413 Chart.Scatter = function(context, config) { 6414 config.type = 'scatter'; 6415 return new Chart(context, config); 6416 }; 6417}; 6418 6419},{}],15:[function(require,module,exports){ 6420'use strict'; 6421 6422var defaults = require(25); 6423var elements = require(40); 6424var helpers = require(45); 6425 6426defaults._set('bar', { 6427 hover: { 6428 mode: 'label' 6429 }, 6430 6431 scales: { 6432 xAxes: [{ 6433 type: 'category', 6434 6435 // Specific to Bar Controller 6436 categoryPercentage: 0.8, 6437 barPercentage: 0.9, 6438 6439 // offset settings 6440 offset: true, 6441 6442 // grid line settings 6443 gridLines: { 6444 offsetGridLines: true 6445 } 6446 }], 6447 6448 yAxes: [{ 6449 type: 'linear' 6450 }] 6451 } 6452}); 6453 6454defaults._set('horizontalBar', { 6455 hover: { 6456 mode: 'index', 6457 axis: 'y' 6458 }, 6459 6460 scales: { 6461 xAxes: [{ 6462 type: 'linear', 6463 position: 'bottom' 6464 }], 6465 6466 yAxes: [{ 6467 position: 'left', 6468 type: 'category', 6469 6470 // Specific to Horizontal Bar Controller 6471 categoryPercentage: 0.8, 6472 barPercentage: 0.9, 6473 6474 // offset settings 6475 offset: true, 6476 6477 // grid line settings 6478 gridLines: { 6479 offsetGridLines: true 6480 } 6481 }] 6482 }, 6483 6484 elements: { 6485 rectangle: { 6486 borderSkipped: 'left' 6487 } 6488 }, 6489 6490 tooltips: { 6491 callbacks: { 6492 title: function(item, data) { 6493 // Pick first xLabel for now 6494 var title = ''; 6495 6496 if (item.length > 0) { 6497 if (item[0].yLabel) { 6498 title = item[0].yLabel; 6499 } else if (data.labels.length > 0 && item[0].index < data.labels.length) { 6500 title = data.labels[item[0].index]; 6501 } 6502 } 6503 6504 return title; 6505 }, 6506 6507 label: function(item, data) { 6508 var datasetLabel = data.datasets[item.datasetIndex].label || ''; 6509 return datasetLabel + ': ' + item.xLabel; 6510 } 6511 }, 6512 mode: 'index', 6513 axis: 'y' 6514 } 6515}); 6516 6517/** 6518 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. 6519 * @private 6520 */ 6521function computeMinSampleSize(scale, pixels) { 6522 var min = scale.isHorizontal() ? scale.width : scale.height; 6523 var ticks = scale.getTicks(); 6524 var prev, curr, i, ilen; 6525 6526 for (i = 1, ilen = pixels.length; i < ilen; ++i) { 6527 min = Math.min(min, pixels[i] - pixels[i - 1]); 6528 } 6529 6530 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 6531 curr = scale.getPixelForTick(i); 6532 min = i > 0 ? Math.min(min, curr - prev) : min; 6533 prev = curr; 6534 } 6535 6536 return min; 6537} 6538 6539/** 6540 * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, 6541 * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This 6542 * mode currently always generates bars equally sized (until we introduce scriptable options?). 6543 * @private 6544 */ 6545function computeFitCategoryTraits(index, ruler, options) { 6546 var thickness = options.barThickness; 6547 var count = ruler.stackCount; 6548 var curr = ruler.pixels[index]; 6549 var size, ratio; 6550 6551 if (helpers.isNullOrUndef(thickness)) { 6552 size = ruler.min * options.categoryPercentage; 6553 ratio = options.barPercentage; 6554 } else { 6555 // When bar thickness is enforced, category and bar percentages are ignored. 6556 // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') 6557 // and deprecate barPercentage since this value is ignored when thickness is absolute. 6558 size = thickness * count; 6559 ratio = 1; 6560 } 6561 6562 return { 6563 chunk: size / count, 6564 ratio: ratio, 6565 start: curr - (size / 2) 6566 }; 6567} 6568 6569/** 6570 * Computes an "optimal" category that globally arranges bars side by side (no gap when 6571 * percentage options are 1), based on the previous and following categories. This mode 6572 * generates bars with different widths when data are not evenly spaced. 6573 * @private 6574 */ 6575function computeFlexCategoryTraits(index, ruler, options) { 6576 var pixels = ruler.pixels; 6577 var curr = pixels[index]; 6578 var prev = index > 0 ? pixels[index - 1] : null; 6579 var next = index < pixels.length - 1 ? pixels[index + 1] : null; 6580 var percent = options.categoryPercentage; 6581 var start, size; 6582 6583 if (prev === null) { 6584 // first data: its size is double based on the next point or, 6585 // if it's also the last data, we use the scale end extremity. 6586 prev = curr - (next === null ? ruler.end - curr : next - curr); 6587 } 6588 6589 if (next === null) { 6590 // last data: its size is also double based on the previous point. 6591 next = curr + curr - prev; 6592 } 6593 6594 start = curr - ((curr - prev) / 2) * percent; 6595 size = ((next - prev) / 2) * percent; 6596 6597 return { 6598 chunk: size / ruler.stackCount, 6599 ratio: options.barPercentage, 6600 start: start 6601 }; 6602} 6603 6604module.exports = function(Chart) { 6605 6606 Chart.controllers.bar = Chart.DatasetController.extend({ 6607 6608 dataElementType: elements.Rectangle, 6609 6610 initialize: function() { 6611 var me = this; 6612 var meta; 6613 6614 Chart.DatasetController.prototype.initialize.apply(me, arguments); 6615 6616 meta = me.getMeta(); 6617 meta.stack = me.getDataset().stack; 6618 meta.bar = true; 6619 }, 6620 6621 update: function(reset) { 6622 var me = this; 6623 var rects = me.getMeta().data; 6624 var i, ilen; 6625 6626 me._ruler = me.getRuler(); 6627 6628 for (i = 0, ilen = rects.length; i < ilen; ++i) { 6629 me.updateElement(rects[i], i, reset); 6630 } 6631 }, 6632 6633 updateElement: function(rectangle, index, reset) { 6634 var me = this; 6635 var chart = me.chart; 6636 var meta = me.getMeta(); 6637 var dataset = me.getDataset(); 6638 var custom = rectangle.custom || {}; 6639 var rectangleOptions = chart.options.elements.rectangle; 6640 6641 rectangle._xScale = me.getScaleForId(meta.xAxisID); 6642 rectangle._yScale = me.getScaleForId(meta.yAxisID); 6643 rectangle._datasetIndex = me.index; 6644 rectangle._index = index; 6645 6646 rectangle._model = { 6647 datasetLabel: dataset.label, 6648 label: chart.data.labels[index], 6649 borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, 6650 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), 6651 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), 6652 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) 6653 }; 6654 6655 me.updateElementGeometry(rectangle, index, reset); 6656 6657 rectangle.pivot(); 6658 }, 6659 6660 /** 6661 * @private 6662 */ 6663 updateElementGeometry: function(rectangle, index, reset) { 6664 var me = this; 6665 var model = rectangle._model; 6666 var vscale = me.getValueScale(); 6667 var base = vscale.getBasePixel(); 6668 var horizontal = vscale.isHorizontal(); 6669 var ruler = me._ruler || me.getRuler(); 6670 var vpixels = me.calculateBarValuePixels(me.index, index); 6671 var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); 6672 6673 model.horizontal = horizontal; 6674 model.base = reset ? base : vpixels.base; 6675 model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; 6676 model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; 6677 model.height = horizontal ? ipixels.size : undefined; 6678 model.width = horizontal ? undefined : ipixels.size; 6679 }, 6680 6681 /** 6682 * @private 6683 */ 6684 getValueScaleId: function() { 6685 return this.getMeta().yAxisID; 6686 }, 6687 6688 /** 6689 * @private 6690 */ 6691 getIndexScaleId: function() { 6692 return this.getMeta().xAxisID; 6693 }, 6694 6695 /** 6696 * @private 6697 */ 6698 getValueScale: function() {
6699 return this.getScaleForId(this.getValueScaleId()); 6700 }, 6701 6702 /** 6703 * @private 6704 */ 6705 getIndexScale: function() { 6706 return this.getScaleForId(this.getIndexScaleId()); 6707 }, 6708 6709 /** 6710 * Returns the stacks based on groups and bar visibility. 6711 * @param {Number} [last] - The dataset index 6712 * @returns {Array} The stack list 6713 * @private 6714 */ 6715 _getStacks: function(last) { 6716 var me = this; 6717 var chart = me.chart; 6718 var scale = me.getIndexScale(); 6719 var stacked = scale.options.stacked; 6720 var ilen = last === undefined ? chart.data.datasets.length : last + 1; 6721 var stacks = []; 6722 var i, meta; 6723 6724 for (i = 0; i < ilen; ++i) { 6725 meta = chart.getDatasetMeta(i); 6726 if (meta.bar && chart.isDatasetVisible(i) && 6727 (stacked === false || 6728 (stacked === true && stacks.indexOf(meta.stack) === -1) || 6729 (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { 6730 stacks.push(meta.stack); 6731 } 6732 } 6733 6734 return stacks; 6735 }, 6736 6737 /** 6738 * Returns the effective number of stacks based on groups and bar visibility. 6739 * @private 6740 */ 6741 getStackCount: function() { 6742 return this._getStacks().length; 6743 }, 6744 6745 /** 6746 * Returns the stack index for the given dataset based on groups and bar visibility. 6747 * @param {Number} [datasetIndex] - The dataset index 6748 * @param {String} [name] - The stack name to find 6749 * @returns {Number} The stack index 6750 * @private 6751 */ 6752 getStackIndex: function(datasetIndex, name) { 6753 var stacks = this._getStacks(datasetIndex); 6754 var index = (name !== undefined) 6755 ? stacks.indexOf(name) 6756 : -1; // indexOf returns -1 if element is not present 6757 6758 return (index === -1) 6759 ? stacks.length - 1 6760 : index; 6761 }, 6762 6763 /** 6764 * @private 6765 */ 6766 getRuler: function() { 6767 var me = this; 6768 var scale = me.getIndexScale(); 6769 var stackCount = me.getStackCount(); 6770 var datasetIndex = me.index; 6771 var isHorizontal = scale.isHorizontal(); 6772 var start = isHorizontal ? scale.left : scale.top; 6773 var end = start + (isHorizontal ? scale.width : scale.height); 6774 var pixels = []; 6775 var i, ilen, min; 6776 6777 for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { 6778 pixels.push(scale.getPixelForValue(null, i, datasetIndex)); 6779 } 6780 6781 min = helpers.isNullOrUndef(scale.options.barThickness) 6782 ? computeMinSampleSize(scale, pixels) 6783 : -1; 6784 6785 return { 6786 min: min, 6787 pixels: pixels, 6788 start: start, 6789 end: end, 6790 stackCount: stackCount, 6791 scale: scale 6792 }; 6793 }, 6794 6795 /** 6796 * Note: pixel values are not clamped to the scale area. 6797 * @private 6798 */ 6799 calculateBarValuePixels: function(datasetIndex, index) { 6800 var me = this; 6801 var chart = me.chart; 6802 var meta = me.getMeta(); 6803 var scale = me.getValueScale(); 6804 var datasets = chart.data.datasets; 6805 var value = scale.getRightValue(datasets[datasetIndex].data[index]); 6806 var stacked = scale.options.stacked; 6807 var stack = meta.stack; 6808 var start = 0; 6809 var i, imeta, ivalue, base, head, size; 6810 6811 if (stacked || (stacked === undefined && stack !== undefined)) { 6812 for (i = 0; i < datasetIndex; ++i) { 6813 imeta = chart.getDatasetMeta(i); 6814 6815 if (imeta.bar && 6816 imeta.stack === stack && 6817 imeta.controller.getValueScaleId() === scale.id && 6818 chart.isDatasetVisible(i)) { 6819 6820 ivalue = scale.getRightValue(datasets[i].data[index]); 6821 if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { 6822 start += ivalue; 6823 } 6824 } 6825 } 6826 } 6827 6828 base = scale.getPixelForValue(start); 6829 head = scale.getPixelForValue(start + value); 6830 size = (head - base) / 2; 6831 6832 return { 6833 size: size, 6834 base: base, 6835 head: head, 6836 center: head + size / 2 6837 }; 6838 }, 6839 6840 /** 6841 * @private 6842 */ 6843 calculateBarIndexPixels: function(datasetIndex, index, ruler) { 6844 var me = this; 6845 var options = ruler.scale.options; 6846 var range = options.barThickness === 'flex' 6847 ? computeFlexCategoryTraits(index, ruler, options) 6848 : computeFitCategoryTraits(index, ruler, options); 6849 6850 var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); 6851 var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); 6852 var size = Math.min( 6853 helpers.valueOrDefault(options.maxBarThickness, Infinity), 6854 range.chunk * range.ratio); 6855 6856 return { 6857 base: center - size / 2, 6858 head: center + size / 2, 6859 center: center, 6860 size: size 6861 }; 6862 }, 6863 6864 draw: function() { 6865 var me = this; 6866 var chart = me.chart; 6867 var scale = me.getValueScale(); 6868 var rects = me.getMeta().data; 6869 var dataset = me.getDataset(); 6870 var ilen = rects.length; 6871 var i = 0; 6872 6873 helpers.canvas.clipArea(chart.ctx, chart.chartArea); 6874 6875 for (; i < ilen; ++i) { 6876 if (!isNaN(scale.getRightValue(dataset.data[i]))) { 6877 rects[i].draw(); 6878 } 6879 } 6880 6881 helpers.canvas.unclipArea(chart.ctx); 6882 }, 6883 6884 setHoverStyle: function(rectangle) { 6885 var dataset = this.chart.data.datasets[rectangle._datasetIndex]; 6886 var index = rectangle._index; 6887 var custom = rectangle.custom || {}; 6888 var model = rectangle._model; 6889
vendor: 4,408 bytes, lines 6890-7031
6890 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 6891 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 6892 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); 6893 }, 6894 6895 removeHoverStyle: function(rectangle) { 6896 var dataset = this.chart.data.datasets[rectangle._datasetIndex]; 6897 var index = rectangle._index; 6898 var custom = rectangle.custom || {}; 6899 var model = rectangle._model; 6900 var rectangleElementOptions = this.chart.options.elements.rectangle; 6901 6902 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); 6903 model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); 6904 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); 6905 } 6906 }); 6907 6908 Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ 6909 /** 6910 * @private 6911 */ 6912 getValueScaleId: function() { 6913 return this.getMeta().xAxisID; 6914 }, 6915 6916 /** 6917 * @private 6918 */ 6919 getIndexScaleId: function() { 6920 return this.getMeta().yAxisID; 6921 } 6922 }); 6923}; 6924 6925},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ 6926'use strict'; 6927 6928var defaults = require(25); 6929var elements = require(40); 6930var helpers = require(45); 6931 6932defaults._set('bubble', { 6933 hover: { 6934 mode: 'single' 6935 }, 6936 6937 scales: { 6938 xAxes: [{ 6939 type: 'linear', // bubble should probably use a linear scale by default 6940 position: 'bottom', 6941 id: 'x-axis-0' // need an ID so datasets can reference the scale 6942 }], 6943 yAxes: [{ 6944 type: 'linear', 6945 position: 'left', 6946 id: 'y-axis-0' 6947 }] 6948 }, 6949 6950 tooltips: { 6951 callbacks: { 6952 title: function() { 6953 // Title doesn't make sense for scatter since we format the data as a point 6954 return ''; 6955 }, 6956 label: function(item, data) { 6957 var datasetLabel = data.datasets[item.datasetIndex].label || ''; 6958 var dataPoint = data.datasets[item.datasetIndex].data[item.index]; 6959 return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; 6960 } 6961 } 6962 } 6963}); 6964 6965 6966module.exports = function(Chart) { 6967 6968 Chart.controllers.bubble = Chart.DatasetController.extend({ 6969 /** 6970 * @protected 6971 */ 6972 dataElementType: elements.Point, 6973 6974 /** 6975 * @protected 6976 */ 6977 update: function(reset) { 6978 var me = this; 6979 var meta = me.getMeta(); 6980 var points = meta.data; 6981 6982 // Update Points 6983 helpers.each(points, function(point, index) { 6984 me.updateElement(point, index, reset); 6985 }); 6986 }, 6987 6988 /** 6989 * @protected 6990 */ 6991 updateElement: function(point, index, reset) { 6992 var me = this; 6993 var meta = me.getMeta(); 6994 var custom = point.custom || {}; 6995 var xScale = me.getScaleForId(meta.xAxisID); 6996 var yScale = me.getScaleForId(meta.yAxisID); 6997 var options = me._resolveElementOptions(point, index); 6998 var data = me.getDataset().data[index]; 6999 var dsIndex = me.index; 7000 7001 var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); 7002 var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); 7003 7004 point._xScale = xScale; 7005 point._yScale = yScale; 7006 point._options = options; 7007 point._datasetIndex = dsIndex; 7008 point._index = index; 7009 point._model = { 7010 backgroundColor: options.backgroundColor, 7011 borderColor: options.borderColor, 7012 borderWidth: options.borderWidth, 7013 hitRadius: options.hitRadius, 7014 pointStyle: options.pointStyle, 7015 radius: reset ? 0 : options.radius, 7016 skip: custom.skip || isNaN(x) || isNaN(y), 7017 x: x, 7018 y: y, 7019 }; 7020 7021 point.pivot(); 7022 }, 7023 7024 /** 7025 * @protected 7026 */ 7027 setHoverStyle: function(point) { 7028 var model = point._model; 7029 var options = point._options; 7030 7031 model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroun
7031dColor, helpers.getHoverColor(options.backgroundColor)); 7032 model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); 7033 model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); 7034 model.radius = options.radius + options.hoverRadius; 7035 }, 7036 7037 /** 7038 * @protected 7039 */ 7040 removeHoverStyle: function(point) { 7041 var model = point._model; 7042 var options = point._options; 7043 7044 model.backgroundColor = options.backgroundColor; 7045 model.borderColor = options.borderColor; 7046 model.borderWidth = options.borderWidth; 7047 model.radius = options.radius; 7048 }, 7049 7050 /** 7051 * @private 7052 */ 7053 _resolveElementOptions: function(point, index) { 7054 var me = this; 7055 var chart = me.chart; 7056 var datasets = chart.data.datasets; 7057 var dataset = datasets[me.index]; 7058 var custom = point.custom || {}; 7059 var options = chart.options.elements.point; 7060 var resolve = helpers.options.resolve; 7061 var data = dataset.data[index]; 7062 var values = {}; 7063 var i, ilen, key; 7064 7065 // Scriptable options 7066 var context = { 7067 chart: chart, 7068 dataIndex: index, 7069 dataset: dataset, 7070 datasetIndex: me.index 7071 }; 7072 7073 var keys = [ 7074 'backgroundColor', 7075 'borderColor', 7076 'borderWidth', 7077 'hoverBackgroundColor', 7078 'hoverBorderColor', 7079 'hoverBorderWidth', 7080 'hoverRadius', 7081 'hitRadius', 7082 'pointStyle' 7083 ]; 7084 7085 for (i = 0, ilen = keys.length; i < ilen; ++i) { 7086 key = keys[i]; 7087 values[key] = resolve([ 7088 custom[key], 7089 dataset[key], 7090 options[key] 7091 ], context, index); 7092 } 7093 7094 // Custom radius resolution 7095 values.radius = resolve([ 7096 custom.radius, 7097 data ? data.r : undefined, 7098 dataset.radius, 7099 options.radius 7100 ], context, index); 7101 7102 return values; 7103 } 7104 }); 7105}; 7106 7107},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ 7108'use strict'; 7109 7110var defaults = require(25); 7111var elements = require(40); 7112var helpers = require(45); 7113 7114defaults._set('doughnut', { 7115 animation: { 7116 // Boolean - Whether we animate the rotation of the Doughnut 7117 animateRotate: true, 7118 // Boolean - Whether we animate scaling the Doughnut from the centre 7119 animateScale: false 7120 }, 7121 hover: { 7122 mode: 'single' 7123 }, 7124 legendCallback: function(chart) { 7125 var text = []; 7126 text.push('<ul class="' + chart.id + '-legend">'); 7127 7128 var data = chart.data; 7129 var datasets = data.datasets; 7130 var labels = data.labels; 7131 7132 if (datasets.length) { 7133 for (var i = 0; i < datasets[0].data.length; ++i) { 7134 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); 7135 if (labels[i]) { 7136 text.push(labels[i]); 7137 } 7138 text.push('</li>'); 7139 } 7140 } 7141 7142 text.push('</ul>'); 7143 return text.join(''); 7144 }, 7145 legend: { 7146 labels: { 7147 generateLabels: function(chart) { 7148 var data = chart.data; 7149 if (data.labels.length && data.datasets.length) { 7150 return data.labels.map(function(label, i) { 7151 var meta = chart.getDatasetMeta(0); 7152 var ds = data.datasets[0]; 7153 var arc = meta.data[i]; 7154 var custom = arc && arc.custom || {}; 7155 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7156 var arcOpts = chart.options.elements.arc; 7157 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); 7158 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); 7159 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); 7160 7161 return { 7162 text: label, 7163 fillStyle: fill, 7164 strokeStyle: stroke, 7165 lineWidth: bw, 7166 hidden: isNaN(ds.data[i]) || meta.data[i].hidden, 7167 7168 // Extra data used for toggling the correct item 7169 index: i 7170 }; 7171 }); 7172 } 7173 return []; 7174 } 7175 }, 7176 7177 onClick: function(e, legendItem) { 7178 var index = legendItem.index; 7179 var chart = this.chart; 7180 var i, ilen, meta; 7181 7182 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 7183 meta = chart.getDatasetMeta(i); 7184 // toggle visibility of index if exists 7185 if (meta.data[index]) { 7186 meta.data[index].hidden = !meta.data[index].hidden; 7187 } 7188 } 7189
7190 chart.update(); 7191 } 7192 }, 7193 7194 // The percentage of the chart that we cut out of the middle. 7195 cutoutPercentage: 50, 7196 7197 // The rotation of the chart, where the first data arc begins. 7198 rotation: Math.PI * -0.5, 7199 7200 // The total circumference of the chart. 7201 circumference: Math.PI * 2.0, 7202 7203 // Need to override these to give a nice default 7204 tooltips: { 7205 callbacks: { 7206 title: function() { 7207 return ''; 7208 }, 7209 label: function(tooltipItem, data) { 7210 var dataLabel = data.labels[tooltipItem.index]; 7211 var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; 7212 7213 if (helpers.isArray(dataLabel)) { 7214 // show value on first line of multiline label 7215 // need to clone because we are changing the value 7216 dataLabel = dataLabel.slice(); 7217 dataLabel[0] += value; 7218 } else { 7219 dataLabel += value; 7220 } 7221 7222 return dataLabel; 7223 } 7224 } 7225 } 7226}); 7227 7228defaults._set('pie', helpers.clone(defaults.doughnut)); 7229defaults._set('pie', { 7230 cutoutPercentage: 0 7231}); 7232 7233module.exports = function(Chart) { 7234 7235 Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ 7236 7237 dataElementType: elements.Arc, 7238 7239 linkScales: helpers.noop, 7240 7241 // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly 7242 getRingIndex: function(datasetIndex) { 7243 var ringIndex = 0; 7244 7245 for (var j = 0; j < datasetIndex; ++j) { 7246 if (this.chart.isDatasetVisible(j)) { 7247 ++ringIndex; 7248 } 7249 } 7250 7251 return ringIndex; 7252 }, 7253 7254 update: function(reset) { 7255 var me = this; 7256 var chart = me.chart; 7257 var chartArea = chart.chartArea; 7258 var opts = chart.options; 7259 var arcOpts = opts.elements.arc; 7260 var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; 7261 var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; 7262 var minSize = Math.min(availableWidth, availableHeight); 7263 var offset = {x: 0, y: 0}; 7264 var meta = me.getMeta(); 7265 var cutoutPercentage = opts.cutoutPercentage; 7266 var circumference = opts.circumference; 7267 7268 // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc 7269 if (circumference < Math.PI * 2.0) { 7270 var startAngle = opts.rotation % (Math.PI * 2.0); 7271 startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); 7272 var endAngle = startAngle + circumference; 7273 var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; 7274 var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; 7275 var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); 7276 var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); 7277 var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); 7278 var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); 7279 var cutout = cutoutPercentage / 100.0; 7280 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))}; 7281 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))}; 7282 var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; 7283 minSize = Math.min(availableWidth / size.width, availableHeight / size.height); 7284 offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; 7285 } 7286 7287 chart.borderWidth = me.getMaxBorderWidth(meta.data); 7288 chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); 7289 chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
7290 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); 7291 chart.offsetX = offset.x * chart.outerRadius; 7292 chart.offsetY = offset.y * chart.outerRadius; 7293 7294 meta.total = me.calculateTotal(); 7295 7296 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); 7297 me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); 7298 7299 helpers.each(meta.data, function(arc, index) { 7300 me.updateElement(arc, index, reset); 7301 }); 7302 }, 7303 7304 updateElement: function(arc, index, reset) { 7305 var me = this; 7306 var chart = me.chart; 7307 var chartArea = chart.chartArea; 7308 var opts = chart.options; 7309 var animationOpts = opts.animation; 7310 var centerX = (chartArea.left + chartArea.right) / 2; 7311 var centerY = (chartArea.top + chartArea.bottom) / 2; 7312 var startAngle = opts.rotation; // non reset case handled later 7313 var endAngle = opts.rotation; // non reset case handled later 7314 var dataset = me.getDataset(); 7315 var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); 7316 var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; 7317 var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; 7318 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7319 7320 helpers.extend(arc, { 7321 // Utility 7322 _datasetIndex: me.index, 7323 _index: index, 7324 7325 // Desired view properties 7326 _model: { 7327 x: centerX + chart.offsetX, 7328 y: centerY + chart.offsetY, 7329 startAngle: startAngle, 7330 endAngle: endAngle, 7331 circumference: circumference, 7332 outerRadius: outerRadius, 7333 innerRadius: innerRadius, 7334 label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) 7335 } 7336 }); 7337 7338 var model = arc._model; 7339 // Resets the visual styles 7340 this.removeHoverStyle(arc); 7341 7342 // Set correct angles if not resetting 7343 if (!reset || !animationOpts.animateRotate) { 7344 if (index === 0) { 7345 model.startAngle = opts.rotation; 7346 } else { 7347 model.startAngle = me.getMeta().data[index - 1]._model.endAngle; 7348 } 7349 7350 model.endAngle = model.startAngle + model.circumference; 7351 } 7352 7353 arc.pivot(); 7354 }, 7355 7356 removeHoverStyle: function(arc) { 7357 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); 7358 }, 7359 7360 calculateTotal: function() { 7361 var dataset = this.getDataset(); 7362 var meta = this.getMeta(); 7363 var total = 0; 7364 var value; 7365 7366 helpers.each(meta.data, function(element, index) { 7367 value = dataset.data[index]; 7368 if (!isNaN(value) && !element.hidden) { 7369 total += Math.abs(value); 7370 } 7371 }); 7372 7373 /* if (total === 0) { 7374 total = NaN; 7375 }*/ 7376 7377 return total; 7378 }, 7379 7380 calculateCircumference: function(value) { 7381 var total = this.getMeta().total; 7382 if (total > 0 && !isNaN(value)) { 7383 return (Math.PI * 2.0) * (Math.abs(value) / total); 7384 } 7385 return 0; 7386 }, 7387 7388 // gets the max border or hover width to properly scale pie charts 7389 getMaxBorderWidth: function(arcs) { 7390 var max = 0; 7391 var index = this.index; 7392 var length = arcs.length; 7393 var borderWidth; 7394 var hoverWidth; 7395 7396 for (var i = 0; i < length; i++) { 7397 borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; 7398 hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; 7399 7400 max = borderWidth > max ? borderWidth : max; 7401 max = hoverWidth > max ? hoverWidth : max; 7402 } 7403 return max; 7404 } 7405 }); 7406}; 7407 7408},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ 7409'use strict'; 7410 7411var defaults = require(25); 7412var elements = require(40); 7413var helpers = require(45); 7414 7415defaults._set('line', { 7416 showLines: true, 7417 spanGaps: false, 7418 7419 hover: { 7420 mode: 'label' 7421 }, 7422 7423 scales: { 7424 xAxes: [{ 7425 type: 'category', 7426 id: 'x-axis-0' 7427 }], 7428 yAxes: [{ 7429 type: 'linear', 7430 id: 'y-axis-0' 7431 }] 7432 } 7433}); 7434 7435module.exports = function(Chart) { 7436 7437 function lineEnabled(dataset, options) { 7438 return helpers.valueOrDefault(dataset.showLine, options.showLines); 7439 } 7440 7441 Chart.controllers.line = Chart.DatasetController.extend({ 7442 7443 datasetElementType: elements.Line, 7444 7445 dataElementType: elements.Point, 7446 7447 update: function(reset) { 7448 var me = this; 7449 var meta = me.getMeta(); 7450 var line = meta.dataset; 7451 var points = meta.data || []; 7452 var options = me.chart.options; 7453 var lineElementOptions = options.elements.line; 7454 var scale = me.getScaleForId(meta.yAxisID); 7455 var i, ilen, custom; 7456 var dataset = me.getDataset(); 7457 var showLine = lineEnabled(dataset, options); 7458 7459 // Update Line 7460 if (showLine) { 7461 custom = line.custom || {}; 7462 7463 // Compatibility: If the properties are defined with only the old name, use those values 7464 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { 7465 dataset.lineTension = dataset.tension; 7466 } 7467 7468 // Utility 7469 line._scale = scale; 7470 line._datasetIndex = me.index; 7471 // Data 7472 line._children = points; 7473 // Model 7474 line._model = { 7475 // Appearance 7476 // The default behavior of lines is to break at null values, according 7477 // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 7478 // This option gives lines the ability to span gaps 7479 spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, 7480 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), 7481 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), 7482 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), 7483 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), 7484 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), 7485 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), 7486 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), 7487 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
7488 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), 7489 steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), 7490 cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), 7491 }; 7492 7493 line.pivot(); 7494 } 7495 7496 // Update Points 7497 for (i = 0, ilen = points.length; i < ilen; ++i) { 7498 me.updateElement(points[i], i, reset); 7499 } 7500 7501 if (showLine && line._model.tension !== 0) { 7502 me.updateBezierControlPoints(); 7503 } 7504 7505 // Now pivot the point for animation 7506 for (i = 0, ilen = points.length; i < ilen; ++i) { 7507 points[i].pivot(); 7508 } 7509 }, 7510 7511 getPointBackgroundColor: function(point, index) { 7512 var backgroundColor = this.chart.options.elements.point.backgroundColor; 7513 var dataset = this.getDataset(); 7514 var custom = point.custom || {}; 7515 7516 if (custom.backgroundColor) { 7517 backgroundColor = custom.backgroundColor; 7518 }
7518 else if (dataset.pointBackgroundColor) { 7519 backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); 7520 } else if (dataset.backgroundColor) { 7521 backgroundColor = dataset.backgroundColor; 7522 } 7523 7524 return backgroundColor; 7525 }, 7526 7527 getPointBorderColor: function(point, index) { 7528 var borderColor = this.chart.options.elements.point.borderColor; 7529 var dataset = this.getDataset(); 7530 var custom = point.custom || {}; 7531 7532 if (custom.borderColor) { 7533 borderColor = custom.borderColor; 7534 } else if (dataset.pointBorderColor) { 7535 borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); 7536 } else if (dataset.borderColor) { 7537 borderColor = dataset.borderColor; 7538 } 7539 7540 return borderColor; 7541 }, 7542 7543 getPointBorderWidth: function(point, index) { 7544 var borderWidth = this.chart.options.elements.point.borderWidth; 7545 var dataset = this.getDataset(); 7546 var custom = point.custom || {}; 7547 7548 if (!isNaN(custom.borderWidth)) { 7549 borderWidth = custom.borderWidth; 7550 } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { 7551 borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); 7552 } else if (!isNaN(dataset.borderWidth)) { 7553 borderWidth = dataset.borderWidth; 7554 } 7555 7556 return borderWidth; 7557 }, 7558 7559 updateElement: function(point, index, reset) { 7560 var me = this; 7561 var meta = me.getMeta(); 7562 var custom = point.custom || {}; 7563 var dataset = me.getDataset(); 7564 var datasetIndex = me.index; 7565 var value = dataset.data[index]; 7566 var yScale = me.getScaleForId(meta.yAxisID); 7567 var xScale = me.getScaleForId(meta.xAxisID); 7568 var pointOptions = me.chart.options.elements.point; 7569 var x, y; 7570 7571 // Compatibility: If the properties are defined with only the old name, use those values 7572 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 7573 dataset.pointRadius = dataset.radius; 7574 } 7575 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { 7576 dataset.pointHitRadius = dataset.hitRadius; 7577 } 7578 7579 x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); 7580 y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); 7581 7582 // Utility 7583 point._xScale = xScale; 7584 point._yScale = yScale; 7585 point._datasetIndex = datasetIndex; 7586 point._index = index; 7587 7588 // Desired view properties 7589 point._model = { 7590 x: x, 7591 y: y, 7592 skip: custom.skip || isNaN(x) || isNaN(y), 7593 // Appearance 7594 radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), 7595 pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
7596 backgroundColor: me.getPointBackgroundColor(point, index), 7597 borderColor: me.getPointBorderColor(point, index), 7598 borderWidth: me.getPointBorderWidth(point, index), 7599 tension: meta.dataset._model ? meta.dataset._model.tension : 0, 7600 steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, 7601 // Tooltip 7602 hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) 7603 }; 7604 }, 7605 7606 calculatePointY: function(value, index, datasetIndex) { 7607 var me = this; 7608 var chart = me.chart; 7609 var meta = me.getMeta(); 7610 var yScale = me.getScaleForId(meta.yAxisID); 7611 var sumPos = 0; 7612 var sumNeg = 0; 7613 var i, ds, dsMeta; 7614 7615 if (yScale.options.stacked) { 7616 for (i = 0; i < datasetIndex; i++) { 7617 ds = chart.data.datasets[i]; 7618 dsMeta = chart.getDatasetMeta(i); 7619 if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { 7620 var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); 7621 if (stackedRightValue < 0) { 7622 sumNeg += stackedRightValue || 0; 7623 } else { 7624 sumPos += stackedRightValue || 0; 7625 } 7626 } 7627 } 7628 7629 var rightValue = Number(yScale.getRightValue(value)); 7630 if (rightValue < 0) { 7631 return yScale.getPixelForValue(sumNeg + rightValue); 7632 } 7633 return yScale.getPixelForValue(sumPos + rightValue); 7634 } 7635 7636 return yScale.getPixelForValue(value); 7637 }, 7638 7639 updateBezierControlPoints: function() { 7640 var me = this; 7641 var meta = me.getMeta(); 7642 var area = me.chart.chartArea; 7643 var points = (meta.data || []); 7644 var i, ilen, point, model, controlPoints; 7645 7646 // Only consider points that are drawn in case the spanGaps option is used 7647 if (meta.dataset._model.spanGaps) { 7648 points = points.filter(function(pt) { 7649 return !pt._model.skip; 7650 }); 7651 } 7652 7653 function capControlPoint(pt, min, max) { 7654 return Math.max(Math.min(pt, max), min); 7655 } 7656 7657 if (meta.dataset._model.cubicInterpolationMode === 'monotone') { 7658 helpers.splineCurveMonotone(points); 7659 } else { 7660 for (i = 0, ilen = points.length; i < ilen; ++i) { 7661 point = points[i]; 7662 model = point._model; 7663 controlPoints = helpers.splineCurve( 7664 helpers.previousItem(points, i)._model, 7665 model, 7666 helpers.nextItem(points, i)._model, 7667 meta.dataset._model.tension 7668 ); 7669 model.controlPointPreviousX = controlPoints.previous.x; 7670 model.controlPointPreviousY = controlPoints.previous.y; 7671 model.controlPointNextX = controlPoints.next.x; 7672 model.controlPointNextY = controlPoints.next.y; 7673 } 7674 } 7675 7676 if (me.chart.options.elements.line.capBezierPoints) { 7677 for (i = 0, ilen = points.length; i < ilen; ++i) { 7678 model = points[i]._model; 7679 model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); 7680 model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); 7681 model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); 7682 model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); 7683 } 7684 } 7685 }, 7686 7687 draw: function() { 7688 var me = this; 7689 var chart = me.chart; 7690 var meta = me.getMeta(); 7691 var points = meta.data || []; 7692 var area = chart.chartArea; 7693 var ilen = points.length; 7694 var i = 0; 7695 7696 helpers.canvas.clipArea(chart.ctx, area); 7697 7698 if (lineEnabled(me.getDataset(), chart.options)) { 7699 meta.dataset.draw(); 7700 } 7701 7702 helpers.canvas.unclipArea(chart.ctx); 7703 7704 // Draw the points 7705 for (; i < ilen; ++i) { 7706 points[i].draw(area); 7707 } 7708 }, 7709 7710 setHoverStyle: function(point) { 7711 // Point 7712 var dataset = this.chart.data.datasets[point._datasetIndex]; 7713 var index = point._index; 7714 var custom = point.custom || {}; 7715 var model = point._model; 7716 7717 model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
7718 model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 7719 model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 7720 model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); 7721 }, 7722 7723 removeHoverStyle: function(point) { 7724 var me = this; 7725 var dataset = me.chart.data.datasets[point._datasetIndex]; 7726 var index = point._index; 7727 var custom = point.custom || {}; 7728 var model = point._model; 7729 7730 // Compatibility: If the properties are defined with only the old name, use those values 7731 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 7732 dataset.pointRadius = dataset.radius; 7733 } 7734 7735 model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
7736 model.backgroundColor = me.getPointBackgroundColor(point, index); 7737 model.borderColor = me.getPointBorderColor(point, index); 7738 model.borderWidth = me.getPointBorderWidth(point, index); 7739 } 7740 }); 7741}; 7742 7743},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ 7744'use strict'; 7745 7746var defaults = require(25); 7747var elements = require(40); 7748var helpers = require(45); 7749 7750defaults._set('polarArea', { 7751 scale: { 7752 type: 'radialLinear', 7753 angleLines: { 7754 display: false 7755 }, 7756 gridLines: { 7757 circular: true 7758 }, 7759 pointLabels: { 7760 display: false 7761 }, 7762 ticks: { 7763 beginAtZero: true 7764 } 7765 }, 7766 7767 // Boolean - Whether to animate the rotation of the chart 7768 animation: { 7769 animateRotate: true, 7770 animateScale: true 7771 }, 7772 7773 startAngle: -0.5 * Math.PI, 7774 legendCallback: function(chart) { 7775 var text = []; 7776 text.push('<ul class="' + chart.id + '-legend">'); 7777 7778 var data = chart.data; 7779 var datasets = data.datasets; 7780 var labels = data.labels; 7781 7782 if (datasets.length) { 7783 for (var i = 0; i < datasets[0].data.length; ++i) { 7784 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); 7785 if (labels[i]) { 7786 text.push(labels[i]); 7787 } 7788 text.push('</li>'); 7789 } 7790 } 7791 7792 text.push('</ul>'); 7793 return text.join(''); 7794 }, 7795 legend: { 7796 labels: { 7797 generateLabels: function(chart) { 7798 var data = chart.data; 7799 if (data.labels.length && data.datasets.length) { 7800 return data.labels.map(function(label, i) { 7801 var meta = chart.getDatasetMeta(0); 7802 var ds = data.datasets[0]; 7803 var arc = meta.data[i]; 7804 var custom = arc.custom || {}; 7805 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7806 var arcOpts = chart.options.elements.arc; 7807 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); 7808 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); 7809 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); 7810 7811 return { 7812 text: label, 7813 fillStyle: fill, 7814 strokeStyle: stroke, 7815 lineWidth: bw, 7816 hidden: isNaN(ds.data[i]) || meta.data[i].hidden, 7817 7818 // Extra data used for toggling the correct item 7819 index: i 7820 }; 7821 }); 7822 } 7823 return []; 7824 } 7825 }, 7826 7827 onClick: function(e, legendItem) { 7828 var index = legendItem.index; 7829 var chart = this.chart; 7830 var i, ilen, meta; 7831 7832 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 7833 meta = chart.getDatasetMeta(i); 7834 meta.data[index].hidden = !meta.data[index].hidden; 7835 } 7836 7837 chart.update(); 7838 } 7839 }, 7840 7841 // Need to override these to give a nice default 7842 tooltips: { 7843 callbacks: { 7844 title: function() { 7845 return ''; 7846 }, 7847 label: function(item, data) { 7848 return data.labels[item.index] + ': ' + item.yLabel; 7849 } 7850 } 7851 } 7852}); 7853 7854module.exports = function(Chart) { 7855 7856 Chart.controllers.polarArea = Chart.DatasetController.extend({ 7857 7858 dataElementType: elements.Arc, 7859 7860 linkScales: helpers.noop, 7861 7862 update: function(reset) { 7863 var me = this; 7864 var chart = me.chart; 7865 var chartArea = chart.chartArea; 7866 var meta = me.getMeta(); 7867 var opts = chart.options; 7868 var arcOpts = opts.elements.arc; 7869 var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); 7870 chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); 7871 chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); 7872 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); 7873 7874 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
7875 me.innerRadius = me.outerRadius - chart.radiusLength; 7876 7877 meta.count = me.countVisibleElements(); 7878 7879 helpers.each(meta.data, function(arc, index) { 7880 me.updateElement(arc, index, reset); 7881 }); 7882 }, 7883 7884 updateElement: function(arc, index, reset) { 7885 var me = this; 7886 var chart = me.chart; 7887 var dataset = me.getDataset(); 7888 var opts = chart.options; 7889 var animationOpts = opts.animation; 7890 var scale = chart.scale; 7891 var labels = chart.data.labels; 7892 7893 var circumference = me.calculateCircumference(dataset.data[index]); 7894 var centerX = scale.xCenter; 7895 var centerY = scale.yCenter; 7896 7897 // If there is NaN data before us, we need to calculate the starting angle correctly. 7898 // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data 7899 var visibleCount = 0; 7900 var meta = me.getMeta(); 7901 for (var i = 0; i < index; ++i) { 7902 if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { 7903 ++visibleCount; 7904 } 7905 } 7906 7907 // var negHalfPI = -0.5 * Math.PI; 7908 var datasetStartAngle = opts.startAngle; 7909 var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); 7910 var startAngle = datasetStartAngle + (circumference * visibleCount); 7911 var endAngle = startAngle + (arc.hidden ? 0 : circumference); 7912 7913 var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); 7914 7915 helpers.extend(arc, { 7916 // Utility 7917 _datasetIndex: me.index, 7918 _index: index, 7919 _scale: scale, 7920 7921 // Desired view properties 7922 _model: { 7923 x: centerX, 7924 y: centerY, 7925 innerRadius: 0, 7926 outerRadius: reset ? resetRadius : distance, 7927 startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, 7928 endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, 7929 label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) 7930 } 7931 }); 7932 7933 // Apply border and fill style 7934 me.removeHoverStyle(arc); 7935 7936 arc.pivot(); 7937 }, 7938 7939 removeHoverStyle: function(arc) { 7940 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); 7941 }, 7942 7943 countVisibleElements: function() { 7944 var dataset = this.getDataset(); 7945 var meta = this.getMeta(); 7946 var count = 0; 7947 7948 helpers.each(meta.data, function(element, index) { 7949 if (!isNaN(dataset.data[index]) && !element.hidden) { 7950 count++; 7951 } 7952 }); 7953 7954 return count; 7955 }, 7956 7957 calculateCircumference: function(value) { 7958 var count = this.getMeta().count; 7959 if (count > 0 && !isNaN(value)) { 7960 return (2 * Math.PI) / count; 7961 } 7962 return 0; 7963 } 7964 }); 7965}; 7966 7967},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ 7968'use strict'; 7969 7970var defaults = require(25); 7971var elements = require(40); 7972var helpers = require(45); 7973 7974defaults._set('radar', { 7975 scale: { 7976 type: 'radialLinear' 7977 }, 7978 elements: { 7979 line: { 7980 tension: 0 // no bezier in radar 7981 } 7982 } 7983}); 7984 7985module.exports = function(Chart) { 7986 7987 Chart.controllers.radar = Chart.DatasetController.extend({ 7988 7989 datasetElementType: elements.Line, 7990 7991 dataElementType: elements.Point, 7992 7993 linkScales: helpers.noop, 7994 7995 update: function(reset) { 7996 var me = this; 7997 var meta = me.getMeta(); 7998 var line = meta.dataset; 7999 var points = meta.data; 8000 var custom = line.custom || {}; 8001 var dataset = me.getDataset(); 8002 var lineElementOptions = me.chart.options.elements.line; 8003 var scale = me.chart.scale; 8004 8005 // Compatibility: If the properties are defined with only the old name, use those values 8006 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { 8007 dataset.lineTension = dataset.tension; 8008 } 8009 8010 helpers.extend(meta.dataset, { 8011 // Utility 8012 _datasetIndex: me.index, 8013 _scale: scale, 8014 // Data 8015 _children: points, 8016 _loop: true, 8017 // Model 8018 _model: { 8019 // Appearance 8020 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), 8021 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), 8022 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), 8023 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
8024 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), 8025 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), 8026 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), 8027 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), 8028 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), 8029 } 8030 }); 8031 8032 meta.dataset.pivot(); 8033 8034 // Update Points 8035 helpers.each(points, function(point, index) { 8036 me.updateElement(point, index, reset); 8037 }, me); 8038 8039 // Update bezier control points 8040 me.updateBezierControlPoints(); 8041 }, 8042 updateElement: function(point, index, reset) { 8043 var me = this; 8044 var custom = point.custom || {}; 8045 var dataset = me.getDataset(); 8046 var scale = me.chart.scale; 8047 var pointElementOptions = me.chart.options.elements.point; 8048 var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); 8049 8050 // Compatibility: If the properties are defined with only the old name, use those values 8051 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 8052 dataset.pointRadius = dataset.radius; 8053 } 8054 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { 8055 dataset.pointHitRadius = dataset.hitRadius; 8056 } 8057 8058 helpers.extend(point, { 8059 // Utility 8060 _datasetIndex: me.index, 8061 _index: index, 8062 _scale: scale, 8063 8064 // Desired view properties 8065 _model: { 8066 x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales 8067 y: reset ? scale.yCenter : pointPosition.y, 8068 8069 // Appearance 8070 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), 8071 radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), 8072 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
8072dColor, index, pointElementOptions.backgroundColor), 8073 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), 8074 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), 8075 pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), 8076 8077 // Tooltip 8078 hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) 8079 } 8080 }); 8081 8082 point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); 8083 }, 8084 updateBezierControlPoints: function() { 8085 var chartArea = this.chart.chartArea; 8086 var meta = this.getMeta(); 8087 8088 helpers.each(meta.data, function(point, index) { 8089 var model = point._model; 8090 var controlPoints = helpers.splineCurve( 8091 helpers.previousItem(meta.data, index, true)._model, 8092 model, 8093 helpers.nextItem(meta.data, index, true)._model, 8094 model.tension 8095 ); 8096 8097 // Prevent the bezier going outside of the bounds of the graph 8098 model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); 8099 model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); 8100 8101 model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); 8102 model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); 8103 8104 // Now pivot the point for animation 8105 point.pivot(); 8106 }); 8107 }, 8108 8109 setHoverStyle: function(point) { 8110 // Point 8111 var dataset = this.chart.data.datasets[point._datasetIndex]; 8112 var custom = point.custom || {}; 8113 var index = point._index; 8114 var model = point._model; 8115 8116 model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); 8117 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 8118 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 8119 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); 8120 }, 8121 8122 removeHoverStyle: function(point) { 8123 var dataset = this.chart.data.datasets[point._datasetIndex]; 8124 var custom = point.custom || {}; 8125 var index = point._index; 8126 var model = point._model; 8127 var pointElementOptions = this.chart.options.elements.point; 8128 8129 model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); 8130 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
8130dColor, index, pointElementOptions.backgroundColor); 8131 model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); 8132 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); 8133 } 8134 }); 8135}; 8136 8137},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ 8138'use strict'; 8139 8140var defaults = require(25); 8141 8142defaults._set('scatter', { 8143 hover: { 8144 mode: 'single' 8145 }, 8146 8147 scales: { 8148 xAxes: [{ 8149 id: 'x-axis-1', // need an ID so datasets can reference the scale 8150 type: 'linear', // scatter should not use a category axis 8151 position: 'bottom' 8152 }], 8153 yAxes: [{ 8154 id: 'y-axis-1', 8155 type: 'linear', 8156 position: 'left' 8157 }] 8158 }, 8159 8160 showLines: false, 8161 8162 tooltips: { 8163 callbacks: { 8164 title: function() { 8165 return ''; // doesn't make sense for scatter since data are formatted as a point 8166 }, 8167 label: function(item) { 8168 return '(' + item.xLabel + ', ' + item.yLabel + ')'; 8169 } 8170 } 8171 } 8172}); 8173 8174module.exports = function(Chart) { 8175 8176 // Scatter charts use line controllers 8177 Chart.controllers.scatter = Chart.controllers.line; 8178 8179}; 8180 8181},{"25":25}],22:[function(require,module,exports){ 8182/* global window: false */ 8183'use strict'; 8184 8185var defaults = require(25); 8186var Element = require(26); 8187var helpers = require(45); 8188 8189defaults._set('global', { 8190 animation: { 8191 duration: 1000, 8192 easing: 'easeOutQuart', 8193 onProgress: helpers.noop, 8194 onComplete: helpers.noop 8195 } 8196}); 8197 8198module.exports = function(Chart) { 8199 8200 Chart.Animation = Element.extend({ 8201 chart: null, // the animation associated chart instance 8202 currentStep: 0, // the current animation step 8203 numSteps: 60, // default number of steps 8204 easing: '', // the easing to use for this animation 8205 render: null, // render function used by the animation service 8206 8207 onAnimationProgress: null, // user specified callback to fire on each step of the animation 8208 onAnimationComplete: null, // user specified callback to fire when the animation finishes 8209 }); 8210 8211 Chart.animationService = { 8212 frameDuration: 17, 8213 animations: [], 8214 dropFrames: 0, 8215 request: null, 8216 8217 /** 8218 * @param {Chart} chart - The chart to animate. 8219 * @param {Chart.Animation} animation - The animation that we will animate. 8220 * @param {Number} duration - The animation duration in ms. 8221 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions 8222 */ 8223 addAnimation: function(chart, animation, duration, lazy) { 8224 var animations = this.animations; 8225 var i, ilen; 8226 8227 animation.chart = chart; 8228 8229 if (!lazy) { 8230 chart.animating = true; 8231 } 8232 8233 for (i = 0, ilen = animations.length; i < ilen; ++i) { 8234 if (animations[i].chart === chart) { 8235 animations[i] = animation; 8236 return; 8237 } 8238 } 8239 8240 animations.push(animation); 8241 8242 // If there are no animations queued, manually kickstart a digest, for lack of a better word 8243 if (animations.length === 1) { 8244 this.requestAnimationFrame(); 8245 } 8246 }, 8247 8248 cancelAnimation: function(chart) { 8249 var index = helpers.findIndex(this.animations, function(animation) { 8250 return animation.chart === chart; 8251 }); 8252 8253 if (index !== -1) { 8254 this.animations.splice(index, 1); 8255 chart.animating = false; 8256 } 8257 }, 8258 8259 requestAnimationFrame: function() { 8260 var me = this; 8261 if (me.request === null) { 8262 // Skip animation frame requests until the active one is executed. 8263 // This can happen when processing mouse events, e.g. 'mousemove' 8264 // and 'mouseout' events will trigger multiple renders. 8265 me.request = helpers.requestAnimFrame.call(window, function() { 8266 me.request = null; 8267 me.startDigest(); 8268 }); 8269 } 8270 }, 8271 8272 /** 8273 * @private 8274 */ 8275 startDigest: function() { 8276 var me = this; 8277 var startTime = Date.now(); 8278 var framesToDrop = 0; 8279 8280 if (me.dropFrames > 1) { 8281 framesToDrop = Math.floor(me.dropFrames); 8282 me.dropFrames = me.dropFrames % 1; 8283 } 8284 8285 me.advance(1 + framesToDrop); 8286 8287 var endTime = Date.now(); 8288 8289 me.dropFrames += (endTime - startTime) / me.frameDuration; 8290 8291 // Do we have more stuff to animate? 8292 if (me.animations.length > 0) { 8293 me.requestAnimationFrame(); 8294 } 8295 }, 8296 8297 /** 8298 * @private 8299 */ 8300 advance: function(count) { 8301 var animations = this.animations; 8302 var animation, chart; 8303 var i = 0; 8304 8305 while (i < animations.length) { 8306 animation = animations[i]; 8307 chart = animation.chart; 8308 8309 animation.currentStep = (animation.currentStep || 0) + count; 8310 animation.currentStep = Math.min(animation.currentStep, animation.numSteps); 8311 8312 helpers.callback(animation.render, [chart, animation], chart); 8313 helpers.callback(animation.onAnimationProgress, [animation], chart); 8314 8315 if (animation.currentStep >= animation.numSteps) { 8316 helpers.callback(animation.onAnimationComplete, [animation], chart); 8317 chart.animating = false; 8318 animations.splice(i, 1); 8319 } else { 8320 ++i; 8321 } 8322 } 8323 } 8324 }; 8325 8326 /** 8327 * Provided for backward compatibility, use Chart.Animation instead 8328 * @prop Chart.Animation#animationObject 8329 * @deprecated since version 2.6.0 8330 * @todo remove at version 3 8331 */
8332 Object.defineProperty(Chart.Animation.prototype, 'animationObject', { 8333 get: function() { 8334 return this; 8335 } 8336 }); 8337 8338 /** 8339 * Provided for backward compatibility, use Chart.Animation#chart instead 8340 * @prop Chart.Animation#chartInstance 8341 * @deprecated since version 2.6.0 8342 * @todo remove at version 3 8343 */ 8344 Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { 8345 get: function() { 8346 return this.chart; 8347 }, 8348 set: function(value) { 8349 this.chart = value; 8350 } 8351 }); 8352 8353}; 8354 8355},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ 8356'use strict'; 8357 8358var defaults = require(25); 8359var helpers = require(45); 8360var Interaction = require(28); 8361var layouts = require(30); 8362var platform = require(48); 8363var plugins = require(31); 8364 8365module.exports = function(Chart) { 8366 8367 // Create a dictionary of chart types, to allow for extension of existing types 8368 Chart.types = {}; 8369 8370 // Store a reference to each instance - allowing us to globally resize chart instances on window resize. 8371 // Destroy method on the chart will remove the instance of the chart from this reference. 8372 Chart.instances = {}; 8373 8374 // Controllers available for dataset visualization eg. bar, line, slice, etc. 8375 Chart.controllers = {}; 8376 8377 /** 8378 * Initializes the given config with global and chart default values. 8379 */ 8380 function initConfig(config) { 8381 config = config || {}; 8382 8383 // Do NOT use configMerge() for the data object because this method merges arrays
vendor: 12,882 bytes, lines 8384-8846
8384 // and so would change references to labels and datasets, preventing data updates. 8385 var data = config.data = config.data || {}; 8386 data.datasets = data.datasets || []; 8387 data.labels = data.labels || []; 8388 8389 config.options = helpers.configMerge( 8390 defaults.global, 8391 defaults[config.type], 8392 config.options || {}); 8393 8394 return config; 8395 } 8396 8397 /** 8398 * Updates the config of the chart 8399 * @param chart {Chart} chart to update the options for 8400 */ 8401 function updateConfig(chart) { 8402 var newOptions = chart.options; 8403 8404 helpers.each(chart.scales, function(scale) { 8405 layouts.removeBox(chart, scale); 8406 }); 8407 8408 newOptions = helpers.configMerge( 8409 Chart.defaults.global, 8410 Chart.defaults[chart.config.type], 8411 newOptions); 8412 8413 chart.options = chart.config.options = newOptions; 8414 chart.ensureScalesHaveIDs(); 8415 chart.buildOrUpdateScales(); 8416 // Tooltip 8417 chart.tooltip._options = newOptions.tooltips; 8418 chart.tooltip.initialize(); 8419 } 8420 8421 function positionIsHorizontal(position) { 8422 return position === 'top' || position === 'bottom'; 8423 } 8424 8425 helpers.extend(Chart.prototype, /** @lends Chart */ { 8426 /** 8427 * @private 8428 */ 8429 construct: function(item, config) { 8430 var me = this; 8431 8432 config = initConfig(config); 8433 8434 var context = platform.acquireContext(item, config); 8435 var canvas = context && context.canvas; 8436 var height = canvas && canvas.height; 8437 var width = canvas && canvas.width; 8438 8439 me.id = helpers.uid(); 8440 me.ctx = context; 8441 me.canvas = canvas; 8442 me.config = config; 8443 me.width = width; 8444 me.height = height; 8445 me.aspectRatio = height ? width / height : null; 8446 me.options = config.options; 8447 me._bufferedRender = false; 8448 8449 /** 8450 * Provided for backward compatibility, Chart and Chart.Controller have been merged, 8451 * the "instance" still need to be defined since it might be called from plugins. 8452 * @prop Chart#chart 8453 * @deprecated since version 2.6.0 8454 * @todo remove at version 3 8455 * @private 8456 */ 8457 me.chart = me; 8458 me.controller = me; // chart.chart.controller #inception 8459 8460 // Add the chart instance to the global namespace 8461 Chart.instances[me.id] = me; 8462 8463 // Define alias to the config data: `chart.data === chart.config.data` 8464 Object.defineProperty(me, 'data', { 8465 get: function() { 8466 return me.config.data; 8467 }, 8468 set: function(value) { 8469 me.config.data = value; 8470 } 8471 }); 8472 8473 if (!context || !canvas) { 8474 // The given item is not a compatible context2d element, let's return before finalizing 8475 // the chart initialization but after setting basic chart / controller properties that 8476 // can help to figure out that the chart is not valid (e.g chart.canvas !== null); 8477 // https://github.com/chartjs/Chart.js/issues/2807 8478 console.error("Failed to create chart: can't acquire context from the given item"); 8479 return; 8480 } 8481 8482 me.initialize(); 8483 me.update(); 8484 }, 8485 8486 /** 8487 * @private 8488 */ 8489 initialize: function() { 8490 var me = this; 8491 8492 // Before init plugin notification 8493 plugins.notify(me, 'beforeInit'); 8494 8495 helpers.retinaScale(me, me.options.devicePixelRatio); 8496 8497 me.bindEvents(); 8498 8499 if (me.options.responsive) { 8500 // Initial resize before chart draws (must be silent to preserve initial animations). 8501 me.resize(true); 8502 } 8503 8504 // Make sure scales have IDs and are built before we build any controllers. 8505 me.ensureScalesHaveIDs(); 8506 me.buildOrUpdateScales(); 8507 me.initToolTip(); 8508 8509 // After init plugin notification 8510 plugins.notify(me, 'afterInit'); 8511 8512 return me; 8513 }, 8514 8515 clear: function() { 8516 helpers.canvas.clear(this); 8517 return this; 8518 }, 8519 8520 stop: function() { 8521 // Stops any current animation loop occurring 8522 Chart.animationService.cancelAnimation(this); 8523 return this; 8524 }, 8525 8526 resize: function(silent) { 8527 var me = this; 8528 var options = me.options; 8529 var canvas = me.canvas; 8530 var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; 8531 8532 // the canvas render width and height will be casted to integers so make sure that 8533 // the canvas display style uses the same integer values to avoid blurring effect. 8534 8535 // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased 8536 var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); 8537 var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); 8538 8539 if (me.width === newWidth && me.height === newHeight) { 8540 return; 8541 } 8542 8543 canvas.width = me.width = newWidth; 8544 canvas.height = me.height = newHeight; 8545 canvas.style.width = newWidth + 'px'; 8546 canvas.style.height = newHeight + 'px'; 8547 8548 helpers.retinaScale(me, options.devicePixelRatio); 8549 8550 if (!silent) { 8551 // Notify any plugins about the resize 8552 var newSize = {width: newWidth, height: newHeight}; 8553 plugins.notify(me, 'resize', [newSize]); 8554 8555 // Notify of resize 8556 if (me.options.onResize) { 8557 me.options.onResize(me, newSize); 8558 } 8559 8560 me.stop(); 8561 me.update(me.options.responsiveAnimationDuration); 8562 } 8563 }, 8564 8565 ensureScalesHaveIDs: function() { 8566 var options = this.options; 8567 var scalesOptions = options.scales || {}; 8568 var scaleOptions = options.scale; 8569 8570 helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { 8571 xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); 8572 }); 8573 8574 helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { 8575 yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); 8576 }); 8577 8578 if (scaleOptions) { 8579 scaleOptions.id = scaleOptions.id || 'scale'; 8580 } 8581 }, 8582 8583 /** 8584 * Builds a map of scale ID to scale object for future lookup. 8585 */ 8586 buildOrUpdateScales: function() { 8587 var me = this; 8588 var options = me.options; 8589 var scales = me.scales || {}; 8590 var items = []; 8591 var updated = Object.keys(scales).reduce(function(obj, id) { 8592 obj[id] = false; 8593 return obj; 8594 }, {}); 8595 8596 if (options.scales) { 8597 items = items.concat( 8598 (options.scales.xAxes || []).map(function(xAxisOptions) { 8599 return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; 8600 }), 8601 (options.scales.yAxes || []).map(function(yAxisOptions) { 8602 return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; 8603 }) 8604 ); 8605 } 8606 8607 if (options.scale) { 8608 items.push({ 8609 options: options.scale, 8610 dtype: 'radialLinear', 8611 isDefault: true, 8612 dposition: 'chartArea' 8613 }); 8614 } 8615 8616 helpers.each(items, function(item) { 8617 var scaleOptions = item.options; 8618 var id = scaleOptions.id; 8619 var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); 8620 8621 if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { 8622 scaleOptions.position = item.dposition; 8623 } 8624 8625 updated[id] = true; 8626 var scale = null; 8627 if (id in scales && scales[id].type === scaleType) { 8628 scale = scales[id]; 8629 scale.options = scaleOptions; 8630 scale.ctx = me.ctx; 8631 scale.chart = me; 8632 } else { 8633 var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); 8634 if (!scaleClass) { 8635 return; 8636 } 8637 scale = new scaleClass({ 8638 id: id, 8639 type: scaleType, 8640 options: scaleOptions, 8641 ctx: me.ctx, 8642 chart: me 8643 }); 8644 scales[scale.id] = scale; 8645 } 8646 8647 scale.mergeTicksOptions(); 8648 8649 // TODO(SB): I think we should be able to remove this custom case (options.scale) 8650 // and consider it as a regular scale part of the "scales"" map only! This would 8651 // make the logic easier and remove some useless? custom code. 8652 if (item.isDefault) { 8653 me.scale = scale; 8654 } 8655 }); 8656 // clear up discarded scales 8657 helpers.each(updated, function(hasUpdated, id) { 8658 if (!hasUpdated) { 8659 delete scales[id]; 8660 } 8661 }); 8662 8663 me.scales = scales; 8664 8665 Chart.scaleService.addScalesToLayout(this); 8666 }, 8667 8668 buildOrUpdateControllers: function() { 8669 var me = this; 8670 var types = []; 8671 var newControllers = []; 8672 8673 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8674 var meta = me.getDatasetMeta(datasetIndex); 8675 var type = dataset.type || me.config.type; 8676 8677 if (meta.type && meta.type !== type) { 8678 me.destroyDatasetMeta(datasetIndex); 8679 meta = me.getDatasetMeta(datasetIndex); 8680 } 8681 meta.type = type; 8682 8683 types.push(meta.type); 8684 8685 if (meta.controller) { 8686 meta.controller.updateIndex(datasetIndex); 8687 meta.controller.linkScales(); 8688 } else { 8689 var ControllerClass = Chart.controllers[meta.type]; 8690 if (ControllerClass === undefined) { 8691 throw new Error('"' + meta.type + '" is not a chart type.'); 8692 } 8693 8694 meta.controller = new ControllerClass(me, datasetIndex); 8695 newControllers.push(meta.controller); 8696 } 8697 }, me); 8698 8699 return newControllers; 8700 }, 8701 8702 /** 8703 * Reset the elements of all datasets 8704 * @private 8705 */ 8706 resetElements: function() { 8707 var me = this; 8708 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8709 me.getDatasetMeta(datasetIndex).controller.reset(); 8710 }, me); 8711 }, 8712 8713 /** 8714 * Resets the chart back to it's state before the initial animation 8715 */ 8716 reset: function() { 8717 this.resetElements(); 8718 this.tooltip.initialize(); 8719 }, 8720 8721 update: function(config) { 8722 var me = this; 8723 8724 if (!config || typeof config !== 'object') { 8725 // backwards compatibility 8726 config = { 8727 duration: config, 8728 lazy: arguments[1] 8729 }; 8730 } 8731 8732 updateConfig(me); 8733 8734 // plugins options references might have change, let's invalidate the cache 8735 // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 8736 plugins._invalidate(me); 8737 8738 if (plugins.notify(me, 'beforeUpdate') === false) { 8739 return; 8740 } 8741 8742 // In case the entire data object changed 8743 me.tooltip._data = me.data; 8744 8745 // Make sure dataset controllers are updated and new controllers are reset 8746 var newControllers = me.buildOrUpdateControllers(); 8747 8748 // Make sure all dataset controllers have correct meta data counts 8749 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8750 me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); 8751 }, me); 8752 8753 me.updateLayout(); 8754 8755 // Can only reset the new controllers after the scales have been updated 8756 if (me.options.animation && me.options.animation.duration) { 8757 helpers.each(newControllers, function(controller) { 8758 controller.reset(); 8759 }); 8760 } 8761 8762 me.updateDatasets(); 8763 8764 // Need to reset tooltip in case it is displayed with elements that are removed 8765 // after update. 8766 me.tooltip.initialize(); 8767 8768 // Last active contains items that were previously in the tooltip. 8769 // When we reset the tooltip, we need to clear it 8770 me.lastActive = []; 8771 8772 // Do this before render so that any plugins that need final scale updates can use it 8773 plugins.notify(me, 'afterUpdate'); 8774 8775 if (me._bufferedRender) { 8776 me._bufferedRequest = { 8777 duration: config.duration, 8778 easing: config.easing, 8779 lazy: config.lazy 8780 }; 8781 } else { 8782 me.render(config); 8783 } 8784 }, 8785 8786 /** 8787 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` 8788 * hook, in which case, plugins will not be called on `afterLayout`. 8789 * @private 8790 */ 8791 updateLayout: function() { 8792 var me = this; 8793 8794 if (plugins.notify(me, 'beforeLayout') === false) { 8795 return; 8796 } 8797 8798 layouts.update(this, this.width, this.height); 8799 8800 /** 8801 * Provided for backward compatibility, use `afterLayout` instead. 8802 * @method IPlugin#afterScaleUpdate 8803 * @deprecated since version 2.5.0 8804 * @todo remove at version 3 8805 * @private 8806 */ 8807 plugins.notify(me, 'afterScaleUpdate'); 8808 plugins.notify(me, 'afterLayout'); 8809 }, 8810 8811 /** 8812 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` 8813 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. 8814 * @private 8815 */ 8816 updateDatasets: function() { 8817 var me = this; 8818 8819 if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { 8820 return; 8821 } 8822 8823 for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { 8824 me.updateDataset(i); 8825 } 8826 8827 plugins.notify(me, 'afterDatasetsUpdate'); 8828 }, 8829 8830 /** 8831 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` 8832 * hook, in which case, plugins will not be called on `afterDatasetUpdate`. 8833 * @private 8834 */ 8835 updateDataset: function(index) { 8836 var me = this; 8837 var meta = me.getDatasetMeta(index); 8838 var args = { 8839 meta: meta, 8840 index: index 8841 }; 8842 8843 if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { 8844 return; 8845 } 8846
8847 meta.controller.update(); 8848 8849 plugins.notify(me, 'afterDatasetUpdate', [args]); 8850 }, 8851 8852 render: function(config) { 8853 var me = this; 8854 8855 if (!config || typeof config !== 'object') { 8856 // backwards compatibility 8857 config = { 8858 duration: config, 8859 lazy: arguments[1] 8860 }; 8861 } 8862 8863 var duration = config.duration; 8864 var lazy = config.lazy; 8865 8866 if (plugins.notify(me, 'beforeRender') === false) { 8867 return; 8868 } 8869 8870 var animationOptions = me.options.animation; 8871 var onComplete = function(animation) { 8872 plugins.notify(me, 'afterRender'); 8873 helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); 8874 }; 8875 8876 if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { 8877 var animation = new Chart.Animation({ 8878 numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps 8879 easing: config.easing || animationOptions.easing, 8880 8881 render: function(chart, animationObject) { 8882 var easingFunction = helpers.easing.effects[animationObject.easing]; 8883 var currentStep = animationObject.currentStep; 8884 var stepDecimal = currentStep / animationObject.numSteps; 8885 8886 chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); 8887 }, 8888 8889 onAnimationProgress: animationOptions.onProgress, 8890 onAnimationComplete: onComplete 8891 }); 8892 8893 Chart.animationService.addAnimation(me, animation, duration, lazy); 8894 } else { 8895 me.draw(); 8896 8897 // See https://github.com/chartjs/Chart.js/issues/3781 8898 onComplete(new Chart.Animation({numSteps: 0, chart: me})); 8899 } 8900 8901 return me; 8902 }, 8903 8904 draw: function(easingValue) { 8905 var me = this; 8906 8907 me.clear(); 8908 8909 if (helpers.isNullOrUndef(easingValue)) { 8910 easingValue = 1; 8911 } 8912 8913 me.transition(easingValue); 8914 8915 if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { 8916 return; 8917 } 8918 8919 // Draw all the scales 8920 helpers.each(me.boxes, function(box) { 8921 box.draw(me.chartArea); 8922 }, me); 8923 8924 if (me.scale) { 8925 me.scale.draw(); 8926 } 8927 8928 me.drawDatasets(easingValue); 8929 me._drawTooltip(easingValue); 8930 8931 plugins.notify(me, 'afterDraw', [easingValue]); 8932 }, 8933 8934 /** 8935 * @private 8936 */ 8937 transition: function(easingValue) { 8938 var me = this; 8939 8940 for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { 8941 if (me.isDatasetVisible(i)) { 8942 me.getDatasetMeta(i).controller.transition(easingValue); 8943 } 8944 } 8945 8946 me.tooltip.transition(easingValue); 8947 }, 8948 8949 /** 8950 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` 8951 * hook, in which case, plugins will not be called on `afterDatasetsDraw`. 8952 * @private 8953 */ 8954 drawDatasets: function(easingValue) { 8955 var me = this; 8956 8957 if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { 8958 return; 8959 } 8960 8961 // Draw datasets reversed to support proper line stacking 8962 for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { 8963 if (me.isDatasetVisible(i)) { 8964 me.drawDataset(i, easingValue); 8965 } 8966 } 8967 8968 plugins.notify(me, 'afterDatasetsDraw', [easingValue]); 8969 }, 8970 8971 /** 8972 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` 8973 * hook, in which case, plugins will not be called on `afterDatasetDraw`. 8974 * @private 8975 */ 8976 drawDataset: function(index, easingValue) { 8977 var me = this; 8978 var meta = me.getDatasetMeta(index); 8979 var args = { 8980 meta: meta, 8981 index: index, 8982 easingValue: easingValue 8983 }; 8984 8985 if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { 8986 return; 8987 } 8988 8989 meta.controller.draw(easingValue); 8990 8991 plugins.notify(me, 'afterDatasetDraw', [args]); 8992 }, 8993 8994 /** 8995 * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` 8996 * hook, in which case, plugins will not be called on `afterTooltipDraw`. 8997 * @private 8998 */ 8999 _drawTooltip: function(easingValue) { 9000 var me = this; 9001 var tooltip = me.tooltip; 9002 var args = { 9003 tooltip: tooltip, 9004 easingValue: easingValue 9005 }; 9006 9007 if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { 9008 return; 9009 } 9010 9011 tooltip.draw(); 9012 9013 plugins.notify(me, 'afterTooltipDraw', [args]); 9014 }, 9015 9016 // Get the single element that was clicked on
vendor: 12,764 bytes, lines 9017-9508
9017 // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw 9018 getElementAtEvent: function(e) { 9019 return Interaction.modes.single(this, e); 9020 }, 9021 9022 getElementsAtEvent: function(e) { 9023 return Interaction.modes.label(this, e, {intersect: true}); 9024 }, 9025 9026 getElementsAtXAxis: function(e) { 9027 return Interaction.modes['x-axis'](this, e, {intersect: true}); 9028 }, 9029 9030 getElementsAtEventForMode: function(e, mode, options) { 9031 var method = Interaction.modes[mode]; 9032 if (typeof method === 'function') { 9033 return method(this, e, options); 9034 } 9035 9036 return []; 9037 }, 9038 9039 getDatasetAtEvent: function(e) { 9040 return Interaction.modes.dataset(this, e, {intersect: true}); 9041 }, 9042 9043 getDatasetMeta: function(datasetIndex) { 9044 var me = this; 9045 var dataset = me.data.datasets[datasetIndex]; 9046 if (!dataset._meta) { 9047 dataset._meta = {}; 9048 } 9049 9050 var meta = dataset._meta[me.id]; 9051 if (!meta) { 9052 meta = dataset._meta[me.id] = { 9053 type: null, 9054 data: [], 9055 dataset: null, 9056 controller: null, 9057 hidden: null, // See isDatasetVisible() comment 9058 xAxisID: null, 9059 yAxisID: null 9060 }; 9061 } 9062 9063 return meta; 9064 }, 9065 9066 getVisibleDatasetCount: function() { 9067 var count = 0; 9068 for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { 9069 if (this.isDatasetVisible(i)) { 9070 count++; 9071 } 9072 } 9073 return count; 9074 }, 9075 9076 isDatasetVisible: function(datasetIndex) { 9077 var meta = this.getDatasetMeta(datasetIndex); 9078 9079 // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, 9080 // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. 9081 return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; 9082 }, 9083 9084 generateLegend: function() { 9085 return this.options.legendCallback(this); 9086 }, 9087 9088 /** 9089 * @private 9090 */ 9091 destroyDatasetMeta: function(datasetIndex) { 9092 var id = this.id; 9093 var dataset = this.data.datasets[datasetIndex]; 9094 var meta = dataset._meta && dataset._meta[id]; 9095 9096 if (meta) { 9097 meta.controller.destroy(); 9098 delete dataset._meta[id]; 9099 } 9100 }, 9101 9102 destroy: function() { 9103 var me = this; 9104 var canvas = me.canvas; 9105 var i, ilen; 9106 9107 me.stop(); 9108 9109 // dataset controllers need to cleanup associated data 9110 for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { 9111 me.destroyDatasetMeta(i); 9112 } 9113 9114 if (canvas) { 9115 me.unbindEvents(); 9116 helpers.canvas.clear(me); 9117 platform.releaseContext(me.ctx); 9118 me.canvas = null; 9119 me.ctx = null; 9120 } 9121 9122 plugins.notify(me, 'destroy'); 9123 9124 delete Chart.instances[me.id]; 9125 }, 9126 9127 toBase64Image: function() { 9128 return this.canvas.toDataURL.apply(this.canvas, arguments); 9129 }, 9130 9131 initToolTip: function() { 9132 var me = this; 9133 me.tooltip = new Chart.Tooltip({ 9134 _chart: me, 9135 _chartInstance: me, // deprecated, backward compatibility 9136 _data: me.data, 9137 _options: me.options.tooltips 9138 }, me); 9139 }, 9140 9141 /** 9142 * @private 9143 */ 9144 bindEvents: function() { 9145 var me = this; 9146 var listeners = me._listeners = {}; 9147 var listener = function() { 9148 me.eventHandler.apply(me, arguments); 9149 }; 9150 9151 helpers.each(me.options.events, function(type) { 9152 platform.addEventListener(me, type, listener); 9153 listeners[type] = listener; 9154 }); 9155 9156 // Elements used to detect size change should not be injected for non responsive charts. 9157 // See https://github.com/chartjs/Chart.js/issues/2210 9158 if (me.options.responsive) { 9159 listener = function() { 9160 me.resize(); 9161 }; 9162 9163 platform.addEventListener(me, 'resize', listener); 9164 listeners.resize = listener; 9165 } 9166 }, 9167 9168 /** 9169 * @private 9170 */ 9171 unbindEvents: function() { 9172 var me = this; 9173 var listeners = me._listeners; 9174 if (!listeners) { 9175 return; 9176 } 9177 9178 delete me._listeners; 9179 helpers.each(listeners, function(listener, type) { 9180 platform.removeEventListener(me, type, listener); 9181 }); 9182 }, 9183 9184 updateHoverStyle: function(elements, mode, enabled) { 9185 var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; 9186 var element, i, ilen; 9187 9188 for (i = 0, ilen = elements.length; i < ilen; ++i) { 9189 element = elements[i]; 9190 if (element) { 9191 this.getDatasetMeta(element._datasetIndex).controller[method](element); 9192 } 9193 } 9194 }, 9195 9196 /** 9197 * @private 9198 */ 9199 eventHandler: function(e) { 9200 var me = this; 9201 var tooltip = me.tooltip; 9202 9203 if (plugins.notify(me, 'beforeEvent', [e]) === false) { 9204 return; 9205 } 9206 9207 // Buffer any update calls so that renders do not occur 9208 me._bufferedRender = true; 9209 me._bufferedRequest = null; 9210 9211 var changed = me.handleEvent(e); 9212 // for smooth tooltip animations issue #4989 9213 // the tooltip should be the source of change 9214 // Animation check workaround: 9215 // tooltip._start will be null when tooltip isn't animating 9216 if (tooltip) { 9217 changed = tooltip._start 9218 ? tooltip.handleEvent(e) 9219 : changed | tooltip.handleEvent(e); 9220 } 9221 9222 plugins.notify(me, 'afterEvent', [e]); 9223 9224 var bufferedRequest = me._bufferedRequest; 9225 if (bufferedRequest) { 9226 // If we have an update that was triggered, we need to do a normal render 9227 me.render(bufferedRequest); 9228 } else if (changed && !me.animating) { 9229 // If entering, leaving, or changing elements, animate the change via pivot 9230 me.stop(); 9231 9232 // We only need to render at this point. Updating will cause scales to be 9233 // recomputed generating flicker & using more memory than necessary. 9234 me.render(me.options.hover.animationDuration, true); 9235 } 9236 9237 me._bufferedRender = false; 9238 me._bufferedRequest = null; 9239 9240 return me; 9241 }, 9242 9243 /** 9244 * Handle an event 9245 * @private 9246 * @param {IEvent} event the event to handle 9247 * @return {Boolean} true if the chart needs to re-render 9248 */ 9249 handleEvent: function(e) { 9250 var me = this; 9251 var options = me.options || {}; 9252 var hoverOptions = options.hover; 9253 var changed = false; 9254 9255 me.lastActive = me.lastActive || []; 9256 9257 // Find Active Elements for hover and tooltips 9258 if (e.type === 'mouseout') { 9259 me.active = []; 9260 } else { 9261 me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); 9262 } 9263 9264 // Invoke onHover hook 9265 // Need to call with native event here to not break backwards compatibility 9266 helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); 9267 9268 if (e.type === 'mouseup' || e.type === 'click') { 9269 if (options.onClick) { 9270 // Use e.native here for backwards compatibility 9271 options.onClick.call(me, e.native, me.active); 9272 } 9273 } 9274 9275 // Remove styling for last active (even if it may still be active) 9276 if (me.lastActive.length) { 9277 me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); 9278 } 9279 9280 // Built in hover styling 9281 if (me.active.length && hoverOptions.mode) { 9282 me.updateHoverStyle(me.active, hoverOptions.mode, true); 9283 } 9284 9285 changed = !helpers.arrayEquals(me.active, me.lastActive); 9286 9287 // Remember Last Actives 9288 me.lastActive = me.active; 9289 9290 return changed; 9291 } 9292 }); 9293 9294 /** 9295 * Provided for backward compatibility, use Chart instead. 9296 * @class Chart.Controller 9297 * @deprecated since version 2.6.0 9298 * @todo remove at version 3 9299 * @private 9300 */ 9301 Chart.Controller = Chart; 9302}; 9303 9304},{"25":25,"28":28,"30":30,"31":31,"45":45,"48":48}],24:[function(require,module,exports){ 9305'use strict'; 9306 9307var helpers = require(45); 9308 9309module.exports = function(Chart) { 9310 9311 var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; 9312 9313 /** 9314 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', 9315 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are 9316 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. 9317 */ 9318 function listenArrayEvents(array, listener) { 9319 if (array._chartjs) { 9320 array._chartjs.listeners.push(listener); 9321 return; 9322 } 9323 9324 Object.defineProperty(array, '_chartjs', { 9325 configurable: true, 9326 enumerable: false, 9327 value: { 9328 listeners: [listener] 9329 } 9330 }); 9331 9332 arrayEvents.forEach(function(key) { 9333 var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); 9334 var base = array[key]; 9335 9336 Object.defineProperty(array, key, { 9337 configurable: true, 9338 enumerable: false, 9339 value: function() { 9340 var args = Array.prototype.slice.call(arguments); 9341 var res = base.apply(this, args); 9342 9343 helpers.each(array._chartjs.listeners, function(object) { 9344 if (typeof object[method] === 'function') { 9345 object[method].apply(object, args); 9346 } 9347 }); 9348 9349 return res; 9350 } 9351 }); 9352 }); 9353 } 9354 9355 /** 9356 * Removes the given array event listener and cleanup extra attached properties (such as 9357 * the _chartjs stub and overridden methods) if array doesn't have any more listeners. 9358 */ 9359 function unlistenArrayEvents(array, listener) { 9360 var stub = array._chartjs; 9361 if (!stub) { 9362 return; 9363 } 9364 9365 var listeners = stub.listeners; 9366 var index = listeners.indexOf(listener); 9367 if (index !== -1) { 9368 listeners.splice(index, 1); 9369 } 9370 9371 if (listeners.length > 0) { 9372 return; 9373 } 9374 9375 arrayEvents.forEach(function(key) { 9376 delete array[key]; 9377 }); 9378 9379 delete array._chartjs; 9380 } 9381 9382 // Base class for all dataset controllers (line, bar, etc) 9383 Chart.DatasetController = function(chart, datasetIndex) { 9384 this.initialize(chart, datasetIndex); 9385 }; 9386 9387 helpers.extend(Chart.DatasetController.prototype, { 9388 9389 /** 9390 * Element type used to generate a meta dataset (e.g. Chart.element.Line). 9391 * @type {Chart.core.element} 9392 */ 9393 datasetElementType: null, 9394 9395 /** 9396 * Element type used to generate a meta data (e.g. Chart.element.Point). 9397 * @type {Chart.core.element} 9398 */ 9399 dataElementType: null, 9400 9401 initialize: function(chart, datasetIndex) { 9402 var me = this; 9403 me.chart = chart; 9404 me.index = datasetIndex; 9405 me.linkScales(); 9406 me.addElements(); 9407 }, 9408 9409 updateIndex: function(datasetIndex) { 9410 this.index = datasetIndex; 9411 }, 9412 9413 linkScales: function() { 9414 var me = this; 9415 var meta = me.getMeta(); 9416 var dataset = me.getDataset(); 9417 9418 if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { 9419 meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; 9420 } 9421 if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { 9422 meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; 9423 } 9424 }, 9425 9426 getDataset: function() { 9427 return this.chart.data.datasets[this.index]; 9428 }, 9429 9430 getMeta: function() { 9431 return this.chart.getDatasetMeta(this.index); 9432 }, 9433 9434 getScaleForId: function(scaleID) { 9435 return this.chart.scales[scaleID]; 9436 }, 9437 9438 reset: function() { 9439 this.update(true); 9440 }, 9441 9442 /** 9443 * @private 9444 */ 9445 destroy: function() { 9446 if (this._data) { 9447 unlistenArrayEvents(this._data, this); 9448 } 9449 }, 9450 9451 createMetaDataset: function() { 9452 var me = this; 9453 var type = me.datasetElementType; 9454 return type && new type({ 9455 _chart: me.chart, 9456 _datasetIndex: me.index 9457 }); 9458 }, 9459 9460 createMetaData: function(index) { 9461 var me = this; 9462 var type = me.dataElementType; 9463 return type && new type({ 9464 _chart: me.chart, 9465 _datasetIndex: me.index, 9466 _index: index 9467 }); 9468 }, 9469 9470 addElements: function() { 9471 var me = this; 9472 var meta = me.getMeta(); 9473 var data = me.getDataset().data || []; 9474 var metaData = meta.data; 9475 var i, ilen; 9476 9477 for (i = 0, ilen = data.length; i < ilen; ++i) { 9478 metaData[i] = metaData[i] || me.createMetaData(i); 9479 } 9480 9481 meta.dataset = meta.dataset || me.createMetaDataset(); 9482 }, 9483 9484 addElementAndReset: function(index) { 9485 var element = this.createMetaData(index); 9486 this.getMeta().data.splice(index, 0, element); 9487 this.updateElement(element, index, true); 9488 }, 9489 9490 buildOrUpdateElements: function() { 9491 var me = this; 9492 var dataset = me.getDataset(); 9493 var data = dataset.data || (dataset.data = []); 9494 9495 // In order to correctly handle data addition/deletion animation (an thus simulate 9496 // real-time charts), we need to monitor these data modifications and synchronize 9497 // the internal meta data accordingly. 9498 if (me._data !== data) { 9499 if (me._data) { 9500 // This case happens when the user replaced the data array instance. 9501 unlistenArrayEvents(me._data, me); 9502 } 9503 9504 listenArrayEvents(data, me); 9505 me._data = data; 9506 } 9507 9508 // Re-sync meta data in case the user replaced the data array or if we missed
9509 // any updates and so make sure that we handle number of datapoints changing. 9510 me.resyncElements(); 9511 }, 9512 9513 update: helpers.noop, 9514 9515 transition: function(easingValue) { 9516 var meta = this.getMeta(); 9517 var elements = meta.data || []; 9518 var ilen = elements.length; 9519 var i = 0; 9520 9521 for (; i < ilen; ++i) { 9522 elements[i].transition(easingValue); 9523 } 9524 9525 if (meta.dataset) { 9526 meta.dataset.transition(easingValue); 9527 } 9528 }, 9529 9530 draw: function() { 9531 var meta = this.getMeta(); 9532 var elements = meta.data || []; 9533 var ilen = elements.length; 9534 var i = 0; 9535 9536 if (meta.dataset) { 9537 meta.dataset.draw(); 9538 } 9539 9540 for (; i < ilen; ++i) { 9541 elements[i].draw(); 9542 } 9543 }, 9544 9545 removeHoverStyle: function(element, elementOpts) { 9546 var dataset = this.chart.data.datasets[element._datasetIndex]; 9547 var index = element._index; 9548 var custom = element.custom || {}; 9549 var valueOrDefault = helpers.valueAtIndexOrDefault; 9550 var model = element._model; 9551 9552 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); 9553 model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); 9554 model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); 9555 }, 9556 9557 setHoverStyle: function(element) { 9558 var dataset = this.chart.data.datasets[element._datasetIndex]; 9559 var index = element._index; 9560 var custom = element.custom || {}; 9561 var valueOrDefault = helpers.valueAtIndexOrDefault; 9562 var getHoverColor = helpers.getHoverColor; 9563 var model = element._model; 9564 9565 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); 9566 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); 9567 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); 9568 }, 9569 9570 /** 9571 * @private 9572 */ 9573 resyncElements: function() { 9574 var me = this; 9575 var meta = me.getMeta(); 9576 var data = me.getDataset().data; 9577 var numMeta = meta.data.length; 9578 var numData = data.length; 9579 9580 if (numData < numMeta) { 9581 meta.data.splice(numData, numMeta - numData); 9582 } else if (numData > numMeta) { 9583 me.insertElements(numMeta, numData - numMeta); 9584 } 9585 }, 9586 9587 /** 9588 * @private 9589 */ 9590 insertElements: function(start, count) { 9591 for (var i = 0; i < count; ++i) { 9592 this.addElementAndReset(start + i); 9593 } 9594 }, 9595 9596 /** 9597 * @private 9598 */ 9599 onDataPush: function() { 9600 this.insertElements(this.getDataset().data.length - 1, arguments.length); 9601 }, 9602 9603 /** 9604 * @private 9605 */ 9606 onDataPop: function() { 9607 this.getMeta().data.pop(); 9608 }, 9609 9610 /** 9611 * @private 9612 */ 9613 onDataShift: function() { 9614 this.getMeta().data.shift(); 9615 }, 9616 9617 /** 9618 * @private 9619 */ 9620 onDataSplice: function(start, count) { 9621 this.getMeta().data.splice(start, count); 9622 this.insertElements(start, arguments.length - 2); 9623 }, 9624 9625 /** 9626 * @private 9627 */ 9628 onDataUnshift: function() { 9629 this.insertElements(0, arguments.length); 9630 } 9631 }); 9632 9633 Chart.DatasetController.extend = helpers.inherits; 9634}; 9635 9636},{"45":45}],25:[function(require,module,exports){ 9637'use strict'; 9638 9639var helpers = require(45); 9640 9641module.exports = { 9642 /** 9643 * @private 9644 */ 9645 _set: function(scope, values) { 9646 return helpers.merge(this[scope] || (this[scope] = {}), values); 9647 } 9648}; 9649 9650},{"45":45}],26:[function(require,module,exports){ 9651'use strict'; 9652 9653var color = require(2); 9654var helpers = require(45); 9655 9656function interpolate(start, view, model, ease) { 9657 var keys = Object.keys(model); 9658 var i, ilen, key, actual, origin, target, type, c0, c1; 9659 9660 for (i = 0, ilen = keys.length; i < ilen; ++i) { 9661 key = keys[i]; 9662 9663 target = model[key]; 9664 9665 // if a value is added to the model after pivot() has been called, the view 9666 // doesn't contain it, so let's initialize the view to the target value. 9667 if (!view.hasOwnProperty(key)) { 9668 view[key] = target; 9669 } 9670 9671 actual = view[key]; 9672
vendor: 28,653 bytes, lines 9673-10577
9673 if (actual === target || key[0] === '_') { 9674 continue; 9675 } 9676 9677 if (!start.hasOwnProperty(key)) { 9678 start[key] = actual; 9679 } 9680 9681 origin = start[key]; 9682 9683 type = typeof target; 9684 9685 if (type === typeof origin) { 9686 if (type === 'string') { 9687 c0 = color(origin); 9688 if (c0.valid) { 9689 c1 = color(target); 9690 if (c1.valid) { 9691 view[key] = c1.mix(c0, ease).rgbString(); 9692 continue; 9693 } 9694 } 9695 } else if (type === 'number' && isFinite(origin) && isFinite(target)) { 9696 view[key] = origin + (target - origin) * ease; 9697 continue; 9698 } 9699 } 9700 9701 view[key] = target; 9702 } 9703} 9704 9705var Element = function(configuration) { 9706 helpers.extend(this, configuration); 9707 this.initialize.apply(this, arguments); 9708}; 9709 9710helpers.extend(Element.prototype, { 9711 9712 initialize: function() { 9713 this.hidden = false; 9714 }, 9715 9716 pivot: function() { 9717 var me = this; 9718 if (!me._view) { 9719 me._view = helpers.clone(me._model); 9720 } 9721 me._start = {}; 9722 return me; 9723 }, 9724 9725 transition: function(ease) { 9726 var me = this; 9727 var model = me._model; 9728 var start = me._start; 9729 var view = me._view; 9730 9731 // No animation -> No Transition 9732 if (!model || ease === 1) { 9733 me._view = model; 9734 me._start = null; 9735 return me; 9736 } 9737 9738 if (!view) { 9739 view = me._view = {}; 9740 } 9741 9742 if (!start) { 9743 start = me._start = {}; 9744 } 9745 9746 interpolate(start, view, model, ease); 9747 9748 return me; 9749 }, 9750 9751 tooltipPosition: function() { 9752 return { 9753 x: this._model.x, 9754 y: this._model.y 9755 }; 9756 }, 9757 9758 hasValue: function() { 9759 return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); 9760 } 9761}); 9762 9763Element.extend = helpers.inherits; 9764 9765module.exports = Element; 9766 9767},{"2":2,"45":45}],27:[function(require,module,exports){ 9768/* global window: false */ 9769/* global document: false */ 9770'use strict'; 9771 9772var color = require(2); 9773var defaults = require(25); 9774var helpers = require(45); 9775 9776module.exports = function(Chart) { 9777 9778 // -- Basic js utility methods 9779 9780 helpers.configMerge = function(/* objects ... */) { 9781 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { 9782 merger: function(key, target, source, options) { 9783 var tval = target[key] || {}; 9784 var sval = source[key]; 9785 9786 if (key === 'scales') { 9787 // scale config merging is complex. Add our own function here for that 9788 target[key] = helpers.scaleMerge(tval, sval); 9789 } else if (key === 'scale') { 9790 // used in polar area & radar charts since there is only one scale 9791 target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]); 9792 } else { 9793 helpers._merger(key, target, source, options); 9794 } 9795 } 9796 }); 9797 }; 9798 9799 helpers.scaleMerge = function(/* objects ... */) { 9800 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { 9801 merger: function(key, target, source, options) { 9802 if (key === 'xAxes' || key === 'yAxes') { 9803 var slen = source[key].length; 9804 var i, type, scale; 9805 9806 if (!target[key]) { 9807 target[key] = []; 9808 } 9809 9810 for (i = 0; i < slen; ++i) { 9811 scale = source[key][i]; 9812 type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); 9813 9814 if (i >= target[key].length) { 9815 target[key].push({}); 9816 } 9817 9818 if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { 9819 // new/untyped scale or type changed: let's apply the new defaults 9820 // then merge source scale to correctly overwrite the defaults. 9821 helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]); 9822 } else { 9823 // scales type are the same 9824 helpers.merge(target[key][i], scale); 9825 } 9826 } 9827 } else { 9828 helpers._merger(key, target, source, options); 9829 } 9830 } 9831 }); 9832 }; 9833 9834 helpers.where = function(collection, filterCallback) { 9835 if (helpers.isArray(collection) && Array.prototype.filter) { 9836 return collection.filter(filterCallback); 9837 } 9838 var filtered = []; 9839 9840 helpers.each(collection, function(item) { 9841 if (filterCallback(item)) { 9842 filtered.push(item); 9843 } 9844 }); 9845 9846 return filtered; 9847 }; 9848 helpers.findIndex = Array.prototype.findIndex ? 9849 function(array, callback, scope) { 9850 return array.findIndex(callback, scope); 9851 } : 9852 function(array, callback, scope) { 9853 scope = scope === undefined ? array : scope; 9854 for (var i = 0, ilen = array.length; i < ilen; ++i) { 9855 if (callback.call(scope, array[i], i, array)) { 9856 return i; 9857 } 9858 } 9859 return -1; 9860 }; 9861 helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { 9862 // Default to start of the array 9863 if (helpers.isNullOrUndef(startIndex)) { 9864 startIndex = -1; 9865 } 9866 for (var i = startIndex + 1; i < arrayToSearch.length; i++) { 9867 var currentItem = arrayToSearch[i]; 9868 if (filterCallback(currentItem)) { 9869 return currentItem; 9870 } 9871 } 9872 }; 9873 helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { 9874 // Default to end of the array 9875 if (helpers.isNullOrUndef(startIndex)) { 9876 startIndex = arrayToSearch.length; 9877 } 9878 for (var i = startIndex - 1; i >= 0; i--) { 9879 var currentItem = arrayToSearch[i]; 9880 if (filterCallback(currentItem)) { 9881 return currentItem; 9882 } 9883 } 9884 }; 9885 9886 // -- Math methods 9887 helpers.isNumber = function(n) { 9888 return !isNaN(parseFloat(n)) && isFinite(n); 9889 }; 9890 helpers.almostEquals = function(x, y, epsilon) { 9891 return Math.abs(x - y) < epsilon; 9892 }; 9893 helpers.almostWhole = function(x, epsilon) { 9894 var rounded = Math.round(x); 9895 return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); 9896 }; 9897 helpers.max = function(array) { 9898 return array.reduce(function(max, value) { 9899 if (!isNaN(value)) { 9900 return Math.max(max, value); 9901 } 9902 return max; 9903 }, Number.NEGATIVE_INFINITY); 9904 }; 9905 helpers.min = function(array) { 9906 return array.reduce(function(min, value) { 9907 if (!isNaN(value)) { 9908 return Math.min(min, value); 9909 } 9910 return min; 9911 }, Number.POSITIVE_INFINITY); 9912 }; 9913 helpers.sign = Math.sign ? 9914 function(x) { 9915 return Math.sign(x); 9916 } : 9917 function(x) { 9918 x = +x; // convert to a number 9919 if (x === 0 || isNaN(x)) { 9920 return x; 9921 } 9922 return x > 0 ? 1 : -1; 9923 }; 9924 helpers.log10 = Math.log10 ? 9925 function(x) { 9926 return Math.log10(x); 9927 } : 9928 function(x) { 9929 var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. 9930 // Check for whole powers of 10, 9931 // which due to floating point rounding error should be corrected. 9932 var powerOf10 = Math.round(exponent); 9933 var isPowerOf10 = x === Math.pow(10, powerOf10); 9934 9935 return isPowerOf10 ? powerOf10 : exponent; 9936 }; 9937 helpers.toRadians = function(degrees) { 9938 return degrees * (Math.PI / 180); 9939 }; 9940 helpers.toDegrees = function(radians) { 9941 return radians * (180 / Math.PI); 9942 }; 9943 // Gets the angle from vertical upright to the point about a centre. 9944 helpers.getAngleFromPoint = function(centrePoint, anglePoint) { 9945 var distanceFromXCenter = anglePoint.x - centrePoint.x; 9946 var distanceFromYCenter = anglePoint.y - centrePoint.y; 9947 var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); 9948 9949 var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); 9950 9951 if (angle < (-0.5 * Math.PI)) { 9952 angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] 9953 } 9954 9955 return { 9956 angle: angle, 9957 distance: radialDistanceFromCenter 9958 }; 9959 }; 9960 helpers.distanceBetweenPoints = function(pt1, pt2) { 9961 return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); 9962 }; 9963 helpers.aliasPixel = function(pixelWidth) { 9964 return (pixelWidth % 2 === 0) ? 0 : 0.5; 9965 }; 9966 helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { 9967 // Props to Rob Spencer at scaled innovation for his post on splining between points 9968 // http://scaledinnovation.com/analytics/splines/aboutSplines.html 9969 9970 // This function must also respect "skipped" points 9971 9972 var previous = firstPoint.skip ? middlePoint : firstPoint; 9973 var current = middlePoint; 9974 var next = afterPoint.skip ? middlePoint : afterPoint; 9975 9976 var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); 9977 var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); 9978 9979 var s01 = d01 / (d01 + d12); 9980 var s12 = d12 / (d01 + d12); 9981 9982 // If all points are the same, s01 & s02 will be inf 9983 s01 = isNaN(s01) ? 0 : s01; 9984 s12 = isNaN(s12) ? 0 : s12; 9985 9986 var fa = t * s01; // scaling factor for triangle Ta 9987 var fb = t * s12; 9988 9989 return { 9990 previous: { 9991 x: current.x - fa * (next.x - previous.x), 9992 y: current.y - fa * (next.y - previous.y) 9993 }, 9994 next: { 9995 x: current.x + fb * (next.x - previous.x), 9996 y: current.y + fb * (next.y - previous.y) 9997 } 9998 }; 9999 }; 10000 helpers.EPSILON = Number.EPSILON || 1e-14; 10001 helpers.splineCurveMonotone = function(points) { 10002 // This function calculates Bézier control points in a similar way than |splineCurve|, 10003 // but preserves monotonicity of the provided data and ensures no local extremums are added 10004 // between the dataset discrete points due to the interpolation. 10005 // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation 10006 10007 var pointsWithTangents = (points || []).map(function(point) { 10008 return { 10009 model: point._model, 10010 deltaK: 0, 10011 mK: 0 10012 }; 10013 }); 10014 10015 // Calculate slopes (deltaK) and initialize tangents (mK) 10016 var pointsLen = pointsWithTangents.length; 10017 var i, pointBefore, pointCurrent, pointAfter; 10018 for (i = 0; i < pointsLen; ++i) { 10019 pointCurrent = pointsWithTangents[i]; 10020 if (pointCurrent.model.skip) { 10021 continue; 10022 } 10023 10024 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; 10025 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; 10026 if (pointAfter && !pointAfter.model.skip) { 10027 var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); 10028 10029 // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 10030 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; 10031 } 10032 10033 if (!pointBefore || pointBefore.model.skip) { 10034 pointCurrent.mK = pointCurrent.deltaK; 10035 } else if (!pointAfter || pointAfter.model.skip) { 10036 pointCurrent.mK = pointBefore.deltaK; 10037 } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { 10038 pointCurrent.mK = 0; 10039 } else { 10040 pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; 10041 } 10042 } 10043 10044 // Adjust tangents to ensure monotonic properties 10045 var alphaK, betaK, tauK, squaredMagnitude; 10046 for (i = 0; i < pointsLen - 1; ++i) { 10047 pointCurrent = pointsWithTangents[i]; 10048 pointAfter = pointsWithTangents[i + 1]; 10049 if (pointCurrent.model.skip || pointAfter.model.skip) { 10050 continue; 10051 } 10052 10053 if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { 10054 pointCurrent.mK = pointAfter.mK = 0; 10055 continue; 10056 } 10057 10058 alphaK = pointCurrent.mK / pointCurrent.deltaK; 10059 betaK = pointAfter.mK / pointCurrent.deltaK; 10060 squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); 10061 if (squaredMagnitude <= 9) { 10062 continue; 10063 } 10064 10065 tauK = 3 / Math.sqrt(squaredMagnitude); 10066 pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; 10067 pointAfter.mK = betaK * tauK * pointCurrent.deltaK; 10068 } 10069 10070 // Compute control points 10071 var deltaX; 10072 for (i = 0; i < pointsLen; ++i) { 10073 pointCurrent = pointsWithTangents[i]; 10074 if (pointCurrent.model.skip) { 10075 continue; 10076 } 10077 10078 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; 10079 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; 10080 if (pointBefore && !pointBefore.model.skip) { 10081 deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; 10082 pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; 10083 pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; 10084 } 10085 if (pointAfter && !pointAfter.model.skip) { 10086 deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; 10087 pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; 10088 pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; 10089 } 10090 } 10091 }; 10092 helpers.nextItem = function(collection, index, loop) { 10093 if (loop) { 10094 return index >= collection.length - 1 ? collection[0] : collection[index + 1]; 10095 } 10096 return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; 10097 }; 10098 helpers.previousItem = function(collection, index, loop) { 10099 if (loop) { 10100 return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; 10101 } 10102 return index <= 0 ? collection[0] : collection[index - 1]; 10103 }; 10104 // Implementation of the nice number algorithm used in determining where axis labels will go 10105 helpers.niceNum = function(range, round) { 10106 var exponent = Math.floor(helpers.log10(range)); 10107 var fraction = range / Math.pow(10, exponent); 10108 var niceFraction; 10109 10110 if (round) { 10111 if (fraction < 1.5) { 10112 niceFraction = 1; 10113 } else if (fraction < 3) { 10114 niceFraction = 2; 10115 } else if (fraction < 7) { 10116 niceFraction = 5; 10117 } else { 10118 niceFraction = 10; 10119 } 10120 } else if (fraction <= 1.0) { 10121 niceFraction = 1; 10122 } else if (fraction <= 2) { 10123 niceFraction = 2; 10124 } else if (fraction <= 5) { 10125 niceFraction = 5; 10126 } else { 10127 niceFraction = 10; 10128 } 10129 10130 return niceFraction * Math.pow(10, exponent); 10131 }; 10132 // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ 10133 helpers.requestAnimFrame = (function() { 10134 if (typeof window === 'undefined') { 10135 return function(callback) { 10136 callback(); 10137 }; 10138 } 10139 return window.requestAnimationFrame || 10140 window.webkitRequestAnimationFrame || 10141 window.mozRequestAnimationFrame || 10142 window.oRequestAnimationFrame || 10143 window.msRequestAnimationFrame || 10144 function(callback) { 10145 return window.setTimeout(callback, 1000 / 60); 10146 }; 10147 }()); 10148 // -- DOM methods 10149 helpers.getRelativePosition = function(evt, chart) { 10150 var mouseX, mouseY; 10151 var e = evt.originalEvent || evt; 10152 var canvas = evt.currentTarget || evt.srcElement; 10153 var boundingRect = canvas.getBoundingClientRect(); 10154 10155 var touches = e.touches; 10156 if (touches && touches.length > 0) { 10157 mouseX = touches[0].clientX; 10158 mouseY = touches[0].clientY; 10159 10160 } else { 10161 mouseX = e.clientX; 10162 mouseY = e.clientY; 10163 } 10164 10165 // Scale mouse coordinates into canvas coordinates 10166 // by following the pattern laid out by 'jerryj' in the comments of 10167 // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ 10168 var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); 10169 var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); 10170 var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); 10171 var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); 10172 var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; 10173 var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; 10174 10175 // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However 10176 // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here 10177 mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); 10178 mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); 10179 10180 return { 10181 x: mouseX, 10182 y: mouseY 10183 }; 10184 10185 }; 10186 10187 // Private helper function to convert max-width/max-height values that may be percentages into a number 10188 function parseMaxStyle(styleValue, node, parentProperty) { 10189 var valueInPixels; 10190 if (typeof styleValue === 'string') { 10191 valueInPixels = parseInt(styleValue, 10); 10192 10193 if (styleValue.indexOf('%') !== -1) { 10194 // percentage * size in dimension 10195 valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; 10196 } 10197 } else { 10198 valueInPixels = styleValue; 10199 } 10200 10201 return valueInPixels; 10202 } 10203 10204 /** 10205 * Returns if the given value contains an effective constraint. 10206 * @private 10207 */ 10208 function isConstrainedValue(value) { 10209 return value !== undefined && value !== null && value !== 'none'; 10210 } 10211 10212 // Private helper to get a constraint dimension 10213 // @param domNode : the node to check the constraint on 10214 // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) 10215 // @param percentageProperty : property of parent to use when calculating width as a percentage 10216 // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser 10217 function getConstraintDimension(domNode, maxStyle, percentageProperty) { 10218 var view = document.defaultView; 10219 var parentNode = domNode.parentNode; 10220 var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; 10221 var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; 10222 var hasCNode = isConstrainedValue(constrainedNode); 10223 var hasCContainer = isConstrainedValue(constrainedContainer); 10224 var infinity = Number.POSITIVE_INFINITY; 10225 10226 if (hasCNode || hasCContainer) { 10227 return Math.min( 10228 hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, 10229 hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); 10230 } 10231 10232 return 'none'; 10233 } 10234 // returns Number or undefined if no constraint 10235 helpers.getConstraintWidth = function(domNode) { 10236 return getConstraintDimension(domNode, 'max-width', 'clientWidth'); 10237 }; 10238 // returns Number or undefined if no constraint 10239 helpers.getConstraintHeight = function(domNode) { 10240 return getConstraintDimension(domNode, 'max-height', 'clientHeight'); 10241 }; 10242 helpers.getMaximumWidth = function(domNode) { 10243 var container = domNode.parentNode; 10244 if (!container) { 10245 return domNode.clientWidth; 10246 } 10247 10248 var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); 10249 var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); 10250 var w = container.clientWidth - paddingLeft - paddingRight; 10251 var cw = helpers.getConstraintWidth(domNode); 10252 return isNaN(cw) ? w : Math.min(w, cw); 10253 }; 10254 helpers.getMaximumHeight = function(domNode) { 10255 var container = domNode.parentNode; 10256 if (!container) { 10257 return domNode.clientHeight; 10258 } 10259 10260 var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); 10261 var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); 10262 var h = container.clientHeight - paddingTop - paddingBottom; 10263 var ch = helpers.getConstraintHeight(domNode); 10264 return isNaN(ch) ? h : Math.min(h, ch); 10265 }; 10266 helpers.getStyle = function(el, property) { 10267 return el.currentStyle ? 10268 el.currentStyle[property] : 10269 document.defaultView.getComputedStyle(el, null).getPropertyValue(property); 10270 }; 10271 helpers.retinaScale = function(chart, forceRatio) { 10272 var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1; 10273 if (pixelRatio === 1) { 10274 return; 10275 } 10276 10277 var canvas = chart.canvas; 10278 var height = chart.height; 10279 var width = chart.width; 10280 10281 canvas.height = height * pixelRatio; 10282 canvas.width = width * pixelRatio; 10283 chart.ctx.scale(pixelRatio, pixelRatio); 10284 10285 // If no style has been set on the canvas, the render size is used as display size, 10286 // making the chart visually bigger, so let's enforce it to the "correct" values. 10287 // See https://github.com/chartjs/Chart.js/issues/3575 10288 if (!canvas.style.height && !canvas.style.width) { 10289 canvas.style.height = height + 'px'; 10290 canvas.style.width = width + 'px'; 10291 } 10292 }; 10293 // -- Canvas methods 10294 helpers.fontString = function(pixelSize, fontStyle, fontFamily) { 10295 return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; 10296 }; 10297 helpers.longestText = function(ctx, font, arrayOfThings, cache) { 10298 cache = cache || {}; 10299 var data = cache.data = cache.data || {}; 10300 var gc = cache.garbageCollect = cache.garbageCollect || []; 10301 10302 if (cache.font !== font) { 10303 data = cache.data = {}; 10304 gc = cache.garbageCollect = []; 10305 cache.font = font; 10306 } 10307 10308 ctx.font = font; 10309 var longest = 0; 10310 helpers.each(arrayOfThings, function(thing) { 10311 // Undefined strings and arrays should not be measured 10312 if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { 10313 longest = helpers.measureText(ctx, data, gc, longest, thing); 10314 } else if (helpers.isArray(thing)) { 10315 // if it is an array lets measure each element 10316 // to do maybe simplify this function a bit so we can do this more recursively? 10317 helpers.each(thing, function(nestedThing) { 10318 // Undefined strings and arrays should not be measured 10319 if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { 10320 longest = helpers.measureText(ctx, data, gc, longest, nestedThing); 10321 } 10322 }); 10323 } 10324 }); 10325 10326 var gcLen = gc.length / 2; 10327 if (gcLen > arrayOfThings.length) { 10328 for (var i = 0; i < gcLen; i++) { 10329 delete data[gc[i]]; 10330 } 10331 gc.splice(0, gcLen); 10332 } 10333 return longest; 10334 }; 10335 helpers.measureText = function(ctx, data, gc, longest, string) { 10336 var textWidth = data[string]; 10337 if (!textWidth) { 10338 textWidth = data[string] = ctx.measureText(string).width; 10339 gc.push(string); 10340 } 10341 if (textWidth > longest) { 10342 longest = textWidth; 10343 } 10344 return longest; 10345 }; 10346 helpers.numberOfLabelLines = function(arrayOfThings) { 10347 var numberOfLines = 1; 10348 helpers.each(arrayOfThings, function(thing) { 10349 if (helpers.isArray(thing)) { 10350 if (thing.length > numberOfLines) { 10351 numberOfLines = thing.length; 10352 } 10353 } 10354 }); 10355 return numberOfLines; 10356 }; 10357 10358 helpers.color = !color ? 10359 function(value) { 10360 console.error('Color.js not found!'); 10361 return value; 10362 } : 10363 function(value) { 10364 /* global CanvasGradient */ 10365 if (value instanceof CanvasGradient) { 10366 value = defaults.global.defaultColor; 10367 } 10368 10369 return color(value); 10370 }; 10371 10372 helpers.getHoverColor = function(colorValue) { 10373 /* global CanvasPattern */ 10374 return (colorValue instanceof CanvasPattern) ? 10375 colorValue : 10376 helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); 10377 }; 10378}; 10379 10380},{"2":2,"25":25,"45":45}],28:[function(require,module,exports){ 10381'use strict'; 10382 10383var helpers = require(45); 10384 10385/** 10386 * Helper function to get relative position for an event 10387 * @param {Event|IEvent} event - The event to get the position for 10388 * @param {Chart} chart - The chart 10389 * @returns {Point} the event position 10390 */ 10391function getRelativePosition(e, chart) { 10392 if (e.native) { 10393 return { 10394 x: e.x, 10395 y: e.y 10396 }; 10397 } 10398 10399 return helpers.getRelativePosition(e, chart); 10400} 10401 10402/** 10403 * Helper function to traverse all of the visible elements in the chart 10404 * @param chart {chart} the chart 10405 * @param handler {Function} the callback to execute for each visible item 10406 */ 10407function parseVisibleItems(chart, handler) { 10408 var datasets = chart.data.datasets; 10409 var meta, i, j, ilen, jlen; 10410 10411 for (i = 0, ilen = datasets.length; i < ilen; ++i) { 10412 if (!chart.isDatasetVisible(i)) { 10413 continue; 10414 } 10415 10416 meta = chart.getDatasetMeta(i); 10417 for (j = 0, jlen = meta.data.length; j < jlen; ++j) { 10418 var element = meta.data[j]; 10419 if (!element._view.skip) { 10420 handler(element); 10421 } 10422 } 10423 } 10424} 10425 10426/** 10427 * Helper function to get the items that intersect the event position 10428 * @param items {ChartElement[]} elements to filter 10429 * @param position {Point} the point to be nearest to 10430 * @return {ChartElement[]} the nearest items 10431 */ 10432function getIntersectItems(chart, position) { 10433 var elements = []; 10434 10435 parseVisibleItems(chart, function(element) { 10436 if (element.inRange(position.x, position.y)) { 10437 elements.push(element); 10438 } 10439 }); 10440 10441 return elements; 10442} 10443 10444/** 10445 * Helper function to get the items nearest to the event position considering all visible items in teh chart 10446 * @param chart {Chart} the chart to look at elements from 10447 * @param position {Point} the point to be nearest to 10448 * @param intersect {Boolean} if true, only consider items that intersect the position 10449 * @param distanceMetric {Function} function to provide the distance between points 10450 * @return {ChartElement[]} the nearest items 10451 */ 10452function getNearestItems(chart, position, intersect, distanceMetric) { 10453 var minDistance = Number.POSITIVE_INFINITY; 10454 var nearestItems = []; 10455 10456 parseVisibleItems(chart, function(element) { 10457 if (intersect && !element.inRange(position.x, position.y)) { 10458 return; 10459 } 10460 10461 var center = element.getCenterPoint(); 10462 var distance = distanceMetric(position, center); 10463 10464 if (distance < minDistance) { 10465 nearestItems = [element]; 10466 minDistance = distance; 10467 } else if (distance === minDistance) { 10468 // Can have multiple items at the same distance in which case we sort by size 10469 nearestItems.push(element); 10470 } 10471 }); 10472 10473 return nearestItems; 10474} 10475 10476/** 10477 * Get a distance metric function for two points based on the 10478 * axis mode setting 10479 * @param {String} axis the axis mode. x|y|xy 10480 */ 10481function getDistanceMetricForAxis(axis) { 10482 var useX = axis.indexOf('x') !== -1; 10483 var useY = axis.indexOf('y') !== -1; 10484 10485 return function(pt1, pt2) { 10486 var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; 10487 var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; 10488 return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); 10489 }; 10490} 10491 10492function indexMode(chart, e, options) { 10493 var position = getRelativePosition(e, chart); 10494 // Default axis for index mode is 'x' to match old behaviour 10495 options.axis = options.axis || 'x'; 10496 var distanceMetric = getDistanceMetricForAxis(options.axis); 10497 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); 10498 var elements = []; 10499 10500 if (!items.length) { 10501 return []; 10502 } 10503 10504 chart.data.datasets.forEach(function(dataset, datasetIndex) { 10505 if (chart.isDatasetVisible(datasetIndex)) { 10506 var meta = chart.getDatasetMeta(datasetIndex); 10507 var element = meta.data[items[0]._index]; 10508 10509 // don't count items that are skipped (null data) 10510 if (element && !element._view.skip) { 10511 elements.push(element); 10512 } 10513 } 10514 }); 10515 10516 return elements; 10517} 10518 10519/** 10520 * @interface IInteractionOptions 10521 */ 10522/** 10523 * If true, only consider items that intersect the point 10524 * @name IInterfaceOptions#boolean 10525 * @type Boolean 10526 */ 10527 10528/** 10529 * Contains interaction related functions 10530 * @namespace Chart.Interaction 10531 */ 10532module.exports = { 10533 // Helper function for different modes 10534 modes: { 10535 single: function(chart, e) { 10536 var position = getRelativePosition(e, chart); 10537 var elements = []; 10538 10539 parseVisibleItems(chart, function(element) { 10540 if (element.inRange(position.x, position.y)) { 10541 elements.push(element); 10542 return elements; 10543 } 10544 }); 10545 10546 return elements.slice(0, 1); 10547 }, 10548 10549 /** 10550 * @function Chart.Interaction.modes.label 10551 * @deprecated since version 2.4.0 10552 * @todo remove at version 3 10553 * @private 10554 */ 10555 label: indexMode, 10556 10557 /** 10558 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something 10559 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item 10560 * @function Chart.Interaction.modes.index 10561 * @since v2.4.0 10562 * @param chart {chart} the chart we are returning items from 10563 * @param e {Event} the event we are find things at 10564 * @param options {IInteractionOptions} options to use during interaction 10565 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10566 */ 10567 index: indexMode, 10568 10569 /** 10570 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something 10571 * If the options.intersect is false, we find the nearest item and return the items in that dataset 10572 * @function Chart.Interaction.modes.dataset 10573 * @param chart {chart} the chart we are returning items from 10574 * @param e {Event} the event we are find things at 10575 * @param options {IInteractionOptions} options to use during interaction 10576 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10577 */
10578 dataset: function(chart, e, options) { 10579 var position = getRelativePosition(e, chart); 10580 options.axis = options.axis || 'xy'; 10581 var distanceMetric = getDistanceMetricForAxis(options.axis); 10582 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); 10583 10584 if (items.length > 0) { 10585 items = chart.getDatasetMeta(items[0]._datasetIndex).data; 10586 } 10587 10588 return items; 10589 }, 10590 10591 /** 10592 * @function Chart.Interaction.modes.x-axis 10593 * @deprecated since version 2.4.0. Use index mode and intersect == true 10594 * @todo remove at version 3 10595 * @private 10596 */ 10597 'x-axis': function(chart, e) { 10598 return indexMode(chart, e, {intersect: false}); 10599 }, 10600 10601 /** 10602 * Point mode returns all elements that hit test based on the event position 10603 * of the event 10604 * @function Chart.Interaction.modes.intersect 10605 * @param chart {chart} the chart we are returning items from 10606 * @param e {Event} the event we are find things at 10607 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10608 */ 10609 point: function(chart, e) { 10610 var position = getRelativePosition(e, chart); 10611 return getIntersectItems(chart, position); 10612 }, 10613 10614 /** 10615 * nearest mode returns the element closest to the point 10616 * @function Chart.Interaction.modes.intersect 10617 * @param chart {chart} the chart we are returning items from 10618 * @param e {Event} the event we are find things at 10619 * @param options {IInteractionOptions} options to use 10620 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10621 */ 10622 nearest: function(chart, e, options) { 10623 var position = getRelativePosition(e, chart); 10624 options.axis = options.axis || 'xy'; 10625 var distanceMetric = getDistanceMetricForAxis(options.axis); 10626 var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); 10627 10628 // We have multiple items at the same distance from the event. Now sort by smallest 10629 if (nearestItems.length > 1) { 10630 nearestItems.sort(function(a, b) { 10631 var sizeA = a.getArea(); 10632 var sizeB = b.getArea(); 10633 var ret = sizeA - sizeB; 10634 10635 if (ret === 0) { 10636 // if equal sort by dataset index 10637 ret = a._datasetIndex - b._datasetIndex; 10638 } 10639 10640 return ret; 10641 }); 10642 } 10643 10644 // Return only 1 item 10645 return nearestItems.slice(0, 1); 10646 }, 10647 10648 /** 10649 * x mode returns the elements that hit-test at the current x coordinate 10650 * @function Chart.Interaction.modes.x 10651 * @param chart {chart} the chart we are returning items from 10652 * @param e {Event} the event we are find things at 10653 * @param options {IInteractionOptions} options to use 10654 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10655 */ 10656 x: function(chart, e, options) { 10657 var position = getRelativePosition(e, chart); 10658 var items = []; 10659 var intersectsItem = false; 10660 10661 parseVisibleItems(chart, function(element) { 10662 if (element.inXRange(position.x)) { 10663 items.push(element); 10664 } 10665 10666 if (element.inRange(position.x, position.y)) { 10667 intersectsItem = true; 10668 } 10669 }); 10670 10671 // If we want to trigger on an intersect and we don't have any items 10672 // that intersect the position, return nothing 10673 if (options.intersect && !intersectsItem) { 10674 items = []; 10675 } 10676 return items; 10677 }, 10678 10679 /** 10680 * y mode returns the elements that hit-test at the current y coordinate 10681 * @function Chart.Interaction.modes.y 10682 * @param chart {chart} the chart we are returning items from 10683 * @param e {Event} the event we are find things at 10684 * @param options {IInteractionOptions} options to use 10685 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10686 */ 10687 y: function(chart, e, options) { 10688 var position = getRelativePosition(e, chart); 10689 var items = []; 10690 var intersectsItem = false; 10691 10692 parseVisibleItems(chart, function(element) { 10693 if (element.inYRange(position.y)) { 10694 items.push(element); 10695 } 10696 10697 if (element.inRange(position.x, position.y)) { 10698 intersectsItem = true; 10699 } 10700 }); 10701 10702 // If we want to trigger on an intersect and we don't have any items 10703 // that intersect the position, return nothing 10704 if (options.intersect && !intersectsItem) { 10705 items = []; 10706 } 10707 return items; 10708 } 10709 } 10710}; 10711 10712},{"45":45}],29:[function(require,module,exports){ 10713'use strict'; 10714 10715var defaults = require(25); 10716 10717defaults._set('global', { 10718 responsive: true, 10719 responsiveAnimationDuration: 0, 10720 maintainAspectRatio: true, 10721 events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], 10722 hover: { 10723 onHover: null, 10724 mode: 'nearest', 10725 intersect: true, 10726 animationDuration: 400 10727 }, 10728 onClick: null, 10729 defaultColor: 'rgba(0,0,0,0.1)', 10730 defaultFontColor: '#666', 10731 defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", 10732 defaultFontSize: 12, 10733 defaultFontStyle: 'normal', 10734 showLines: true, 10735 10736 // Element defaults defined in element extensions 10737 elements: {}, 10738 10739 // Layout options such as padding 10740 layout: { 10741 padding: { 10742 top: 0, 10743 right: 0, 10744 bottom: 0, 10745 left: 0 10746 } 10747 } 10748}); 10749 10750module.exports = function() { 10751 10752 // Occupy the global variable of Chart, and create a simple base class 10753 var Chart = function(item, config) { 10754 this.construct(item, config); 10755 return this; 10756 }; 10757 10758 Chart.Chart = Chart; 10759 10760 return Chart; 10761}; 10762 10763},{"25":25}],30:[function(require,module,exports){ 10764'use strict'; 10765 10766var helpers = require(45); 10767 10768function filterByPosition(array, position) { 10769 return helpers.where(array, function(v) { 10770 return v.position === position; 10771 }); 10772} 10773 10774function sortByWeight(array, reverse) { 10775 array.forEach(function(v, i) { 10776 v._tmpIndex_ = i; 10777 return v; 10778 }); 10779 array.sort(function(a, b) { 10780 var v0 = reverse ? b : a; 10781 var v1 = reverse ? a : b; 10782 return v0.weight === v1.weight ? 10783 v0._tmpIndex_ - v1._tmpIndex_ : 10784 v0.weight - v1.weight; 10785 }); 10786 array.forEach(function(v) { 10787 delete v._tmpIndex_; 10788 }); 10789} 10790 10791/** 10792 * @interface ILayoutItem 10793 * @prop {String} position - The position of the item in the chart layout. Possible values are 10794 * 'left', 'top', 'right', 'bottom', and 'chartArea' 10795 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area 10796 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down 10797 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) 10798 * @prop {Function} update - Takes two parameters: width and height. Returns size of item 10799 * @prop {Function} getPadding - Returns an object with padding on the edges 10800 * @prop {Number} width - Width of item. Must be valid after update() 10801 * @prop {Number} height - Height of item. Must be valid after update() 10802 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update 10803 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update 10804 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update 10805 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update 10806 */ 10807 10808// The layout service is very self explanatory. It's responsible for the layout within a chart. 10809// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need 10810// It is this service's responsibility of carrying out that layout. 10811module.exports = { 10812 defaults: {}, 10813 10814 /** 10815 * Register a box to a chart. 10816 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. 10817 * @param {Chart} chart - the chart to use 10818 * @param {ILayoutItem} item - the item to add to be layed out 10819 */
10820 addBox: function(chart, item) { 10821 if (!chart.boxes) { 10822 chart.boxes = []; 10823 } 10824 10825 // initialize item with default values 10826 item.fullWidth = item.fullWidth || false; 10827 item.position = item.position || 'top'; 10828 item.weight = item.weight || 0; 10829 10830 chart.boxes.push(item); 10831 }, 10832 10833 /** 10834 * Remove a layoutItem from a chart 10835 * @param {Chart} chart - the chart to remove the box from 10836 * @param {Object} layoutItem - the item to remove from the layout 10837 */ 10838 removeBox: function(chart, layoutItem) { 10839 var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; 10840 if (index !== -1) { 10841 chart.boxes.splice(index, 1); 10842 } 10843 }, 10844 10845 /** 10846 * Sets (or updates) options on the given `item`. 10847 * @param {Chart} chart - the chart in which the item lives (or will be added to) 10848 * @param {Object} item - the item to configure with the given options 10849 * @param {Object} options - the new item options. 10850 */ 10851 configure: function(chart, item, options) { 10852 var props = ['fullWidth', 'position', 'weight']; 10853 var ilen = props.length; 10854 var i = 0; 10855 var prop; 10856 10857 for (; i < ilen; ++i) { 10858 prop = props[i]; 10859 if (options.hasOwnProperty(prop)) { 10860 item[prop] = options[prop]; 10861 } 10862 } 10863 }, 10864 10865 /** 10866 * Fits boxes of the given chart into the given size by having each box measure itself 10867 * then running a fitting algorithm 10868 * @param {Chart} chart - the chart 10869 * @param {Number} width - the width to fit into 10870 * @param {Number} height - the height to fit into 10871 */ 10872 update: function(chart, width, height) { 10873 if (!chart) { 10874 return; 10875 } 10876 10877 var layoutOptions = chart.options.layout || {}; 10878 var padding = helpers.options.toPadding(layoutOptions.padding); 10879 var leftPadding = padding.left; 10880 var rightPadding = padding.right; 10881 var topPadding = padding.top; 10882 var bottomPadding = padding.bottom; 10883 10884 var leftBoxes = filterByPosition(chart.boxes, 'left'); 10885 var rightBoxes = filterByPosition(chart.boxes, 'right'); 10886 var topBoxes = filterByPosition(chart.boxes, 'top'); 10887 var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); 10888 var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); 10889 10890 // Sort boxes by weight. A higher weight is further away from the chart area 10891 sortByWeight(leftBoxes, true); 10892 sortByWeight(rightBoxes, false); 10893 sortByWeight(topBoxes, true); 10894 sortByWeight(bottomBoxes, false); 10895 10896 // Essentially we now have any number of boxes on each of the 4 sides. 10897 // Our canvas looks like the following. 10898 // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and 10899 // B1 is the bottom axis 10900 // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays 10901 // These locations are single-box locations only, when trying to register a chartArea location that is already taken, 10902 // an error will be thrown. 10903 // 10904 // |----------------------------------------------------| 10905 // | T1 (Full Width) | 10906 // |----------------------------------------------------| 10907 // | | | T2 | | 10908 // | |----|-------------------------------------|----| 10909 // | | | C1 | | C2 | | 10910 // | | |----| |----| | 10911 // | | | | | 10912 // | L1 | L2 | ChartArea (C0) | R1 | 10913 // | | | | | 10914 // | | |----| |----| | 10915 // | | | C3 | | C4 | | 10916 // | |----|-------------------------------------|----| 10917 // | | | B1 | | 10918 // |----------------------------------------------------| 10919 // | B2 (Full Width) | 10920 // |----------------------------------------------------| 10921 // 10922 // What we do to find the best sizing, we do the following 10923 // 1. Determine the minimum size of the chart area. 10924 // 2. Split the remaining width equally between each vertical axis 10925 // 3. Split the remaining height equally between each horizontal axis 10926 // 4. Give each layout the maximum size it can be. The layout will return it's minimum size 10927 // 5. Adjust the sizes of each axis based on it's minimum reported size. 10928 // 6. Refit each axis 10929 // 7. Position each axis in the final location 10930 // 8. Tell the chart the final location of the chart area 10931 // 9. Tell any axes that overlay the chart area the positions of the chart area 10932 10933 // Step 1 10934 var chartWidth = width - leftPadding - rightPadding;
10935 var chartHeight = height - topPadding - bottomPadding; 10936 var chartAreaWidth = chartWidth / 2; // min 50% 10937 var chartAreaHeight = chartHeight / 2; // min 50% 10938 10939 // Step 2 10940 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); 10941 10942 // Step 3 10943 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); 10944 10945 // Step 4 10946 var maxChartAreaWidth = chartWidth; 10947 var maxChartAreaHeight = chartHeight; 10948 var minBoxSizes = []; 10949 10950 function getMinimumBoxSize(box) { 10951 var minSize; 10952 var isHorizontal = box.isHorizontal(); 10953 10954 if (isHorizontal) { 10955 minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); 10956 maxChartAreaHeight -= minSize.height; 10957 } else { 10958 minSize = box.update(verticalBoxWidth, maxChartAreaHeight); 10959 maxChartAreaWidth -= minSize.width; 10960 } 10961 10962 minBoxSizes.push({ 10963 horizontal: isHorizontal, 10964 minSize: minSize, 10965 box: box, 10966 }); 10967 } 10968 10969 helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); 10970 10971 // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) 10972 var maxHorizontalLeftPadding = 0; 10973 var maxHorizontalRightPadding = 0; 10974 var maxVerticalTopPadding = 0; 10975 var maxVerticalBottomPadding = 0; 10976 10977 helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { 10978 if (horizontalBox.getPadding) { 10979 var boxPadding = horizontalBox.getPadding(); 10980 maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); 10981 maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); 10982 } 10983 }); 10984 10985 helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { 10986 if (verticalBox.getPadding) { 10987 var boxPadding = verticalBox.getPadding(); 10988 maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); 10989 maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); 10990 } 10991 }); 10992 10993 // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could 10994 // be if the axes are drawn at their minimum sizes. 10995 // Steps 5 & 6 10996 var totalLeftBoxesWidth = leftPadding; 10997 var totalRightBoxesWidth = rightPadding; 10998 var totalTopBoxesHeight = topPadding; 10999 var totalBottomBoxesHeight = bottomPadding; 11000 11001 // Function to fit a box 11002 function fitBox(box) { 11003 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { 11004 return minBox.box === box; 11005 }); 11006 11007 if (minBoxSize) { 11008 if (box.isHorizontal()) { 11009 var scaleMargin = { 11010 left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), 11011 right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), 11012 top: 0, 11013 bottom: 0 11014 }; 11015 11016 // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends 11017 // on the margin. Sometimes they need to increase in size slightly 11018 box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); 11019 } else { 11020 box.update(minBoxSize.minSize.width, maxChartAreaHeight); 11021 } 11022 } 11023 } 11024 11025 // Update, and calculate the left and right margins for the horizontal boxes 11026 helpers.each(leftBoxes.concat(rightBoxes), fitBox); 11027 11028 helpers.each(leftBoxes, function(box) { 11029 totalLeftBoxesWidth += box.width; 11030 }); 11031 11032 helpers.each(rightBoxes, function(box) { 11033 totalRightBoxesWidth += box.width; 11034 }); 11035 11036 // Set the Left and Right margins for the horizontal boxes 11037 helpers.each(topBoxes.concat(bottomBoxes), fitBox); 11038 11039 // Figure out how much margin is on the top and bottom of the vertical boxes 11040 helpers.each(topBoxes, function(box) { 11041 totalTopBoxesHeight += box.height; 11042 }); 11043 11044 helpers.each(bottomBoxes, function(box) { 11045 totalBottomBoxesHeight += box.height; 11046 }); 11047 11048 function finalFitVerticalBox(box) { 11049 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { 11050 return minSize.box === box; 11051 }); 11052 11053 var scaleMargin = { 11054 left: 0, 11055 right: 0,
11056 top: totalTopBoxesHeight, 11057 bottom: totalBottomBoxesHeight 11058 }; 11059 11060 if (minBoxSize) { 11061 box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); 11062 } 11063 } 11064 11065 // Let the left layout know the final margin 11066 helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); 11067 11068 // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) 11069 totalLeftBoxesWidth = leftPadding; 11070 totalRightBoxesWidth = rightPadding; 11071 totalTopBoxesHeight = topPadding; 11072 totalBottomBoxesHeight = bottomPadding; 11073 11074 helpers.each(leftBoxes, function(box) { 11075 totalLeftBoxesWidth += box.width; 11076 }); 11077 11078 helpers.each(rightBoxes, function(box) { 11079 totalRightBoxesWidth += box.width; 11080 }); 11081 11082 helpers.each(topBoxes, function(box) { 11083 totalTopBoxesHeight += box.height; 11084 }); 11085 helpers.each(bottomBoxes, function(box) { 11086 totalBottomBoxesHeight += box.height; 11087 }); 11088 11089 // We may be adding some padding to account for rotated x axis labels 11090 var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); 11091 totalLeftBoxesWidth += leftPaddingAddition; 11092 totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); 11093 11094 var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); 11095 totalTopBoxesHeight += topPaddingAddition; 11096 totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); 11097 11098 // Figure out if our chart area changed. This would occur if the dataset layout label rotation 11099 // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do 11100 // without calling `fit` again 11101 var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; 11102 var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; 11103 11104 if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { 11105 helpers.each(leftBoxes, function(box) { 11106 box.height = newMaxChartAreaHeight; 11107 }); 11108 11109 helpers.each(rightBoxes, function(box) { 11110 box.height = newMaxChartAreaHeight; 11111 }); 11112 11113 helpers.each(topBoxes, function(box) { 11114 if (!box.fullWidth) { 11115 box.width = newMaxChartAreaWidth; 11116 } 11117 }); 11118 11119 helpers.each(bottomBoxes, function(box) { 11120 if (!box.fullWidth) { 11121 box.width = newMaxChartAreaWidth; 11122 } 11123 }); 11124 11125 maxChartAreaHeight = newMaxChartAreaHeight; 11126 maxChartAreaWidth = newMaxChartAreaWidth; 11127 } 11128 11129 // Step 7 - Position the boxes 11130 var left = leftPadding + leftPaddingAddition; 11131 var top = topPadding + topPaddingAddition; 11132 11133 function placeBox(box) { 11134 if (box.isHorizontal()) { 11135 box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; 11136 box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; 11137 box.top = top; 11138 box.bottom = top + box.height; 11139 11140 // Move to next point 11141 top = box.bottom; 11142 11143 } else { 11144 11145 box.left = left; 11146 box.right = left + box.width;
11147 box.top = totalTopBoxesHeight; 11148 box.bottom = totalTopBoxesHeight + maxChartAreaHeight; 11149 11150 // Move to next point 11151 left = box.right; 11152 } 11153 } 11154 11155 helpers.each(leftBoxes.concat(topBoxes), placeBox); 11156 11157 // Account for chart width and height 11158 left += maxChartAreaWidth; 11159 top += maxChartAreaHeight; 11160 11161 helpers.each(rightBoxes, placeBox); 11162 helpers.each(bottomBoxes, placeBox); 11163 11164 // Step 8 11165 chart.chartArea = { 11166 left: totalLeftBoxesWidth, 11167 top: totalTopBoxesHeight, 11168 right: totalLeftBoxesWidth + maxChartAreaWidth, 11169 bottom: totalTopBoxesHeight + maxChartAreaHeight 11170 }; 11171 11172 // Step 9 11173 helpers.each(chartAreaBoxes, function(box) { 11174 box.left = chart.chartArea.left; 11175 box.top = chart.chartArea.top; 11176 box.right = chart.chartArea.right; 11177 box.bottom = chart.chartArea.bottom; 11178 11179 box.update(maxChartAreaWidth, maxChartAreaHeight); 11180 }); 11181 } 11182}; 11183 11184},{"45":45}],31:[function(require,module,exports){ 11185'use strict'; 11186 11187var defaults = require(25); 11188var helpers = require(45); 11189 11190defaults._set('global', { 11191 plugins: {} 11192}); 11193 11194/** 11195 * The plugin service singleton 11196 * @namespace Chart.plugins 11197 * @since 2.1.0 11198 */ 11199module.exports = { 11200 /** 11201 * Globally registered plugins. 11202 * @private 11203 */ 11204 _plugins: [], 11205 11206 /** 11207 * This identifier is used to invalidate the descriptors cache attached to each chart 11208 * when a global plugin is registered or unregistered. In this case, the cache ID is 11209 * incremented and descriptors are regenerated during following API calls. 11210 * @private 11211 */ 11212 _cacheId: 0, 11213 11214 /** 11215 * Registers the given plugin(s) if not already registered. 11216 * @param {Array|Object} plugins plugin instance(s). 11217 */ 11218 register: function(plugins) { 11219 var p = this._plugins; 11220 ([]).concat(plugins).forEach(function(plugin) { 11221 if (p.indexOf(plugin) === -1) { 11222 p.push(plugin); 11223 } 11224 }); 11225 11226 this._cacheId++; 11227 }, 11228 11229 /** 11230 * Unregisters the given plugin(s) only if registered. 11231 * @param {Array|Object} plugins plugin instance(s). 11232 */ 11233 unregister: function(plugins) { 11234 var p = this._plugins; 11235 ([]).concat(plugins).forEach(function(plugin) { 11236 var idx = p.indexOf(plugin); 11237 if (idx !== -1) { 11238 p.splice(idx, 1); 11239 } 11240 }); 11241 11242 this._cacheId++; 11243 }, 11244 11245 /** 11246 * Remove all registered plugins. 11247 * @since 2.1.5 11248 */ 11249 clear: function() { 11250 this._plugins = []; 11251 this._cacheId++; 11252 }, 11253 11254 /** 11255 * Returns the number of registered plugins? 11256 * @returns {Number} 11257 * @since 2.1.5 11258 */ 11259 count: function() { 11260 return this._plugins.length; 11261 }, 11262 11263 /** 11264 * Returns all registered plugin instances. 11265 * @returns {Array} array of plugin objects. 11266 * @since 2.1.5 11267 */ 11268 getAll: function() { 11269 return this._plugins; 11270 }, 11271 11272 /** 11273 * Calls enabled plugins for `chart` on the specified hook and with the given args. 11274 * This method immediately returns as soon as a plugin explicitly returns false. The 11275 * returned value can be used, for instance, to interrupt the current action. 11276 * @param {Object} chart - The chart instance for which plugins should be called. 11277 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). 11278 * @param {Array} [args] - Extra arguments to apply to the hook call. 11279 * @returns {Boolean} false if any of the plugins return false, else returns true. 11280 */ 11281 notify: function(chart, hook, args) { 11282 var descriptors = this.descriptors(chart); 11283 var ilen = descriptors.length; 11284 var i, descriptor, plugin, params, method; 11285 11286 for (i = 0; i < ilen; ++i) { 11287 descriptor = descriptors[i]; 11288 plugin = descriptor.plugin; 11289 method = plugin[hook]; 11290 if (typeof method === 'function') { 11291 params = [chart].concat(args || []); 11292 params.push(descriptor.options); 11293 if (method.apply(plugin, params) === false) { 11294 return false; 11295 } 11296 } 11297 } 11298 11299 return true; 11300 }, 11301 11302 /** 11303 * Returns descriptors of enabled plugins for the given chart. 11304 * @returns {Array} [{ plugin, options }] 11305 * @private 11306 */ 11307 descriptors: function(chart) { 11308 var cache = chart.$plugins || (chart.$plugins = {}); 11309 if (cache.id === this._cacheId) { 11310 return cache.descriptors; 11311 } 11312 11313 var plugins = []; 11314 var descriptors = []; 11315 var config = (chart && chart.config) || {}; 11316 var options = (config.options && config.options.plugins) || {}; 11317
vendor: 9,640 bytes, lines 11318-11546
11318 this._plugins.concat(config.plugins || []).forEach(function(plugin) { 11319 var idx = plugins.indexOf(plugin); 11320 if (idx !== -1) { 11321 return; 11322 } 11323 11324 var id = plugin.id; 11325 var opts = options[id]; 11326 if (opts === false) { 11327 return; 11328 } 11329 11330 if (opts === true) { 11331 opts = helpers.clone(defaults.global.plugins[id]); 11332 } 11333 11334 plugins.push(plugin); 11335 descriptors.push({ 11336 plugin: plugin, 11337 options: opts || {} 11338 }); 11339 }); 11340 11341 cache.descriptors = descriptors; 11342 cache.id = this._cacheId; 11343 return descriptors; 11344 }, 11345 11346 /** 11347 * Invalidates cache for the given chart: descriptors hold a reference on plugin option, 11348 * but in some cases, this reference can be changed by the user when updating options. 11349 * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 11350 * @private 11351 */ 11352 _invalidate: function(chart) { 11353 delete chart.$plugins; 11354 } 11355}; 11356 11357/** 11358 * Plugin extension hooks. 11359 * @interface IPlugin 11360 * @since 2.1.0 11361 */ 11362/** 11363 * @method IPlugin#beforeInit 11364 * @desc Called before initializing `chart`. 11365 * @param {Chart.Controller} chart - The chart instance. 11366 * @param {Object} options - The plugin options. 11367 */ 11368/** 11369 * @method IPlugin#afterInit 11370 * @desc Called after `chart` has been initialized and before the first update. 11371 * @param {Chart.Controller} chart - The chart instance. 11372 * @param {Object} options - The plugin options. 11373 */ 11374/** 11375 * @method IPlugin#beforeUpdate 11376 * @desc Called before updating `chart`. If any plugin returns `false`, the update 11377 * is cancelled (and thus subsequent render(s)) until another `update` is triggered. 11378 * @param {Chart.Controller} chart - The chart instance. 11379 * @param {Object} options - The plugin options. 11380 * @returns {Boolean} `false` to cancel the chart update. 11381 */ 11382/** 11383 * @method IPlugin#afterUpdate 11384 * @desc Called after `chart` has been updated and before rendering. Note that this 11385 * hook will not be called if the chart update has been previously cancelled. 11386 * @param {Chart.Controller} chart - The chart instance. 11387 * @param {Object} options - The plugin options. 11388 */ 11389/** 11390 * @method IPlugin#beforeDatasetsUpdate 11391 * @desc Called before updating the `chart` datasets. If any plugin returns `false`, 11392 * the datasets update is cancelled until another `update` is triggered. 11393 * @param {Chart.Controller} chart - The chart instance. 11394 * @param {Object} options - The plugin options. 11395 * @returns {Boolean} false to cancel the datasets update. 11396 * @since version 2.1.5 11397*/ 11398/** 11399 * @method IPlugin#afterDatasetsUpdate 11400 * @desc Called after the `chart` datasets have been updated. Note that this hook 11401 * will not be called if the datasets update has been previously cancelled. 11402 * @param {Chart.Controller} chart - The chart instance. 11403 * @param {Object} options - The plugin options. 11404 * @since version 2.1.5 11405 */ 11406/** 11407 * @method IPlugin#beforeDatasetUpdate 11408 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin 11409 * returns `false`, the datasets update is cancelled until another `update` is triggered. 11410 * @param {Chart} chart - The chart instance. 11411 * @param {Object} args - The call arguments. 11412 * @param {Number} args.index - The dataset index. 11413 * @param {Object} args.meta - The dataset metadata. 11414 * @param {Object} options - The plugin options. 11415 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11416 */ 11417/** 11418 * @method IPlugin#afterDatasetUpdate 11419 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note 11420 * that this hook will not be called if the datasets update has been previously cancelled. 11421 * @param {Chart} chart - The chart instance. 11422 * @param {Object} args - The call arguments. 11423 * @param {Number} args.index - The dataset index. 11424 * @param {Object} args.meta - The dataset metadata. 11425 * @param {Object} options - The plugin options. 11426 */ 11427/** 11428 * @method IPlugin#beforeLayout 11429 * @desc Called before laying out `chart`. If any plugin returns `false`, 11430 * the layout update is cancelled until another `update` is triggered. 11431 * @param {Chart.Controller} chart - The chart instance. 11432 * @param {Object} options - The plugin options. 11433 * @returns {Boolean} `false` to cancel the chart layout. 11434 */ 11435/** 11436 * @method IPlugin#afterLayout 11437 * @desc Called after the `chart` has been layed out. Note that this hook will not 11438 * be called if the layout update has been previously cancelled. 11439 * @param {Chart.Controller} chart - The chart instance. 11440 * @param {Object} options - The plugin options. 11441 */ 11442/** 11443 * @method IPlugin#beforeRender 11444 * @desc Called before rendering `chart`. If any plugin returns `false`, 11445 * the rendering is cancelled until another `render` is triggered. 11446 * @param {Chart.Controller} chart - The chart instance. 11447 * @param {Object} options - The plugin options. 11448 * @returns {Boolean} `false` to cancel the chart rendering. 11449 */ 11450/** 11451 * @method IPlugin#afterRender 11452 * @desc Called after the `chart` has been fully rendered (and animation completed). Note 11453 * that this hook will not be called if the rendering has been previously cancelled. 11454 * @param {Chart.Controller} chart - The chart instance. 11455 * @param {Object} options - The plugin options. 11456 */ 11457/** 11458 * @method IPlugin#beforeDraw 11459 * @desc Called before drawing `chart` at every animation frame specified by the given 11460 * easing value. If any plugin returns `false`, the frame drawing is cancelled until 11461 * another `render` is triggered. 11462 * @param {Chart.Controller} chart - The chart instance. 11463 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11464 * @param {Object} options - The plugin options. 11465 * @returns {Boolean} `false` to cancel the chart drawing. 11466 */ 11467/** 11468 * @method IPlugin#afterDraw 11469 * @desc Called after the `chart` has been drawn for the specific easing value. Note 11470 * that this hook will not be called if the drawing has been previously cancelled. 11471 * @param {Chart.Controller} chart - The chart instance. 11472 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11473 * @param {Object} options - The plugin options. 11474 */ 11475/** 11476 * @method IPlugin#beforeDatasetsDraw 11477 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, 11478 * the datasets drawing is cancelled until another `render` is triggered. 11479 * @param {Chart.Controller} chart - The chart instance. 11480 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11481 * @param {Object} options - The plugin options. 11482 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11483 */ 11484/** 11485 * @method IPlugin#afterDatasetsDraw 11486 * @desc Called after the `chart` datasets have been drawn. Note that this hook 11487 * will not be called if the datasets drawing has been previously cancelled. 11488 * @param {Chart.Controller} chart - The chart instance. 11489 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11490 * @param {Object} options - The plugin options. 11491 */ 11492/** 11493 * @method IPlugin#beforeDatasetDraw 11494 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets 11495 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing 11496 * is cancelled until another `render` is triggered. 11497 * @param {Chart} chart - The chart instance. 11498 * @param {Object} args - The call arguments. 11499 * @param {Number} args.index - The dataset index. 11500 * @param {Object} args.meta - The dataset metadata. 11501 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11502 * @param {Object} options - The plugin options. 11503 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11504 */ 11505/** 11506 * @method IPlugin#afterDatasetDraw 11507 * @desc Called after the `chart` datasets at the given `args.index` have been drawn 11508 * (datasets are drawn in the reverse order). Note that this hook will not be called 11509 * if the datasets drawing has been previously cancelled. 11510 * @param {Chart} chart - The chart instance. 11511 * @param {Object} args - The call arguments. 11512 * @param {Number} args.index - The dataset index. 11513 * @param {Object} args.meta - The dataset metadata. 11514 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11515 * @param {Object} options - The plugin options. 11516 */ 11517/** 11518 * @method IPlugin#beforeTooltipDraw 11519 * @desc Called before drawing the `tooltip`. If any plugin returns `false`, 11520 * the tooltip drawing is cancelled until another `render` is triggered. 11521 * @param {Chart} chart - The chart instance. 11522 * @param {Object} args - The call arguments. 11523 * @param {Object} args.tooltip - The tooltip. 11524 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11525 * @param {Object} options - The plugin options. 11526 * @returns {Boolean} `false` to cancel the chart tooltip drawing. 11527 */ 11528/** 11529 * @method IPlugin#afterTooltipDraw 11530 * @desc Called after drawing the `tooltip`. Note that this hook will not 11531 * be called if the tooltip drawing has been previously cancelled. 11532 * @param {Chart} chart - The chart instance. 11533 * @param {Object} args - The call arguments. 11534 * @param {Object} args.tooltip - The tooltip. 11535 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11536 * @param {Object} options - The plugin options. 11537 */ 11538/** 11539 * @method IPlugin#beforeEvent 11540 * @desc Called before processing the specified `event`. If any plugin returns `false`, 11541 * the event will be discarded. 11542 * @param {Chart.Controller} chart - The chart instance. 11543 * @param {IEvent} event - The event object. 11544 * @param {Object} options - The plugin options. 11545 */ 11546/**
11547 * @method IPlugin#afterEvent 11548 * @desc Called after the `event` has been consumed. Note that this hook 11549 * will not be called if the `event` has been previously discarded. 11550 * @param {Chart.Controller} chart - The chart instance. 11551 * @param {IEvent} event - The event object. 11552 * @param {Object} options - The plugin options. 11553 */ 11554/** 11555 * @method IPlugin#resize 11556 * @desc Called after the chart as been resized. 11557 * @param {Chart.Controller} chart - The chart instance. 11558 * @param {Number} size - The new canvas display size (eq. canvas.style width & height). 11559 * @param {Object} options - The plugin options. 11560 */ 11561/** 11562 * @method IPlugin#destroy 11563 * @desc Called after the chart as been destroyed. 11564 * @param {Chart.Controller} chart - The chart instance. 11565 * @param {Object} options - The plugin options. 11566 */ 11567 11568},{"25":25,"45":45}],32:[function(require,module,exports){ 11569'use strict'; 11570 11571var defaults = require(25); 11572var Element = require(26); 11573var helpers = require(45); 11574var Ticks = require(34); 11575 11576defaults._set('scale', { 11577 display: true, 11578 position: 'left', 11579 offset: false, 11580 11581 // grid line settings 11582 gridLines: { 11583 display: true, 11584 color: 'rgba(0, 0, 0, 0.1)', 11585 lineWidth: 1, 11586 drawBorder: true, 11587 drawOnChartArea: true, 11588 drawTicks: true, 11589 tickMarkLength: 10, 11590 zeroLineWidth: 1, 11591 zeroLineColor: 'rgba(0,0,0,0.25)', 11592 zeroLineBorderDash: [], 11593 zeroLineBorderDashOffset: 0.0, 11594 offsetGridLines: false, 11595 borderDash: [], 11596 borderDashOffset: 0.0 11597 }, 11598 11599 // scale label 11600 scaleLabel: { 11601 // display property 11602 display: false, 11603 11604 // actual label 11605 labelString: '', 11606 11607 // line height 11608 lineHeight: 1.2, 11609 11610 // top/bottom padding 11611 padding: { 11612 top: 4, 11613 bottom: 4 11614 } 11615 }, 11616 11617 // label settings 11618 ticks: { 11619 beginAtZero: false, 11620 minRotation: 0, 11621 maxRotation: 50, 11622 mirror: false, 11623 padding: 0, 11624 reverse: false, 11625 display: true, 11626 autoSkip: true, 11627 autoSkipPadding: 0, 11628 labelOffset: 0, 11629 // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. 11630 callback: Ticks.formatters.values, 11631 minor: {}, 11632 major: {} 11633 } 11634}); 11635 11636function labelsFromTicks(ticks) { 11637 var labels = []; 11638 var i, ilen; 11639 11640 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 11641 labels.push(ticks[i].label); 11642 } 11643 11644 return labels; 11645} 11646 11647function getLineValue(scale, index, offsetGridLines) { 11648 var lineValue = scale.getPixelForTick(index); 11649 11650 if (offsetGridLines) { 11651 if (index === 0) { 11652 lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; 11653 } else { 11654 lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; 11655 } 11656 } 11657 return lineValue; 11658} 11659 11660module.exports = function(Chart) { 11661 11662 function computeTextSize(context, tick, font) { 11663 return helpers.isArray(tick) ? 11664 helpers.longestText(context, font, tick) : 11665 context.measureText(tick).width; 11666 } 11667 11668 function parseFontOptions(options) { 11669 var valueOrDefault = helpers.valueOrDefault; 11670 var globalDefaults = defaults.global; 11671 var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); 11672 var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); 11673 var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); 11674 11675 return { 11676 size: size, 11677 style: style, 11678 family: family, 11679 font: helpers.fontString(size, style, family) 11680 }; 11681 } 11682 11683 function parseLineHeight(options) { 11684 return helpers.options.toLineHeight( 11685 helpers.valueOrDefault(options.lineHeight, 1.2), 11686 helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); 11687 } 11688 11689 Chart.Scale = Element.extend({ 11690 /** 11691 * Get the padding needed for the scale 11692 * @method getPadding 11693 * @private 11694 * @returns {Padding} the necessary padding 11695 */ 11696 getPadding: function() { 11697 var me = this; 11698 return { 11699 left: me.paddingLeft || 0, 11700 top: me.paddingTop || 0, 11701 right: me.paddingRight || 0, 11702 bottom: me.paddingBottom || 0 11703 }; 11704 }, 11705 11706 /** 11707 * Returns the scale tick objects ({label, major}) 11708 * @since 2.7 11709 */ 11710 getTicks: function() { 11711 return this._ticks; 11712 }, 11713 11714 // These methods are ordered by lifecyle. Utilities then follow. 11715 // Any function defined here is inherited by all scale types. 11716 // Any function can be extended by the scale type 11717 11718 mergeTicksOptions: function() { 11719 var ticks = this.options.ticks; 11720 if (ticks.minor === false) { 11721 ticks.minor = { 11722 display: false 11723 }; 11724 } 11725 if (ticks.major === false) { 11726 ticks.major = { 11727 display: false 11728 }; 11729 } 11730 for (var key in ticks) { 11731 if (key !== 'major' && key !== 'minor') { 11732 if (typeof ticks.minor[key] === 'undefined') { 11733 ticks.minor[key] = ticks[key]; 11734 } 11735 if (typeof ticks.major[key] === 'undefined') { 11736 ticks.major[key] = ticks[key]; 11737 } 11738 } 11739 } 11740 }, 11741 beforeUpdate: function() { 11742 helpers.callback(this.options.beforeUpdate, [this]); 11743 }, 11744 update: function(maxWidth, maxHeight, margins) { 11745 var me = this; 11746 var i, ilen, labels, label, ticks, tick; 11747 11748 // Update Lifecycle - Probably don't want to ever extend or over
11748write this function ;) 11749 me.beforeUpdate(); 11750 11751 // Absorb the master measurements 11752 me.maxWidth = maxWidth; 11753 me.maxHeight = maxHeight; 11754 me.margins = helpers.extend({ 11755 left: 0, 11756 right: 0, 11757 top: 0, 11758 bottom: 0 11759 }, margins); 11760 me.longestTextCache = me.longestTextCache || {}; 11761 11762 // Dimensions 11763 me.beforeSetDimensions(); 11764 me.setDimensions(); 11765 me.afterSetDimensions(); 11766 11767 // Data min/max 11768 me.beforeDataLimits(); 11769 me.determineDataLimits(); 11770 me.afterDataLimits(); 11771 11772 // Ticks - `this.ticks` is now DEPRECATED! 11773 // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member 11774 // and must not be accessed directly from outside this class. `this.ticks` being 11775 // around for long time and not marked as private, we can't change its structure 11776 // without unexpected breaking changes. If you need to access the scale ticks, 11777 // use scale.getTicks() instead. 11778 11779 me.beforeBuildTicks(); 11780 11781 // New implementations should return an array of objects but for BACKWARD COMPAT, 11782 // we still support no return (`this.ticks` internally set by calling this method). 11783 ticks = me.buildTicks() || []; 11784 11785 me.afterBuildTicks(); 11786 11787 me.beforeTickToLabelConversion(); 11788 11789 // New implementations should return the formatted tick labels but for BACKWARD 11790 // COMPAT, we still support no return (`this.ticks` internally changed by calling 11791 // this method and supposed to contain only string values). 11792 labels = me.convertTicksToLabels(ticks) || me.ticks; 11793 11794 me.afterTickToLabelConversion(); 11795 11796 me.ticks = labels; // BACKWARD COMPATIBILITY 11797 11798 // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! 11799 11800 // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) 11801 for (i = 0, ilen = labels.length; i < ilen; ++i) { 11802 label = labels[i]; 11803 tick = ticks[i]; 11804 if (!tick) { 11805 ticks.push(tick = { 11806 label: label, 11807 major: false 11808 }); 11809 } else { 11810 tick.label = label; 11811 } 11812 } 11813 11814 me._ticks = ticks; 11815 11816 // Tick Rotation 11817 me.beforeCalculateTickRotation(); 11818 me.calculateTickRotation(); 11819 me.afterCalculateTickRotation(); 11820 // Fit 11821 me.beforeFit(); 11822 me.fit(); 11823 me.afterFit(); 11824 // 11825 me.afterUpdate(); 11826 11827 return me.minSize; 11828 11829 }, 11830 afterUpdate: function() { 11831 helpers.callback(this.options.afterUpdate, [this]); 11832 }, 11833 11834 // 11835 11836 beforeSetDimensions: function() { 11837 helpers.callback(this.options.beforeSetDimensions, [this]); 11838 }, 11839 setDimensions: function() { 11840 var me = this; 11841 // Set the unconstrained dimension before label rotation 11842 if (me.isHorizontal()) { 11843 // Reset position before calculating rotation 11844 me.width = me.maxWidth; 11845 me.left = 0; 11846 me.right = me.width; 11847 } else { 11848 me.height = me.maxHeight; 11849 11850 // Reset position before calculating rotation 11851 me.top = 0; 11852 me.bottom = me.height; 11853 } 11854 11855 // Reset padding 11856 me.paddingLeft = 0; 11857 me.paddingTop = 0; 11858 me.paddingRight = 0; 11859 me.paddingBottom = 0; 11860 }, 11861 afterSetDimensions: function() { 11862 helpers.callback(this.options.afterSetDimensions, [this]); 11863 }, 11864 11865 // Data limits 11866 beforeDataLimits: function() { 11867 helpers.callback(this.options.beforeDataLimits, [this]); 11868 }, 11869 determineDataLimits: helpers.noop, 11870 afterDataLimits: function() { 11871 helpers.callback(this.options.afterDataLimits, [this]); 11872 }, 11873 11874 // 11875 beforeBuildTicks: function() { 11876 helpers.callback(this.options.beforeBuildTicks, [this]); 11877 }, 11878 buildTicks: helpers.noop, 11879 afterBuildTicks: function() { 11880 helpers.callback(this.options.afterBuildTicks, [this]); 11881 }, 11882 11883 beforeTickToLabelConversion: function() { 11884 helpers.callback(this.options.beforeTickToLabelConversion, [this]); 11885 }, 11886 convertTicksToLabels: function() { 11887 var me = this; 11888 // Convert ticks to strings 11889 var tickOpts = me.options.ticks; 11890 me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); 11891 }, 11892 afterTickToLabelConversion: function() { 11893 helpers.callback(this.options.afterTickToLabelConversion, [this]); 11894 }, 11895 11896 // 11897 11898 beforeCalculateTickRotation: function() { 11899 helpers.callback(this.options.beforeCalculateTickRotation, [this]); 11900 }, 11901 calculateTickRotation: function() { 11902 var me = this; 11903 var context = me.ctx; 11904 var tickOpts = me.options.ticks; 11905 var labels = labelsFromTicks(me._ticks); 11906 11907 // Get the width of each grid by calculating the difference 11908 // between x offsets between 0 and 1.
11909 var tickFont = parseFontOptions(tickOpts); 11910 context.font = tickFont.font; 11911 11912 var labelRotation = tickOpts.minRotation || 0; 11913 11914 if (labels.length && me.options.display && me.isHorizontal()) { 11915 var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); 11916 var labelWidth = originalLabelWidth; 11917 var cosRotation, sinRotation; 11918 11919 // Allow 3 pixels x2 padding either side for label readability 11920 var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; 11921 11922 // Max label rotation can be set or default to 90 - also act as a loop counter 11923 while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { 11924 var angleRadians = helpers.toRadians(labelRotation); 11925 cosRotation = Math.cos(angleRadians); 11926 sinRotation = Math.sin(angleRadians); 11927 11928 if (sinRotation * originalLabelWidth > me.maxHeight) { 11929 // go back one step 11930 labelRotation--; 11931 break; 11932 } 11933 11934 labelRotation++; 11935 labelWidth = cosRotation * originalLabelWidth; 11936 } 11937 } 11938 11939 me.labelRotation = labelRotation; 11940 }, 11941 afterCalculateTickRotation: function() { 11942 helpers.callback(this.options.afterCalculateTickRotation, [this]); 11943 }, 11944 11945 // 11946 11947 beforeFit: function() { 11948 helpers.callback(this.options.beforeFit, [this]); 11949 }, 11950 fit: function() { 11951 var me = this; 11952 // Reset 11953 var minSize = me.minSize = { 11954 width: 0, 11955 height: 0 11956 }; 11957 11958 var labels = labelsFromTicks(me._ticks); 11959 11960 var opts = me.options; 11961 var tickOpts = opts.ticks; 11962 var scaleLabelOpts = opts.scaleLabel; 11963 var gridLineOpts = opts.gridLines; 11964 var display = opts.display; 11965 var isHorizontal = me.isHorizontal(); 11966 11967 var tickFont = parseFontOptions(tickOpts); 11968 var tickMarkLength = opts.gridLines.tickMarkLength; 11969 11970 // Width 11971 if (isHorizontal) { 11972 // subtract the margins to line up with the chartArea if we are a full width scale 11973 minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; 11974 } else { 11975 minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; 11976 } 11977 11978 // height 11979 if (isHorizontal) { 11980 minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; 11981 } else { 11982 minSize.height = me.maxHeight; // fill all the height 11983 } 11984 11985 // Are we showing a title for the scale? 11986 if (scaleLabelOpts.display && display) { 11987 var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); 11988 var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); 11989 var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; 11990 11991 if (isHorizontal) { 11992 minSize.height += deltaHeight; 11993 } else { 11994 minSize.width += deltaHeight; 11995 } 11996 } 11997 11998 // Don't bother fitting the ticks if we are not showing them 11999 if (tickOpts.display && display) { 12000 var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); 12001 var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); 12002 var lineSpace = tickFont.size * 0.5; 12003 var tickPadding = me.options.ticks.padding; 12004 12005 if (isHorizontal) { 12006 // A horizontal axis is more constrained by the height. 12007 me.longestLabelWidth = largestTextWidth; 12008 12009 var angleRadians = helpers.toRadians(me.labelRotation); 12010 var cosRotation = Math.cos(angleRadians); 12011 var sinRotation = Math.sin(angleRadians); 12012 12013 // TODO - improve this calculation 12014 var labelHeight = (sinRotation * largestTextWidth) 12015 + (tickFont.size * tallestLabelHeightInLines) 12016 + (lineSpace * (tallestLabelHeightInLines - 1)) 12017 + lineSpace; // padding 12018 12019 minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); 12020 12021 me.ctx.font = tickFont.font; 12022 var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); 12023 var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); 12024 12025 // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned 12026 // which means that the right padding is dominated by the font height 12027 if (me.labelRotation !== 0) { 12028 me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges 12029 me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; 12030 } else {
12031 me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges 12032 me.paddingRight = lastLabelWidth / 2 + 3; 12033 } 12034 } else { 12035 // A vertical axis is more constrained by the width. Labels are the 12036 // dominant factor here, so get that length first and account for padding 12037 if (tickOpts.mirror) { 12038 largestTextWidth = 0; 12039 } else { 12040 // use lineSpace for consistency with horizontal axis 12041 // tickPadding is not implemented for horizontal 12042 largestTextWidth += tickPadding + lineSpace; 12043 } 12044 12045 minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); 12046 12047 me.paddingTop = tickFont.size / 2; 12048 me.paddingBottom = tickFont.size / 2; 12049 } 12050 } 12051 12052 me.handleMargins(); 12053 12054 me.width = minSize.width; 12055 me.height = minSize.height; 12056 }, 12057 12058 /** 12059 * Handle margins and padding interactions 12060 * @private 12061 */ 12062 handleMargins: function() { 12063 var me = this; 12064 if (me.margins) { 12065 me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); 12066 me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); 12067 me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); 12068 me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); 12069 } 12070 }, 12071 12072 afterFit: function() { 12073 helpers.callback(this.options.afterFit, [this]); 12074 }, 12075 12076 // Shared Methods 12077 isHorizontal: function() { 12078 return this.options.position === 'top' || this.options.position === 'bottom'; 12079 }, 12080 isFullWidth: function() { 12081 return (this.options.fullWidth); 12082 }, 12083 12084 // 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 12085 getRightValue: function(rawValue) { 12086 // Null and undefined values first 12087 if (helpers.isNullOrUndef(rawValue)) { 12088 return NaN; 12089 } 12090 // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values 12091 if (typeof rawValue === 'number' && !isFinite(rawValue)) { 12092 return NaN; 12093 } 12094 // If it is in fact an object, dive in one more level 12095 if (rawValue) { 12096 if (this.isHorizontal()) { 12097 if (rawValue.x !== undefined) { 12098 return this.getRightValue(rawValue.x); 12099 } 12100 } else if (rawValue.y !== undefined) { 12101 return this.getRightValue(rawValue.y); 12102 } 12103 } 12104 12105 // Value is good, return it 12106 return rawValue; 12107 }, 12108 12109 /** 12110 * Used to get the value to display in the tooltip for the data at the given index 12111 * @param index 12112 * @param datasetIndex 12113 */ 12114 getLabelForIndex: helpers.noop, 12115 12116 /** 12117 * Returns the location of the given data point. Value can either be an index or a numerical value 12118 * The coordinate (0, 0) is at the upper-left corner of the canvas 12119 * @param value 12120 * @param index 12121 * @param datasetIndex 12122 */ 12123 getPixelForValue: helpers.noop, 12124 12125 /** 12126 * Used to get the data value from a given pixel. This is the inverse of getPixelForValue 12127 * The coordinate (0, 0) is at the upper-left corner of the canvas 12128 * @param pixel 12129 */ 12130 getValueForPixel: helpers.noop, 12131 12132 /** 12133 * Returns the location of the tick at the given index 12134 * The coordinate (0, 0) is at the upper-left corner of the canvas 12135 */ 12136 getPixelForTick: function(index) { 12137 var me = this; 12138 var offset = me.options.offset; 12139 if (me.isHorizontal()) { 12140 var innerWidth = me.width - (me.paddingLeft + me.paddingRight); 12141 var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); 12142 var pixel = (tickWidth * index) + me.paddingLeft; 12143 12144 if (offset) { 12145 pixel += tickWidth / 2; 12146 } 12147 12148 var finalVal = me.left + Math.round(pixel); 12149 finalVal += me.isFullWidth() ? me.margins.left : 0; 12150 return finalVal; 12151 } 12152 var innerHeight = me.height - (me.paddingTop + me.paddingBottom); 12153 return me.top + (index * (innerHeight / (me._ticks.length - 1))); 12154 }, 12155 12156 /** 12157 * Utility for getting the pixel location of a percentage of scale 12158 * The coordinate (0, 0) is at the upper-left corner of the canvas 12159 */
12160 getPixelForDecimal: function(decimal) { 12161 var me = this; 12162 if (me.isHorizontal()) { 12163 var innerWidth = me.width - (me.paddingLeft + me.paddingRight); 12164 var valueOffset = (innerWidth * decimal) + me.paddingLeft; 12165 12166 var finalVal = me.left + Math.round(valueOffset); 12167 finalVal += me.isFullWidth() ? me.margins.left : 0; 12168 return finalVal; 12169 } 12170 return me.top + (decimal * me.height); 12171 }, 12172 12173 /** 12174 * Returns the pixel for the minimum chart value 12175 * The coordinate (0, 0) is at the upper-left corner of the canvas 12176 */ 12177 getBasePixel: function() { 12178 return this.getPixelForValue(this.getBaseValue()); 12179 }, 12180 12181 getBaseValue: function() { 12182 var me = this; 12183 var min = me.min; 12184 var max = me.max; 12185 12186 return me.beginAtZero ? 0 : 12187 min < 0 && max < 0 ? max : 12188 min > 0 && max > 0 ? min : 12189 0; 12190 }, 12191 12192 /** 12193 * Returns a subset of ticks to be plotted to avoid overlapping labels. 12194 * @private 12195 */ 12196 _autoSkip: function(ticks) { 12197 var skipRatio; 12198 var me = this; 12199 var isHorizontal = me.isHorizontal(); 12200 var optionTicks = me.options.ticks.minor; 12201 var tickCount = ticks.length; 12202 var labelRotationRadians = helpers.toRadians(me.labelRotation); 12203 var cosRotation = Math.cos(labelRotationRadians); 12204 var longestRotatedLabel = me.longestLabelWidth * cosRotation; 12205 var result = []; 12206 var i, tick, shouldSkip; 12207 12208 // figure out the maximum number of gridlines to show 12209 var maxTicks; 12210 if (optionTicks.maxTicksLimit) { 12211 maxTicks = optionTicks.maxTicksLimit; 12212 } 12213 12214 if (isHorizontal) { 12215 skipRatio = false; 12216 12217 if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { 12218 skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); 12219 } 12220 12221 // if they defined a max number of optionTicks, 12222 // increase skipRatio until that number is met 12223 if (maxTicks && tickCount > maxTicks) { 12224 skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); 12225 } 12226 } 12227 12228 for (i = 0; i < tickCount; i++) { 12229 tick = ticks[i]; 12230 12231 // Since we always show the last tick,we need may need to hide the last shown one before 12232 shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); 12233 if (shouldSkip && i !== tickCount - 1) { 12234 // leave tick in place but make sure it's not displayed (#4635) 12235 delete tick.label; 12236 } 12237 result.push(tick); 12238 } 12239 return result; 12240 }, 12241 12242 // Actually draw the scale on the canvas 12243 // @param {rectangle} chartArea : the area of the chart to draw full grid lines on 12244 draw: function(chartArea) { 12245 var me = this; 12246 var options = me.options; 12247 if (!options.display) { 12248 return; 12249 } 12250 12251 var context = me.ctx; 12252 var globalDefaults = defaults.global; 12253 var optionTicks = options.ticks.minor; 12254 var optionMajorTicks = options.ticks.major || optionTicks; 12255 var gridLines = options.gridLines; 12256 var scaleLabel = options.scaleLabel; 12257 12258 var isRotated = me.labelRotation !== 0; 12259 var isHorizontal = me.isHorizontal(); 12260 12261 var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); 12262 var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); 12263 var tickFont = parseFontOptions(optionTicks); 12264 var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); 12265 var majorTickFont = parseFontOptions(optionMajorTicks); 12266 12267 var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; 12268 12269 var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); 12270 var scaleLabelFont = parseFontOptions(scaleLabel); 12271 var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); 12272 var labelRotationRadians = helpers.toRadians(me.labelRotation); 12273 12274 var itemsToDraw = []; 12275 12276 var axisWidth = me.options.gridLines.lineWidth; 12277 var xTickStart = options.position === 'right' ? me.right : me.right - axisWidth - tl; 12278 var xTickEnd = options.position === 'right' ? me.right + tl : me.right; 12279 var yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth; 12280 var yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth; 12281 12282 helpers.each(ticks, function(tick, index) { 12283 // autoskipper skipped this tick (#4635) 12284 if (helpers.isNullOrUndef(tick.label)) { 12285 return; 12286 } 12287 12288 var label = tick.label; 12289 var lineWidth, lineColor, borderDash, borderDashOffset; 12290 if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { 12291 // Draw the first index specially
12292 lineWidth = gridLines.zeroLineWidth; 12293 lineColor = gridLines.zeroLineColor; 12294 borderDash = gridLines.zeroLineBorderDash; 12295 borderDashOffset = gridLines.zeroLineBorderDashOffset; 12296 } else { 12297 lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); 12298 lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); 12299 borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); 12300 borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); 12301 } 12302 12303 // Common properties 12304 var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; 12305 var textAlign = 'middle'; 12306 var textBaseline = 'middle'; 12307 var tickPadding = optionTicks.padding; 12308 12309 if (isHorizontal) { 12310 var labelYOffset = tl + tickPadding; 12311 12312 if (options.position === 'bottom') { 12313 // bottom 12314 textBaseline = !isRotated ? 'top' : 'middle'; 12315 textAlign = !isRotated ? 'center' : 'right'; 12316 labelY = me.top + labelYOffset; 12317 } else { 12318 // top 12319 textBaseline = !isRotated ? 'bottom' : 'middle'; 12320 textAlign = !isRotated ? 'center' : 'left'; 12321 labelY = me.bottom - labelYOffset; 12322 } 12323 12324 var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); 12325 if (xLineValue < me.left) { 12326 lineColor = 'rgba(0,0,0,0)'; 12327 } 12328 xLineValue += helpers.aliasPixel(lineWidth); 12329 12330 labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) 12331 12332 tx1 = tx2 = x1 = x2 = xLineValue; 12333 ty1 = yTickStart; 12334 ty2 = yTickEnd; 12335 y1 = chartArea.top; 12336 y2 = chartArea.bottom + axisWidth; 12337 } else { 12338 var isLeft = options.position === 'left'; 12339 var labelXOffset; 12340 12341 if (optionTicks.mirror) { 12342 textAlign = isLeft ? 'left' : 'right'; 12343 labelXOffset = tickPadding; 12344 } else { 12345 textAlign = isLeft ? 'right' : 'left'; 12346 labelXOffset = tl + tickPadding; 12347 } 12348 12349 labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; 12350 12351 var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); 12352 if (yLineValue < me.top) { 12353 lineColor = 'rgba(0,0,0,0)'; 12354 } 12355 yLineValue += helpers.aliasPixel(lineWidth); 12356 12357 labelY = me.getPixelForTick(index) + optionTicks.labelOffset; 12358 12359 tx1 = xTickStart; 12360 tx2 = xTickEnd; 12361 x1 = chartArea.left; 12362 x2 = chartArea.right + axisWidth; 12363 ty1 = ty2 = y1 = y2 = yLineValue; 12364 } 12365 12366 itemsToDraw.push({ 12367 tx1: tx1, 12368 ty1: ty1, 12369 tx2: tx2, 12370 ty2: ty2, 12371 x1: x1, 12372 y1: y1, 12373 x2: x2, 12374 y2: y2, 12375 labelX: labelX, 12376 labelY: labelY, 12377 glWidth: lineWidth, 12378 glColor: lineColor, 12379 glBorderDash: borderDash, 12380 glBorderDashOffset: borderDashOffset, 12381 rotation: -1 * labelRotationRadians, 12382 label: label, 12383 major: tick.major, 12384 textBaseline: textBaseline, 12385 textAlign: textAlign 12386 }); 12387 }); 12388 12389 // Draw all of the tick labels, tick marks, and grid lines at the correct places 12390 helpers.each(itemsToDraw, function(itemToDraw) { 12391 if (gridLines.display) { 12392 context.save(); 12393 context.lineWidth = itemToDraw.glWidth; 12394 context.strokeStyle = itemToDraw.glColor; 12395 if (context.setLineDash) { 12396 context.setLineDash(itemToDraw.glBorderDash); 12397 context.lineDashOffset = itemToDraw.glBorderDashOffset; 12398 } 12399 12400 context.beginPath(); 12401 12402 if (gridLines.drawTicks) { 12403 context.moveTo(itemToDraw.tx1, itemToDraw.ty1); 12404 context.lineTo(itemToDraw.tx2, itemToDraw.ty2); 12405 } 12406 12407 if (gridLines.drawOnChartArea) { 12408 context.moveTo(itemToDraw.x1, itemToDraw.y1); 12409 context.lineTo(itemToDraw.x2, itemToDraw.y2); 12410 } 12411 12412 context.stroke(); 12413 context.restore(); 12414 } 12415 12416 if (optionTicks.display) { 12417 // Make sure we draw text in the correct color and font 12418 context.save(); 12419 context.translate(itemToDraw.labelX, itemToDraw.labelY); 12420 context.rotate(itemToDraw.rotation); 12421 context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; 12422 context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; 12423 context.textBaseline = itemToDraw.textBaseline; 12424 context.textAlign = itemToDraw.textAlign; 12425 12426 var label = itemToDraw.label; 12427 if (helpers.isArray(label)) { 12428 var lineCount = label.length; 12429 var lineHeight = tickFont.size * 1.5; 12430 var y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2; 12431 12432 for (var i = 0; i < lineCount; ++i) { 12433 // We just make sure the multiline element is a string here.. 12434 context.fillText('' + label[i], 0, y); 12435 // apply same lineSpacing as calculated @ L#320 12436 y += lineHeight; 12437 } 12438 } else {
12439 context.fillText(label, 0, 0); 12440 } 12441 context.restore(); 12442 } 12443 }); 12444 12445 if (scaleLabel.display) { 12446 // Draw the scale label 12447 var scaleLabelX; 12448 var scaleLabelY; 12449 var rotation = 0; 12450 var halfLineHeight = parseLineHeight(scaleLabel) / 2; 12451 12452 if (isHorizontal) { 12453 scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width 12454 scaleLabelY = options.position === 'bottom' 12455 ? me.bottom - halfLineHeight - scaleLabelPadding.bottom 12456 : me.top + halfLineHeight + scaleLabelPadding.top; 12457 } else { 12458 var isLeft = options.position === 'left'; 12459 scaleLabelX = isLeft 12460 ? me.left + halfLineHeight + scaleLabelPadding.top 12461 : me.right - halfLineHeight - scaleLabelPadding.top; 12462 scaleLabelY = me.top + ((me.bottom - me.top) / 2); 12463 rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; 12464 } 12465 12466 context.save(); 12467 context.translate(scaleLabelX, scaleLabelY); 12468 context.rotate(rotation); 12469 context.textAlign = 'center'; 12470 context.textBaseline = 'middle'; 12471 context.fillStyle = scaleLabelFontColor; // render in correct colour 12472 context.font = scaleLabelFont.font;
12473 context.fillText(scaleLabel.labelString, 0, 0); 12474 context.restore(); 12475 } 12476 12477 if (gridLines.drawBorder) { 12478 // Draw the line at the edge of the axis 12479 context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); 12480 context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); 12481 var x1 = me.left; 12482 var x2 = me.right + axisWidth; 12483 var y1 = me.top; 12484 var y2 = me.bottom + axisWidth; 12485 12486 var aliasPixel = helpers.aliasPixel(context.lineWidth); 12487 if (isHorizontal) { 12488 y1 = y2 = options.position === 'top' ? me.bottom : me.top; 12489 y1 += aliasPixel; 12490 y2 += aliasPixel; 12491 } else { 12492 x1 = x2 = options.position === 'left' ? me.right : me.left; 12493 x1 += aliasPixel; 12494 x2 += aliasPixel; 12495 } 12496 12497 context.beginPath(); 12498 context.moveTo(x1, y1); 12499 context.lineTo(x2, y2); 12500 context.stroke(); 12501 } 12502 } 12503 }); 12504}; 12505 12506},{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){ 12507'use strict'; 12508 12509var defaults = require(25); 12510var helpers = require(45); 12511var layouts = require(30); 12512 12513module.exports = function(Chart) { 12514 12515 Chart.scaleService = { 12516 // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then 12517 // use the new chart options to grab the correct scale 12518 constructors: {}, 12519 // Use a registration function so that we can move to an ES6 map when we no longer need to support 12520 // old browsers 12521 12522 // Scale config defaults 12523 defaults: {}, 12524 registerScaleType: function(type, scaleConstructor, scaleDefaults) { 12525 this.constructors[type] = scaleConstructor; 12526 this.defaults[type] = helpers.clone(scaleDefaults); 12527 }, 12528 getScaleConstructor: function(type) { 12529 return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; 12530 }, 12531 getScaleDefaults: function(type) { 12532 // Return the scale defaults merged with the global settings so that we always use the latest ones 12533 return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; 12534 }, 12535 updateScaleDefaults: function(type, additions) { 12536 var me = this; 12537 if (me.defaults.hasOwnProperty(type)) { 12538 me.defaults[type] = helpers.extend(me.defaults[type], additions); 12539 } 12540 }, 12541 addScalesToLayout: function(chart) { 12542 // Adds each scale to the chart.boxes array to be sized accordingly 12543 helpers.each(chart.scales, function(scale) { 12544 // Set ILayoutItem parameters for backwards compatibility 12545 scale.fullWidth = scale.options.fullWidth; 12546 scale.position = scale.options.position; 12547 scale.weight = scale.options.weight; 12548 layouts.addBox(chart, scale); 12549 }); 12550 } 12551 }; 12552}; 12553 12554},{"25":25,"30":30,"45":45}],34:[function(require,module,exports){ 12555'use strict'; 12556 12557var helpers = require(45); 12558 12559/** 12560 * Namespace to hold static tick generation functions 12561 * @namespace Chart.Ticks 12562 */ 12563module.exports = { 12564 /** 12565 * Namespace to hold formatters for different types of ticks 12566 * @namespace Chart.Ticks.formatters 12567 */ 12568 formatters: { 12569 /** 12570 * Formatter for value labels 12571 * @method Chart.Ticks.formatters.values 12572 * @param value the value to display 12573 * @return {String|Array} the label to display 12574 */ 12575 values: function(value) { 12576 return helpers.isArray(value) ? value : '' + value; 12577 }, 12578 12579 /** 12580 * Formatter for linear numeric ticks 12581 * @method Chart.Ticks.formatters.linear 12582 * @param tickValue {Number} the value to be formatted 12583 * @param index {Number} the position of the tickValue parameter in the ticks array 12584 * @param ticks {Array<Number>} the list of ticks being converted 12585 * @return {String} string representation of the tickValue parameter 12586 */ 12587 linear: function(tickValue, index, ticks) { 12588 // If we have lots of ticks, don't use the ones 12589 var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; 12590 12591 // If we have a number like 2.5 as the delta, figure out how many decimal places we need 12592 if (Math.abs(delta) > 1) { 12593 if (tickValue !== Math.floor(tickValue)) { 12594 // not an integer 12595 delta = tickValue - Math.floor(tickValue); 12596 } 12597 } 12598 12599 var logDelta = helpers.log10(Math.abs(delta)); 12600 var tickString = ''; 12601 12602 if (tickValue !== 0) { 12603 var numDecimal = -1 * Math.floor(logDelta); 12604 numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places 12605 tickString = tickValue.toFixed(numDecimal); 12606 } else { 12607 tickString = '0'; // never show decimal places for 0 12608 } 12609 12610 return tickString; 12611 }, 12612 12613 logarithmic: function(tickValue, index, ticks) { 12614 var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); 12615 12616 if (tickValue === 0) { 12617 return '0'; 12618 } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { 12619 return tickValue.toExponential(); 12620 } 12621 return ''; 12622 } 12623 } 12624}; 12625 12626},{"45":45}],35:[function(require,module,exports){ 12627'use strict'; 12628 12629var defaults = require(25); 12630var Element = require(26); 12631var helpers = require(45); 12632 12633defaults._set('global', { 12634 tooltips: { 12635 enabled: true, 12636 custom: null, 12637 mode: 'nearest', 12638 position: 'average', 12639 intersect: true, 12640 backgroundColor: 'rgba(0,0,0,0.8)', 12641 titleFontStyle: 'bold', 12642 titleSpacing: 2, 12643 titleMarginBottom: 6, 12644 titleFontColor: '#fff', 12645 titleAlign: 'left', 12646 bodySpacing: 2, 12647 bodyFontColor: '#fff', 12648 bodyAlign: 'left', 12649 footerFontStyle: 'bold', 12650 footerSpacing: 2, 12651 footerMarginTop: 6, 12652 footerFontColor: '#fff', 12653 footerAlign: 'left', 12654 yPadding: 6, 12655 xPadding: 6, 12656 caretPadding: 2, 12657 caretSize: 5, 12658 cornerRadius: 6,
vendor: 11,404 bytes, lines 12659-13033
12659 multiKeyBackground: '#fff', 12660 displayColors: true, 12661 borderColor: 'rgba(0,0,0,0)', 12662 borderWidth: 0, 12663 callbacks: { 12664 // Args are: (tooltipItems, data) 12665 beforeTitle: helpers.noop, 12666 title: function(tooltipItems, data) { 12667 // Pick first xLabel for now 12668 var title = ''; 12669 var labels = data.labels; 12670 var labelCount = labels ? labels.length : 0; 12671 12672 if (tooltipItems.length > 0) { 12673 var item = tooltipItems[0]; 12674 12675 if (item.xLabel) { 12676 title = item.xLabel; 12677 } else if (labelCount > 0 && item.index < labelCount) { 12678 title = labels[item.index]; 12679 } 12680 } 12681 12682 return title; 12683 }, 12684 afterTitle: helpers.noop, 12685 12686 // Args are: (tooltipItems, data) 12687 beforeBody: helpers.noop, 12688 12689 // Args are: (tooltipItem, data) 12690 beforeLabel: helpers.noop, 12691 label: function(tooltipItem, data) { 12692 var label = data.datasets[tooltipItem.datasetIndex].label || ''; 12693 12694 if (label) { 12695 label += ': '; 12696 } 12697 label += tooltipItem.yLabel; 12698 return label; 12699 }, 12700 labelColor: function(tooltipItem, chart) { 12701 var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); 12702 var activeElement = meta.data[tooltipItem.index]; 12703 var view = activeElement._view; 12704 return { 12705 borderColor: view.borderColor, 12706 backgroundColor: view.backgroundColor 12707 }; 12708 }, 12709 labelTextColor: function() { 12710 return this._options.bodyFontColor; 12711 }, 12712 afterLabel: helpers.noop, 12713 12714 // Args are: (tooltipItems, data) 12715 afterBody: helpers.noop, 12716 12717 // Args are: (tooltipItems, data) 12718 beforeFooter: helpers.noop, 12719 footer: helpers.noop, 12720 afterFooter: helpers.noop 12721 } 12722 } 12723}); 12724 12725module.exports = function(Chart) { 12726 12727 /** 12728 * Helper method to merge the opacity into a color 12729 */ 12730 function mergeOpacity(colorString, opacity) { 12731 var color = helpers.color(colorString); 12732 return color.alpha(opacity * color.alpha()).rgbaString(); 12733 } 12734 12735 // Helper to push or concat based on if the 2nd parameter is an array or not 12736 function pushOrConcat(base, toPush) { 12737 if (toPush) { 12738 if (helpers.isArray(toPush)) { 12739 // base = base.concat(toPush); 12740 Array.prototype.push.apply(base, toPush); 12741 } else { 12742 base.push(toPush); 12743 } 12744 } 12745 12746 return base; 12747 } 12748 12749 // Private helper to create a tooltip item model 12750 // @param element : the chart element (point, arc, bar) to create the tooltip item for 12751 // @return : new tooltip item 12752 function createTooltipItem(element) { 12753 var xScale = element._xScale; 12754 var yScale = element._yScale || element._scale; // handle radar || polarArea charts 12755 var index = element._index; 12756 var datasetIndex = element._datasetIndex; 12757 12758 return { 12759 xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', 12760 yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', 12761 index: index, 12762 datasetIndex: datasetIndex, 12763 x: element._model.x, 12764 y: element._model.y 12765 }; 12766 } 12767 12768 /** 12769 * Helper to get the reset model for the tooltip 12770 * @param tooltipOpts {Object} the tooltip options 12771 */ 12772 function getBaseModel(tooltipOpts) { 12773 var globalDefaults = defaults.global; 12774 var valueOrDefault = helpers.valueOrDefault; 12775 12776 return { 12777 // Positioning 12778 xPadding: tooltipOpts.xPadding, 12779 yPadding: tooltipOpts.yPadding, 12780 xAlign: tooltipOpts.xAlign, 12781 yAlign: tooltipOpts.yAlign, 12782 12783 // Body 12784 bodyFontColor: tooltipOpts.bodyFontColor, 12785 _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), 12786 _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), 12787 _bodyAlign: tooltipOpts.bodyAlign, 12788 bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), 12789 bodySpacing: tooltipOpts.bodySpacing, 12790 12791 // Title 12792 titleFontColor: tooltipOpts.titleFontColor, 12793 _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), 12794 _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), 12795 titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), 12796 _titleAlign: tooltipOpts.titleAlign, 12797 titleSpacing: tooltipOpts.titleSpacing, 12798 titleMarginBottom: tooltipOpts.titleMarginBottom, 12799 12800 // Footer 12801 footerFontColor: tooltipOpts.footerFontColor, 12802 _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), 12803 _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), 12804 footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), 12805 _footerAlign: tooltipOpts.footerAlign, 12806 footerSpacing: tooltipOpts.footerSpacing, 12807 footerMarginTop: tooltipOpts.footerMarginTop, 12808 12809 // Appearance 12810 caretSize: tooltipOpts.caretSize, 12811 cornerRadius: tooltipOpts.cornerRadius, 12812 backgroundColor: tooltipOpts.backgroundColor, 12813 opacity: 0, 12814 legendColorBackground: tooltipOpts.multiKeyBackground, 12815 displayColors: tooltipOpts.displayColors, 12816 borderColor: tooltipOpts.borderColor, 12817 borderWidth: tooltipOpts.borderWidth 12818 }; 12819 } 12820 12821 /** 12822 * Get the size of the tooltip 12823 */ 12824 function getTooltipSize(tooltip, model) { 12825 var ctx = tooltip._chart.ctx; 12826 12827 var height = model.yPadding * 2; // Tooltip Padding 12828 var width = 0; 12829 12830 // Count of all lines in the body 12831 var body = model.body; 12832 var combinedBodyLength = body.reduce(function(count, bodyItem) { 12833 return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; 12834 }, 0); 12835 combinedBodyLength += model.beforeBody.length + model.afterBody.length; 12836 12837 var titleLineCount = model.title.length; 12838 var footerLineCount = model.footer.length; 12839 var titleFontSize = model.titleFontSize; 12840 var bodyFontSize = model.bodyFontSize; 12841 var footerFontSize = model.footerFontSize; 12842 12843 height += titleLineCount * titleFontSize; // Title Lines 12844 height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing 12845 height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin 12846 height += combinedBodyLength * bodyFontSize; // Body Lines 12847 height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing 12848 height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin 12849 height += footerLineCount * (footerFontSize); // Footer Lines 12850 height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing 12851 12852 // Title width 12853 var widthPadding = 0; 12854 var maxLineWidth = function(line) { 12855 width = Math.max(width, ctx.measureText(line).width + widthPadding); 12856 }; 12857 12858 ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); 12859 helpers.each(model.title, maxLineWidth); 12860 12861 // Body width 12862 ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); 12863 helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); 12864 12865 // Body lines may include some extra width due to the color box 12866 widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; 12867 helpers.each(body, function(bodyItem) { 12868 helpers.each(bodyItem.before, maxLineWidth); 12869 helpers.each(bodyItem.lines, maxLineWidth); 12870 helpers.each(bodyItem.after, maxLineWidth); 12871 }); 12872 12873 // Reset back to 0 12874 widthPadding = 0; 12875 12876 // Footer width 12877 ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); 12878 helpers.each(model.footer, maxLineWidth); 12879 12880 // Add padding 12881 width += 2 * model.xPadding; 12882 12883 return { 12884 width: width, 12885 height: height 12886 }; 12887 } 12888 12889 /** 12890 * Helper to get the alignment of a tooltip given the size 12891 */ 12892 function determineAlignment(tooltip, size) { 12893 var model = tooltip._model; 12894 var chart = tooltip._chart; 12895 var chartArea = tooltip._chart.chartArea; 12896 var xAlign = 'center'; 12897 var yAlign = 'center'; 12898 12899 if (model.y < size.height) { 12900 yAlign = 'top'; 12901 } else if (model.y > (chart.height - size.height)) { 12902 yAlign = 'bottom'; 12903 } 12904 12905 var lf, rf; // functions to determine left, right alignment 12906 var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart 12907 var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges 12908 var midX = (chartArea.left + chartArea.right) / 2; 12909 var midY = (chartArea.top + chartArea.bottom) / 2; 12910 12911 if (yAlign === 'center') { 12912 lf = function(x) { 12913 return x <= midX; 12914 }; 12915 rf = function(x) { 12916 return x > midX; 12917 }; 12918 } else { 12919 lf = function(x) { 12920 return x <= (size.width / 2); 12921 }; 12922 rf = function(x) { 12923 return x >= (chart.width - (size.width / 2)); 12924 }; 12925 } 12926 12927 olf = function(x) { 12928 return x + size.width + model.caretSize + model.caretPadding > chart.width; 12929 }; 12930 orf = function(x) { 12931 return x - size.width - model.caretSize - model.caretPadding < 0; 12932 }; 12933 yf = function(y) { 12934 return y <= midY ? 'top' : 'bottom'; 12935 }; 12936 12937 if (lf(model.x)) { 12938 xAlign = 'left'; 12939 12940 // Is tooltip too wide and goes over the right side of the chart.? 12941 if (olf(model.x)) { 12942 xAlign = 'center'; 12943 yAlign = yf(model.y); 12944 } 12945 } else if (rf(model.x)) { 12946 xAlign = 'right'; 12947 12948 // Is tooltip too wide and goes outside left edge of canvas? 12949 if (orf(model.x)) { 12950 xAlign = 'center'; 12951 yAlign = yf(model.y); 12952 } 12953 } 12954 12955 var opts = tooltip._options; 12956 return { 12957 xAlign: opts.xAlign ? opts.xAlign : xAlign, 12958 yAlign: opts.yAlign ? opts.yAlign : yAlign 12959 }; 12960 } 12961 12962 /** 12963 * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment 12964 */ 12965 function getBackgroundPoint(vm, size, alignment, chart) { 12966 // Background Position 12967 var x = vm.x; 12968 var y = vm.y; 12969 12970 var caretSize = vm.caretSize; 12971 var caretPadding = vm.caretPadding; 12972 var cornerRadius = vm.cornerRadius; 12973 var xAlign = alignment.xAlign; 12974 var yAlign = alignment.yAlign; 12975 var paddingAndSize = caretSize + caretPadding; 12976 var radiusAndPadding = cornerRadius + caretPadding; 12977 12978 if (xAlign === 'right') { 12979 x -= size.width; 12980 } else if (xAlign === 'center') { 12981 x -= (size.width / 2); 12982 if (x + size.width > chart.width) { 12983 x = chart.width - size.width; 12984 } 12985 if (x < 0) { 12986 x = 0; 12987 } 12988 } 12989 12990 if (yAlign === 'top') { 12991 y += paddingAndSize; 12992 } else if (yAlign === 'bottom') { 12993 y -= size.height + paddingAndSize; 12994 } else { 12995 y -= (size.height / 2); 12996 } 12997 12998 if (yAlign === 'center') { 12999 if (xAlign === 'left') { 13000 x += paddingAndSize; 13001 } else if (xAlign === 'right') { 13002 x -= paddingAndSize; 13003 } 13004 } else if (xAlign === 'left') { 13005 x -= radiusAndPadding; 13006 } else if (xAlign === 'right') { 13007 x += radiusAndPadding; 13008 } 13009 13010 return { 13011 x: x, 13012 y: y 13013 }; 13014 } 13015 13016 Chart.Tooltip = Element.extend({ 13017 initialize: function() { 13018 this._model = getBaseModel(this._options); 13019 this._lastActive = []; 13020 }, 13021 13022 // Get the title 13023 // Args are: (tooltipItem, data) 13024 getTitle: function() { 13025 var me = this; 13026 var opts = me._options; 13027 var callbacks = opts.callbacks; 13028 13029 var beforeTitle = callbacks.beforeTitle.apply(me, arguments); 13030 var title = callbacks.title.apply(me, arguments); 13031 var afterTitle = callbacks.afterTitle.apply(me, arguments); 13032 13033 var lines = [];
13034 lines = pushOrConcat(lines, beforeTitle); 13035 lines = pushOrConcat(lines, title); 13036 lines = pushOrConcat(lines, afterTitle); 13037 13038 return lines; 13039 }, 13040 13041 // Args are: (tooltipItem, data) 13042 getBeforeBody: function() { 13043 var lines = this._options.callbacks.beforeBody.apply(this, arguments); 13044 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; 13045 }, 13046 13047 // Args are: (tooltipItem, data) 13048 getBody: function(tooltipItems, data) { 13049 var me = this; 13050 var callbacks = me._options.callbacks; 13051 var bodyItems = []; 13052 13053 helpers.each(tooltipItems, function(tooltipItem) { 13054 var bodyItem = { 13055 before: [], 13056 lines: [], 13057 after: [] 13058 }; 13059 pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); 13060 pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); 13061 pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); 13062 13063 bodyItems.push(bodyItem); 13064 }); 13065 13066 return bodyItems; 13067 }, 13068 13069 // Args are: (tooltipItem, data) 13070 getAfterBody: function() { 13071 var lines = this._options.callbacks.afterBody.apply(this, arguments); 13072 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; 13073 }, 13074 13075 // Get the footer and beforeFooter and afterFooter lines 13076 // Args are: (tooltipItem, data) 13077 getFooter: function() { 13078 var me = this; 13079 var callbacks = me._options.callbacks; 13080 13081 var beforeFooter = callbacks.beforeFooter.apply(me, arguments); 13082 var footer = callbacks.footer.apply(me, arguments); 13083 var afterFooter = callbacks.afterFooter.apply(me, arguments); 13084 13085 var lines = []; 13086 lines = pushOrConcat(lines, beforeFooter); 13087 lines = pushOrConcat(lines, footer); 13088 lines = pushOrConcat(lines, afterFooter); 13089 13090 return lines; 13091 }, 13092 13093 update: function(changed) { 13094 var me = this; 13095 var opts = me._options; 13096 13097 // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition 13098 // 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 13099 // which breaks any animations. 13100 var existingModel = me._model; 13101 var model = me._model = getBaseModel(opts); 13102 var active = me._active; 13103 13104 var data = me._data; 13105 13106 // In the case where active.length === 0 we need to keep these at existing values for good animations 13107 var alignment = { 13108 xAlign: existingModel.xAlign, 13109 yAlign: existingModel.yAlign 13110 }; 13111 var backgroundPoint = { 13112 x: existingModel.x, 13113 y: existingModel.y 13114 }; 13115 var tooltipSize = { 13116 width: existingModel.width, 13117 height: existingModel.height 13118 }; 13119 var tooltipPosition = { 13120 x: existingModel.caretX, 13121 y: existingModel.caretY 13122 }; 13123 13124 var i, len; 13125 13126 if (active.length) { 13127 model.opacity = 1; 13128 13129 var labelColors = []; 13130 var labelTextColors = []; 13131 tooltipPosition = Chart.Tooltip.positioners[opts.position].call(me, active, me._eventPosition); 13132 13133 var tooltipItems = []; 13134 for (i = 0, len = active.length; i < len; ++i) { 13135 tooltipItems.push(createTooltipItem(active[i])); 13136 } 13137 13138 // If the user provided a filter function, use it to modify the tooltip items 13139 if (opts.filter) { 13140 tooltipItems = tooltipItems.filter(function(a) { 13141 return opts.filter(a, data); 13142 }); 13143 } 13144 13145 // If the user provided a sorting function, use it to modify the tooltip items 13146 if (opts.itemSort) { 13147 tooltipItems = tooltipItems.sort(function(a, b) { 13148 return opts.itemSort(a, b, data); 13149 }); 13150 } 13151 13152 // Determine colors for boxes 13153 helpers.each(tooltipItems, function(tooltipItem) { 13154 labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); 13155 labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); 13156 }); 13157 13158 13159 // Build the Text Lines 13160 model.title = me.getTitle(tooltipItems, data); 13161 model.beforeBody = me.getBeforeBody(tooltipItems, data); 13162 model.body = me.getBody(tooltipItems, data); 13163 model.afterBody = me.getAfterBody(tooltipItems, data); 13164 model.footer = me.getFooter(tooltipItems, data); 13165 13166 // Initial positioning and colors 13167 model.x = Math.round(tooltipPosition.x); 13168 model.y = Math.round(tooltipPosition.y); 13169 model.caretPadding = opts.caretPadding; 13170 model.labelColors = labelColors; 13171 model.labelTextColors = labelTextColors; 13172 13173 // data points 13174 model.dataPoints = tooltipItems; 13175 13176 // We need to determine alignment of the tooltip 13177 tooltipSize = getTooltipSize(this, model); 13178 alignment = determineAlignment(this, tooltipSize); 13179 // Final Size and Position
13180 backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); 13181 } else { 13182 model.opacity = 0; 13183 } 13184 13185 model.xAlign = alignment.xAlign; 13186 model.yAlign = alignment.yAlign; 13187 model.x = backgroundPoint.x; 13188 model.y = backgroundPoint.y; 13189 model.width = tooltipSize.width; 13190 model.height = tooltipSize.height; 13191 13192 // Point where the caret on the tooltip points to 13193 model.caretX = tooltipPosition.x; 13194 model.caretY = tooltipPosition.y; 13195 13196 me._model = model; 13197 13198 if (changed && opts.custom) { 13199 opts.custom.call(me, model); 13200 } 13201 13202 return me; 13203 }, 13204 drawCaret: function(tooltipPoint, size) { 13205 var ctx = this._chart.ctx; 13206 var vm = this._view; 13207 var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); 13208 13209 ctx.lineTo(caretPosition.x1, caretPosition.y1); 13210 ctx.lineTo(caretPosition.x2, caretPosition.y2); 13211 ctx.lineTo(caretPosition.x3, caretPosition.y3); 13212 }, 13213 getCaretPosition: function(tooltipPoint, size, vm) { 13214 var x1, x2, x3, y1, y2, y3; 13215 var caretSize = vm.caretSize; 13216 var cornerRadius = vm.cornerRadius; 13217 var xAlign = vm.xAlign; 13218 var yAlign = vm.yAlign; 13219 var ptX = tooltipPoint.x; 13220 var ptY = tooltipPoint.y; 13221 var width = size.width; 13222 var height = size.height; 13223 13224 if (yAlign === 'center') { 13225 y2 = ptY + (height / 2); 13226 13227 if (xAlign === 'left') { 13228 x1 = ptX; 13229 x2 = x1 - caretSize; 13230 x3 = x1; 13231 13232 y1 = y2 + caretSize; 13233 y3 = y2 - caretSize; 13234 } else { 13235 x1 = ptX + width; 13236 x2 = x1 + caretSize; 13237 x3 = x1; 13238 13239 y1 = y2 - caretSize; 13240 y3 = y2 + caretSize; 13241 } 13242 } else { 13243 if (xAlign === 'left') { 13244 x2 = ptX + cornerRadius + (caretSize); 13245 x1 = x2 - caretSize; 13246 x3 = x2 + caretSize; 13247 } else if (xAlign === 'right') { 13248 x2 = ptX + width - cornerRadius - caretSize; 13249 x1 = x2 - caretSize; 13250 x3 = x2 + caretSize; 13251 } else { 13252 x2 = vm.caretX; 13253 x1 = x2 - caretSize; 13254 x3 = x2 + caretSize; 13255 } 13256 if (yAlign === 'top') { 13257 y1 = ptY; 13258 y2 = y1 - caretSize; 13259 y3 = y1; 13260 } else { 13261 y1 = ptY + height; 13262 y2 = y1 + caretSize; 13263 y3 = y1; 13264 // invert drawing order 13265 var tmp = x3; 13266 x3 = x1; 13267 x1 = tmp; 13268 } 13269 } 13270 return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; 13271 }, 13272 drawTitle: function(pt, vm, ctx, opacity) { 13273 var title = vm.title; 13274 13275 if (title.length) { 13276 ctx.textAlign = vm._titleAlign; 13277 ctx.textBaseline = 'top'; 13278 13279 var titleFontSize = vm.titleFontSize; 13280 var titleSpacing = vm.titleSpacing; 13281 13282 ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); 13283 ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); 13284 13285 var i, len; 13286 for (i = 0, len = title.length; i < len; ++i) { 13287 ctx.fillText(title[i], pt.x, pt.y); 13288 pt.y += titleFontSize + titleSpacing; // Line Height and spacing 13289 13290 if (i + 1 === title.length) { 13291 pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing 13292 } 13293 } 13294 } 13295 }, 13296 drawBody: function(pt, vm, ctx, opacity) { 13297 var bodyFontSize = vm.bodyFontSize; 13298 var bodySpacing = vm.bodySpacing; 13299 var body = vm.body; 13300 13301 ctx.textAlign = vm._bodyAlign; 13302 ctx.textBaseline = 'top'; 13303 ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); 13304 13305 // Before Body 13306 var xLinePadding = 0; 13307 var fillLineOfText = function(line) { 13308 ctx.fillText(line, pt.x + xLinePadding, pt.y); 13309 pt.y += bodyFontSize + bodySpacing; 13310 }; 13311 13312 // Before body lines 13313 ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity); 13314 helpers.each(vm.beforeBody, fillLineOfText); 13315 13316 var drawColorBoxes = vm.displayColors; 13317 xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; 13318 13319 // Draw body lines now 13320 helpers.each(body, function(bodyItem, i) { 13321 var textColor = mergeOpacity(vm.labelTextColors[i], opacity); 13322 ctx.fillStyle = textColor; 13323 helpers.each(bodyItem.before, fillLineOfText); 13324 13325 helpers.each(bodyItem.lines, function(line) { 13326 // Draw Legend-like boxes if needed 13327 if (drawColorBoxes) { 13328 // Fill a white rect so that colours merge nicely if the opacity is < 1
13329 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); 13330 ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); 13331 13332 // Border 13333 ctx.lineWidth = 1; 13334 ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); 13335 ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); 13336 13337 // Inner square 13338 ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); 13339 ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); 13340 ctx.fillStyle = textColor; 13341 } 13342 13343 fillLineOfText(line); 13344 }); 13345 13346 helpers.each(bodyItem.after, fillLineOfText); 13347 }); 13348 13349 // Reset back to 0 for after body 13350 xLinePadding = 0; 13351 13352 // After body lines 13353 helpers.each(vm.afterBody, fillLineOfText); 13354 pt.y -= bodySpacing; // Remove last body spacing 13355 }, 13356 drawFooter: function(pt, vm, ctx, opacity) { 13357 var footer = vm.footer; 13358 13359 if (footer.length) { 13360 pt.y += vm.footerMarginTop; 13361 13362 ctx.textAlign = vm._footerAlign; 13363 ctx.textBaseline = 'top'; 13364 13365 ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); 13366 ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); 13367 13368 helpers.each(footer, function(line) { 13369 ctx.fillText(line, pt.x, pt.y); 13370 pt.y += vm.footerFontSize + vm.footerSpacing; 13371 }); 13372 } 13373 }, 13374 drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { 13375 ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); 13376 ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); 13377 ctx.lineWidth = vm.borderWidth; 13378 var xAlign = vm.xAlign; 13379 var yAlign = vm.yAlign; 13380 var x = pt.x; 13381 var y = pt.y; 13382 var width = tooltipSize.width; 13383 var height = tooltipSize.height; 13384 var radius = vm.cornerRadius; 13385 13386 ctx.beginPath(); 13387 ctx.moveTo(x + radius, y); 13388 if (yAlign === 'top') { 13389 this.drawCaret(pt, tooltipSize); 13390 } 13391 ctx.lineTo(x + width - radius, y); 13392 ctx.quadraticCurveTo(x + width, y, x + width, y + radius); 13393 if (yAlign === 'center' && xAlign === 'right') { 13394 this.drawCaret(pt, tooltipSize); 13395 } 13396 ctx.lineTo(x + width, y + height - radius); 13397 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); 13398 if (yAlign === 'bottom') { 13399 this.drawCaret(pt, tooltipSize); 13400 } 13401 ctx.lineTo(x + radius, y + height); 13402 ctx.quadraticCurveTo(x, y + height, x, y + height - radius); 13403 if (yAlign === 'center' && xAlign === 'left') { 13404 this.drawCaret(pt, tooltipSize); 13405 } 13406 ctx.lineTo(x, y + radius); 13407 ctx.quadraticCurveTo(x, y, x + radius, y); 13408 ctx.closePath(); 13409 13410 ctx.fill(); 13411 13412 if (vm.borderWidth > 0) { 13413 ctx.stroke(); 13414 } 13415 }, 13416 draw: function() { 13417 var ctx = this._chart.ctx; 13418 var vm = this._view; 13419 13420 if (vm.opacity === 0) { 13421 return; 13422 } 13423 13424 var tooltipSize = { 13425 width: vm.width, 13426 height: vm.height 13427 }; 13428 var pt = { 13429 x: vm.x, 13430 y: vm.y 13431 }; 13432 13433 // IE11/Edge does not like very small opacities, so snap to 0 13434 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; 13435 13436 // Truthy/falsey value for empty tooltip 13437 var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; 13438 13439 if (this._options.enabled && hasTooltipContent) { 13440 // Draw Background 13441 this.drawBackground(pt, vm, ctx, tooltipSize, opacity); 13442 13443 // Draw Title, Body, and Footer 13444 pt.x += vm.xPadding; 13445 pt.y += vm.yPadding; 13446 13447 // Titles 13448 this.drawTitle(pt, vm, ctx, opacity); 13449 13450 // Body 13451 this.drawBody(pt, vm, ctx, opacity); 13452 13453 // Footer 13454 this.drawFooter(pt, vm, ctx, opacity); 13455 } 13456 }, 13457 13458 /** 13459 * Handle an event 13460 * @private 13461 * @param {IEvent} event - The event to handle 13462 * @returns {Boolean} true if the tooltip changed 13463 */ 13464 handleEvent: function(e) { 13465 var me = this; 13466 var options = me._options; 13467 var changed = false; 13468 13469 me._lastActive = me._lastActive || []; 13470 13471 // Find Active Elements for tooltips 13472 if (e.type === 'mouseout') { 13473 me._active = []; 13474 } else { 13475 me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); 13476 } 13477 13478 // Remember Last Actives 13479 changed = !helpers.arrayEquals(me._active, me._lastActive); 13480 13481 // Only handle target event on tooltip change 13482 if (changed) { 13483 me._lastActive = me._active; 13484 13485 if (options.enabled || options.custom) { 13486 me._eventPosition = { 13487 x: e.x, 13488 y: e.y 13489 }; 13490 13491 me.update(true); 13492 me.pivot(); 13493 } 13494 } 13495 13496 return changed; 13497 } 13498 }); 13499 13500 /** 13501 * @namespace Chart.Tooltip.positioners 13502 */ 13503 Chart.Tooltip.positioners = { 13504 /** 13505 * Average mode places the tooltip at the average position of the elements shown 13506 * @function Chart.Tooltip.positioners.average 13507 * @param elements {ChartElement[]} the elements being displayed in the tooltip 13508 * @returns {Point} tooltip position 13509 */ 13510 average: function(elements) { 13511 if (!elements.length) { 13512 return false; 13513 } 13514 13515 var i, len; 13516 var x = 0; 13517 var y = 0; 13518 var count = 0; 13519 13520 for (i = 0, len = elements.length; i < len; ++i) { 13521 var el = elements[i]; 13522 if (el && el.hasValue()) { 13523 var pos = el.tooltipPosition(); 13524 x += pos.x; 13525 y += pos.y; 13526 ++count; 13527 } 13528 } 13529 13530 return { 13531 x: Math.round(x / count), 13532 y: Math.round(y / count) 13533 }; 13534 }, 13535 13536 /** 13537 * Gets the tooltip position nearest of the item nearest to the event position 13538 * @function Chart.Tooltip.positioners.nearest 13539 * @param elements {Chart.Element[]} the tooltip elements 13540 * @param eventPosition {Point} the position of the event in canvas coordinates 13541 * @returns {Point} the tooltip position 13542 */ 13543 nearest: function(elements, eventPosition) { 13544 var x = eventPosition.x; 13545 var y = eventPosition.y; 13546 var minDistance = Number.POSITIVE_INFINITY; 13547 var i, len, nearestElement; 13548 13549 for (i = 0, len = elements.length; i < len; ++i) { 13550 var el = elements[i]; 13551 if (el && el.hasValue()) { 13552 var center = el.getCenterPoint(); 13553 var d = helpers.distanceBetweenPoints(eventPosition, center); 13554 13555 if (d < minDistance) { 13556 minDistance = d; 13557 nearestElement = el; 13558 } 13559 } 13560 } 13561 13562 if (nearestElement) { 13563 var tp = nearestElement.tooltipPosition(); 13564 x = tp.x; 13565 y = tp.y; 13566 } 13567 13568 return { 13569 x: x, 13570 y: y 13571 }; 13572 } 13573 }; 13574}; 13575 13576},{"25":25,"26":26,"45":45}],36:[function(require,module,exports){ 13577'use strict'; 13578 13579var defaults = require(25); 13580var Element = require(26); 13581var helpers = require(45); 13582 13583defaults._set('global', { 13584 elements: { 13585 arc: { 13586 backgroundColor: defaults.global.defaultColor, 13587 borderColor: '#fff', 13588 borderWidth: 2 13589 } 13590 } 13591}); 13592 13593module.exports = Element.extend({ 13594 inLabelRange: function(mouseX) { 13595 var vm = this._view; 13596 13597 if (vm) { 13598 return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
13599 } 13600 return false; 13601 }, 13602 13603 inRange: function(chartX, chartY) { 13604 var vm = this._view; 13605 13606 if (vm) { 13607 var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); 13608 var angle = pointRelativePosition.angle; 13609 var distance = pointRelativePosition.distance; 13610 13611 // Sanitise angle range 13612 var startAngle = vm.startAngle; 13613 var endAngle = vm.endAngle; 13614 while (endAngle < startAngle) { 13615 endAngle += 2.0 * Math.PI; 13616 } 13617 while (angle > endAngle) { 13618 angle -= 2.0 * Math.PI; 13619 } 13620 while (angle < startAngle) { 13621 angle += 2.0 * Math.PI; 13622 } 13623 13624 // Check if within the range of the open/close angle 13625 var betweenAngles = (angle >= startAngle && angle <= endAngle); 13626 var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); 13627 13628 return (betweenAngles && withinRadius); 13629 } 13630 return false; 13631 }, 13632 13633 getCenterPoint: function() { 13634 var vm = this._view; 13635 var halfAngle = (vm.startAngle + vm.endAngle) / 2; 13636 var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; 13637 return { 13638 x: vm.x + Math.cos(halfAngle) * halfRadius, 13639 y: vm.y + Math.sin(halfAngle) * halfRadius 13640 }; 13641 }, 13642 13643 getArea: function() { 13644 var vm = this._view; 13645 return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); 13646 }, 13647 13648 tooltipPosition: function() { 13649 var vm = this._view; 13650 var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); 13651 var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; 13652 13653 return { 13654 x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), 13655 y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) 13656 }; 13657 }, 13658 13659 draw: function() { 13660 var ctx = this._chart.ctx; 13661 var vm = this._view; 13662 var sA = vm.startAngle; 13663 var eA = vm.endAngle; 13664 13665 ctx.beginPath(); 13666 13667 ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); 13668 ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); 13669 13670 ctx.closePath(); 13671 ctx.strokeStyle = vm.borderColor; 13672 ctx.lineWidth = vm.borderWidth; 13673 13674 ctx.fillStyle = vm.backgroundColor; 13675 13676 ctx.fill(); 13677 ctx.lineJoin = 'bevel'; 13678 13679 if (vm.borderWidth) { 13680 ctx.stroke(); 13681 } 13682 } 13683}); 13684 13685},{"25":25,"26":26,"45":45}],37:[function(require,module,exports){ 13686'use strict'; 13687 13688var defaults = require(25); 13689var Element = require(26); 13690var helpers = require(45); 13691 13692var globalDefaults = defaults.global; 13693 13694defaults._set('global', { 13695 elements: { 13696 line: { 13697 tension: 0.4, 13698 backgroundColor: globalDefaults.defaultColor, 13699 borderWidth: 3, 13700 borderColor: globalDefaults.defaultColor, 13701 borderCapStyle: 'butt', 13702 borderDash: [], 13703 borderDashOffset: 0.0, 13704 borderJoinStyle: 'miter', 13705 capBezierPoints: true, 13706 fill: true, // do we fill in the area between the line and its base axis 13707 } 13708 } 13709}); 13710 13711module.exports = Element.extend({ 13712 draw: function() { 13713 var me = this; 13714 var vm = me._view; 13715 var ctx = me._chart.ctx; 13716 var spanGaps = vm.spanGaps; 13717 var points = me._children.slice(); // clone array 13718 var globalOptionLineElements = globalDefaults.elements.line; 13719 var lastDrawnIndex = -1; 13720 var index, current, previous, currentVM; 13721 13722 // If we are looping, adding the first point again 13723 if (me._loop && points.length) { 13724 points.push(points[0]); 13725 } 13726 13727 ctx.save(); 13728 13729 // Stroke Line Options 13730 ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; 13731 13732 // IE 9 and 10 do not support line dash 13733 if (ctx.setLineDash) { 13734 ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); 13735 } 13736 13737 ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; 13738 ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; 13739 ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; 13740 ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; 13741 13742 // Stroke Line 13743 ctx.beginPath(); 13744 lastDrawnIndex = -1; 13745 13746 for (index = 0; index < points.length; ++index) { 13747 current = points[index]; 13748 previous = helpers.previousItem(points, index); 13749 currentVM = current._view; 13750 13751 // First point moves to it's starting position no matter what 13752 if (index === 0) { 13753 if (!currentVM.skip) { 13754 ctx.moveTo(currentVM.x, currentVM.y); 13755 lastDrawnIndex = index; 13756 } 13757 } else { 13758 previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; 13759 13760 if (!currentVM.skip) { 13761 if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { 13762 // There was a gap and this is the first point after the gap 13763 ctx.moveTo(currentVM.x, currentVM.y); 13764 } else { 13765 // Line to next point 13766 helpers.canvas.lineTo(ctx, previous._view, current._view); 13767 } 13768 lastDrawnIndex = index; 13769 } 13770 } 13771 } 13772 13773 ctx.stroke();
13774 ctx.restore(); 13775 } 13776}); 13777 13778},{"25":25,"26":26,"45":45}],38:[function(require,module,exports){ 13779'use strict'; 13780 13781var defaults = require(25); 13782var Element = require(26); 13783var helpers = require(45); 13784 13785var defaultColor = defaults.global.defaultColor; 13786 13787defaults._set('global', { 13788 elements: { 13789 point: { 13790 radius: 3, 13791 pointStyle: 'circle', 13792 backgroundColor: defaultColor, 13793 borderColor: defaultColor, 13794 borderWidth: 1, 13795 // Hover 13796 hitRadius: 1, 13797 hoverRadius: 4, 13798 hoverBorderWidth: 1 13799 } 13800 } 13801}); 13802 13803function xRange(mouseX) { 13804 var vm = this._view; 13805 return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; 13806} 13807 13808function yRange(mouseY) { 13809 var vm = this._view; 13810 return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; 13811} 13812 13813module.exports = Element.extend({ 13814 inRange: function(mouseX, mouseY) { 13815 var vm = this._view; 13816 return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; 13817 }, 13818 13819 inLabelRange: xRange, 13820 inXRange: xRange, 13821 inYRange: yRange, 13822 13823 getCenterPoint: function() { 13824 var vm = this._view; 13825 return { 13826 x: vm.x, 13827 y: vm.y 13828 }; 13829 }, 13830 13831 getArea: function() { 13832 return Math.PI * Math.pow(this._view.radius, 2); 13833 }, 13834 13835 tooltipPosition: function() { 13836 var vm = this._view; 13837 return { 13838 x: vm.x, 13839 y: vm.y, 13840 padding: vm.radius + vm.borderWidth 13841 }; 13842 }, 13843 13844 draw: function(chartArea) { 13845 var vm = this._view; 13846 var model = this._model; 13847 var ctx = this._chart.ctx; 13848 var pointStyle = vm.pointStyle; 13849 var radius = vm.radius; 13850 var x = vm.x; 13851 var y = vm.y; 13852 var color = helpers.color; 13853 var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) 13854 var ratio = 0; 13855 13856 if (vm.skip) { 13857 return; 13858 } 13859 13860 ctx.strokeStyle = vm.borderColor || defaultColor; 13861 ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); 13862 ctx.fillStyle = vm.backgroundColor || defaultColor; 13863 13864 // Cliping for Points. 13865 // going out from inner charArea? 13866 if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) { 13867 // Point fade out 13868 if (model.x < chartArea.left) { 13869 ratio = (x - model.x) / (chartArea.left - model.x); 13870 } else if (chartArea.right * errMargin < model.x) { 13871 ratio = (model.x - x) / (model.x - chartArea.right); 13872 } else if (model.y < chartArea.top) { 13873 ratio = (y - model.y) / (chartArea.top - model.y); 13874 } else if (chartArea.bottom * errMargin < model.y) { 13875 ratio = (model.y - y) / (model.y - chartArea.bottom); 13876 } 13877 ratio = Math.round(ratio * 100) / 100; 13878 ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); 13879 ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); 13880 } 13881 13882 helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y); 13883 } 13884}); 13885 13886},{"25":25,"26":26,"45":45}],39:[function(require,module,exports){ 13887'use strict'; 13888 13889var defaults = require(25); 13890var Element = require(26); 13891 13892defaults._set('global', { 13893 elements: { 13894 rectangle: { 13895 backgroundColor: defaults.global.defaultColor, 13896 borderColor: defaults.global.defaultColor, 13897 borderSkipped: 'bottom', 13898 borderWidth: 0 13899 } 13900 } 13901}); 13902 13903function isVertical(bar) { 13904 return bar._view.width !== undefined; 13905} 13906 13907/** 13908 * Helper function to get the bounds of the bar regardless of the orientation 13909 * @param bar {Chart.Element.Rectangle} the bar 13910 * @return {Bounds} bounds of the bar 13911 * @private 13912 */ 13913function getBarBounds(bar) { 13914 var vm = bar._view; 13915 var x1, x2, y1, y2; 13916 13917 if (isVertical(bar)) { 13918 // vertical 13919 var halfWidth = vm.width / 2; 13920 x1 = vm.x - halfWidth; 13921 x2 = vm.x + halfWidth; 13922 y1 = Math.min(vm.y, vm.base); 13923 y2 = Math.max(vm.y, vm.base); 13924 } else { 13925 // horizontal bar 13926 var halfHeight = vm.height / 2; 13927 x1 = Math.min(vm.x, vm.base); 13928 x2 = Math.max(vm.x, vm.base); 13929 y1 = vm.y - halfHeight; 13930 y2 = vm.y + halfHeight; 13931 } 13932 13933 return { 13934 left: x1, 13935 top: y1, 13936 right: x2, 13937 bottom: y2 13938 }; 13939} 13940 13941module.exports = Element.extend({ 13942 draw: function() { 13943 var ctx = this._chart.ctx; 13944 var vm = this._view; 13945 var left, right, top, bottom, signX, signY, borderSkipped; 13946 var borderWidth = vm.borderWidth; 13947 13948 if (!vm.horizontal) { 13949 // bar 13950 left = vm.x - vm.width / 2; 13951 right = vm.x + vm.width / 2; 13952 top = vm.y; 13953 bottom = vm.base; 13954 signX = 1; 13955 signY = bottom > top ? 1 : -1; 13956 borderSkipped = vm.borderSkipped || 'bottom'; 13957 } else { 13958 // horizontal bar 13959 left = vm.base; 13960 right = vm.x; 13961 top = vm.y - vm.height / 2; 13962 bottom = vm.y + vm.height / 2; 13963 signX = right > left ? 1 : -1; 13964 signY = 1; 13965 borderSkipped = vm.borderSkipped || 'left'; 13966 } 13967 13968 // Canvas doesn't allow us to stroke inside the width so we can 13969 // adjust the sizes to fit if we're setting a stroke on the line 13970 if (borderWidth) { 13971 // borderWidth shold be less than bar width and bar height. 13972 var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); 13973 borderWidth = borderWidth > barSize ? barSize : borderWidth; 13974 var halfStroke = borderWidth / 2; 13975 // Adjust borderWidth when bar top position is near vm.base(zero). 13976 var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); 13977 var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); 13978 var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); 13979 var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); 13980 // not become a vertical line? 13981 if (borderLeft !== borderRight) { 13982 top = borderTop; 13983 bottom = borderBottom; 13984 } 13985 // not become a horizontal line? 13986 if (borderTop !== borderBottom) { 13987 left = borderLeft; 13988 right = borderRight; 13989 } 13990 } 13991 13992 ctx.beginPath(); 13993 ctx.fillStyle = vm.backgroundColor; 13994 ctx.strokeStyle = vm.borderColor; 13995 ctx.lineWidth = borderWidth; 13996 13997 // Corner points, from bottom-left to bottom-right clockwise 13998 // | 1 2 | 13999 // | 0 3 | 14000 var corners = [ 14001 [left, bottom], 14002 [left, top], 14003 [right, top], 14004 [right, bottom] 14005 ]; 14006 14007 // Find first (starting) corner with fallback to 'bottom' 14008 var borders = ['bottom', 'left', 'top', 'right']; 14009 var startCorner = borders.indexOf(borderSkipped, 0); 14010 if (startCorner === -1) { 14011 startCorner = 0; 14012 } 14013 14014 function cornerAt(index) { 14015 return corners[(startCorner + index) % 4]; 14016 } 14017 14018 // Draw rectangle from 'startCorner' 14019 var corner = cornerAt(0); 14020 ctx.moveTo(corner[0], corner[1]); 14021 14022 for (var i = 1; i < 4; i++) { 14023 corner = cornerAt(i); 14024 ctx.lineTo(corner[0], corner[1]); 14025 } 14026 14027 ctx.fill(); 14028 if (borderWidth) { 14029 ctx.stroke(); 14030 } 14031 }, 14032 14033 height: function() { 14034 var vm = this._view; 14035 return vm.base - vm.y; 14036 }, 14037 14038 inRange: function(mouseX, mouseY) { 14039 var inRange = false; 14040 14041 if (this._view) { 14042 var bounds = getBarBounds(this); 14043 inRange = mouseX >
14043= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; 14044 } 14045 14046 return inRange; 14047 }, 14048 14049 inLabelRange: function(mouseX, mouseY) { 14050 var me = this; 14051 if (!me._view) { 14052 return false; 14053 } 14054 14055 var inRange = false; 14056 var bounds = getBarBounds(me); 14057 14058 if (isVertical(me)) { 14059 inRange = mouseX >= bounds.left && mouseX <= bounds.right; 14060 } else { 14061 inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; 14062 } 14063 14064 return inRange; 14065 }, 14066 14067 inXRange: function(mouseX) { 14068 var bounds = getBarBounds(this); 14069 return mouseX >= bounds.left && mouseX <= bounds.right; 14070 }, 14071 14072 inYRange: function(mouseY) { 14073 var bounds = getBarBounds(this); 14074 return mouseY >= bounds.top && mouseY <= bounds.bottom; 14075 }, 14076 14077 getCenterPoint: function() { 14078 var vm = this._view; 14079 var x, y; 14080 if (isVertical(this)) { 14081 x = vm.x; 14082 y = (vm.y + vm.base) / 2; 14083 } else { 14084 x = (vm.x + vm.base) / 2; 14085 y = vm.y; 14086 } 14087 14088 return {x: x, y: y}; 14089 }, 14090 14091 getArea: function() { 14092 var vm = this._view; 14093 return vm.width * Math.abs(vm.y - vm.base); 14094 }, 14095 14096 tooltipPosition: function() { 14097 var vm = this._view; 14098 return { 14099 x: vm.x, 14100 y: vm.y 14101 }; 14102 } 14103}); 14104 14105},{"25":25,"26":26}],40:[function(require,module,exports){ 14106'use strict'; 14107 14108module.exports = {}; 14109module.exports.Arc = require(36); 14110module.exports.Line = require(37); 14111module.exports.Point = require(38); 14112module.exports.Rectangle = require(39); 14113 14114},{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){ 14115'use strict'; 14116 14117var helpers = require(42); 14118 14119/** 14120 * @namespace Chart.helpers.canvas 14121 */ 14122var exports = module.exports = { 14123 /** 14124 * Clears the entire canvas associated to the given `chart`. 14125 * @param {Chart} chart - The chart for which to clear the canvas. 14126 */ 14127 clear: function(chart) { 14128 chart.ctx.clearRect(0, 0, chart.width, chart.height); 14129 }, 14130 14131 /** 14132 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a 14133 * given size (width, height) and the same `radius` for all corners. 14134 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. 14135 * @param {Number} x - The x axis of the coordinate for the rectangle starting point. 14136 * @param {Number} y - The y axis of the coordinate for the rectangle starting point. 14137 * @param {Number} width - The rectangle's width. 14138 * @param {Number} height - The rectangle's height. 14139 * @param {Number} radius - The rounded amount (in pixels) for the four corners. 14140 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? 14141 */ 14142 roundedRect: function(ctx, x, y, width, height, radius) { 14143 if (radius) { 14144 var rx = Math.min(radius, width / 2); 14145 var ry = Math.min(radius, height / 2); 14146 14147 ctx.moveTo(x + rx, y); 14148 ctx.lineTo(x + width - rx, y); 14149 ctx.quadraticCurveTo(x + width, y, x + width, y + ry); 14150 ctx.lineTo(x + width, y + height - ry); 14151 ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); 14152 ctx.lineTo(x + rx, y + height); 14153 ctx.quadraticCurveTo(x, y + height, x, y + height - ry); 14154 ctx.lineTo(x, y + ry); 14155 ctx.quadraticCurveTo(x, y, x + rx, y); 14156 } else { 14157 ctx.rect(x, y, width, height); 14158 } 14159 }, 14160 14161 drawPoint: function(ctx, style, radius, x, y) { 14162 var type, edgeLength, xOffset, yOffset, height, size; 14163 14164 if (style && typeof style === 'object') { 14165 type = style.toString(); 14166 if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { 14167 ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); 14168 return; 14169 } 14170 } 14171 14172 if (isNaN(radius) || radius <= 0) { 14173 return; 14174 } 14175 14176 switch (style) { 14177 // Default includes circle 14178 default: 14179 ctx.beginPath(); 14180 ctx.arc(x, y, radius, 0, Math.PI * 2); 14181 ctx.closePath(); 14182 ctx.fill(); 14183 break; 14184 case 'triangle': 14185 ctx.beginPath(); 14186 edgeLength = 3 * radius / Math.sqrt(3); 14187 height = edgeLength * Math.sqrt(3) / 2; 14188 ctx.moveTo(x - edgeLength / 2, y + height / 3); 14189 ctx.lineTo(x + edgeLength / 2, y + height / 3); 14190 ctx.lineTo(x, y - 2 * height / 3); 14191 ctx.closePath(); 14192 ctx.fill(); 14193 break; 14194 case 'rect': 14195 size = 1 / Math.SQRT2 * radius; 14196 ctx.beginPath(); 14197 ctx.fillRect(x - size, y - size, 2 * size, 2 * size); 14198 ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); 14199 break; 14200 case 'rectRounded': 14201 var offset = radius / Math.SQRT2; 14202 var leftX = x - offset; 14203 var topY = y - offset; 14204 var sideSize = Math.SQRT2 * radius; 14205 ctx.beginPath(); 14206 this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2); 14207 ctx.closePath(); 14208 ctx.fill(); 14209 break; 14210 case 'rectRot': 14211 size = 1 / Math.SQRT2 * radius; 14212 ctx.beginPath(); 14213 ctx.moveTo(x - size, y); 14214 ctx.lineTo(x, y + size); 14215 ctx.lineTo(x + size, y); 14216 ctx.lineTo(x, y - size); 14217 ctx.closePath(); 14218 ctx.fill(); 14219 break; 14220 case 'cross': 14221 ctx.beginPath(); 14222 ctx.moveTo(x, y + radius);
14223 ctx.lineTo(x, y - radius); 14224 ctx.moveTo(x - radius, y); 14225 ctx.lineTo(x + radius, y); 14226 ctx.closePath(); 14227 break; 14228 case 'crossRot': 14229 ctx.beginPath(); 14230 xOffset = Math.cos(Math.PI / 4) * radius; 14231 yOffset = Math.sin(Math.PI / 4) * radius; 14232 ctx.moveTo(x - xOffset, y - yOffset); 14233 ctx.lineTo(x + xOffset, y + yOffset); 14234 ctx.moveTo(x - xOffset, y + yOffset); 14235 ctx.lineTo(x + xOffset, y - yOffset); 14236 ctx.closePath(); 14237 break; 14238 case 'star': 14239 ctx.beginPath(); 14240 ctx.moveTo(x, y + radius); 14241 ctx.lineTo(x, y - radius); 14242 ctx.moveTo(x - radius, y); 14243 ctx.lineTo(x + radius, y); 14244 xOffset = Math.cos(Math.PI / 4) * radius; 14245 yOffset = Math.sin(Math.PI / 4) * radius; 14246 ctx.moveTo(x - xOffset, y - yOffset); 14247 ctx.lineTo(x + xOffset, y + yOffset); 14248 ctx.moveTo(x - xOffset, y + yOffset); 14249 ctx.lineTo(x + xOffset, y - yOffset); 14250 ctx.closePath(); 14251 break; 14252 case 'line': 14253 ctx.beginPath(); 14254 ctx.moveTo(x - radius, y); 14255 ctx.lineTo(x + radius, y); 14256 ctx.closePath(); 14257 break; 14258 case 'dash': 14259 ctx.beginPath(); 14260 ctx.moveTo(x, y); 14261 ctx.lineTo(x + radius, y); 14262 ctx.closePath(); 14263 break; 14264 } 14265 14266 ctx.stroke(); 14267 }, 14268 14269 clipArea: function(ctx, area) { 14270 ctx.save(); 14271 ctx.beginPath(); 14272 ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); 14273 ctx.clip(); 14274 }, 14275 14276 unclipArea: function(ctx) { 14277 ctx.restore(); 14278 }, 14279 14280 lineTo: function(ctx, previous, target, flip) { 14281 if (target.steppedLine) { 14282 if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { 14283 ctx.lineTo(previous.x, target.y); 14284 } else { 14285 ctx.lineTo(target.x, previous.y); 14286 } 14287 ctx.lineTo(target.x, target.y); 14288 return; 14289 } 14290 14291 if (!target.tension) { 14292 ctx.lineTo(target.x, target.y); 14293 return; 14294 } 14295 14296 ctx.bezierCurveTo( 14297 flip ? previous.controlPointPreviousX : previous.controlPointNextX, 14298 flip ? previous.controlPointPreviousY : previous.controlPointNextY, 14299 flip ? target.controlPointNextX : target.controlPointPreviousX, 14300 flip ? target.controlPointNextY : target.controlPointPreviousY, 14301 target.x, 14302 target.y); 14303 } 14304}; 14305 14306// DEPRECATIONS 14307 14308/** 14309 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. 14310 * @namespace Chart.helpers.clear 14311 * @deprecated since version 2.7.0 14312 * @todo remove at version 3 14313 * @private 14314 */ 14315helpers.clear = exports.clear; 14316 14317/** 14318 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. 14319 * @namespace Chart.helpers.drawRoundedRectangle 14320 * @deprecated since version 2.7.0 14321 * @todo remove at version 3 14322 * @private 14323 */ 14324helpers.drawRoundedRectangle = function(ctx) { 14325 ctx.beginPath(); 14326 exports.roundedRect.apply(exports, arguments); 14327 ctx.closePath(); 14328}; 14329 14330},{"42":42}],42:[function(require,module,exports){ 14331'use strict'; 14332 14333/** 14334 * @namespace Chart.helpers 14335 */ 14336var helpers = { 14337 /** 14338 * An empty function that can be used, for example, for optional callback. 14339 */ 14340 noop: function() {}, 14341 14342 /** 14343 * Returns a unique id, sequentially generated from a global variable. 14344 * @returns {Number} 14345 * @function 14346 */ 14347 uid: (function() { 14348 var id = 0; 14349 return function() { 14350 return id++; 14351 }; 14352 }()), 14353 14354 /** 14355 * Returns true if `value` is neither null nor undefined, else returns false. 14356 * @param {*} value - The value to test. 14357 * @returns {Boolean} 14358 * @since 2.7.0 14359 */ 14360 isNullOrUndef: function(value) { 14361 return value === null || typeof value === 'undefined'; 14362 }, 14363 14364 /** 14365 * Returns true if `value` is an array, else returns false. 14366 * @param {*} value - The value to test. 14367 * @returns {Boolean} 14368 * @function 14369 */ 14370 isArray: Array.isArray ? Array.isArray : function(value) { 14371 return Object.prototype.toString.call(value) === '[object Array]'; 14372 }, 14373 14374 /** 14375 * Returns true if `value` is an object (excluding null), else returns false. 14376 * @param {*} value - The value to test. 14377 * @returns {Boolean} 14378 * @since 2.7.0 14379 */ 14380 isObject: function(value) { 14381 return value !== null && Object.prototype.toString.call(value) === '[object Object]'; 14382 }, 14383 14384 /** 14385 * Returns `value` if defined, else returns `defaultValue`. 14386 * @param {*} value - The value to return if defined. 14387 * @param {*} defaultValue - The value to return if `value` is undefined. 14388 * @returns {*} 14389 */ 14390 valueOrDefault: function(value, defaultValue) { 14391 return typeof value === 'undefined' ? defaultValue : value; 14392 }, 14393 14394 /** 14395 * Returns value at the given `index` in array if defined, else returns `defaultValue`. 14396 * @param {Array} value - The array to lookup for value at `index`. 14397 * @param {Number} index - The index in `value` to lookup for value. 14398 * @param {*} defaultValue - The value to return if `value[index]` is undefined. 14399 * @returns {*} 14400 */ 14401 valueAtIndexOrDefault: function(value, index, defaultValue) { 14402 return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); 14403 }, 14404 14405 /** 14406 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the 14407 * value returned by `fn`. If `fn` is not a function, this method returns undefined. 14408 * @param {Function} fn - The function to call. 14409 * @param {Array|undefined|null} args - The arguments with which `fn` should be called. 14410 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. 14411 * @returns {*} 14412 */ 14413 callback: function(fn, args, thisArg) { 14414 if (fn && typeof fn.call === 'function') { 14415 return fn.apply(thisArg, args); 14416 } 14417 }, 14418 14419 /** 14420 * Note(SB) for performance sake, this method should only be used when loopable type 14421 * is unknown or in none intensive code (not called often and small loopable). Else 14422 * it's preferable to use a regular for() loop and save extra function calls. 14423 * @param {Object|Array} loopable - The object or array to be iterated. 14424 * @param {Function} fn - The function to call for each item. 14425 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. 14426 * @param {Boolean} [reverse] - If true, iterates backward on the loopable. 14427 */ 14428 each: function(loopable, fn, thisArg, reverse) { 14429 var i, len, keys; 14430 if (helpers.isArray(loopable)) { 14431 len = loopable.length; 14432 if (reverse) { 14433 for (i = len - 1; i >= 0; i--) { 14434 fn.call(thisArg, loopable[i], i); 14435 } 14436 } else { 14437 for (i = 0; i < len; i++) { 14438 fn.call(thisArg, loopable[i], i); 14439 } 14440 } 14441 } else if (helpers.isObject(loopable)) { 14442 keys = Object.keys(loopable); 14443 len = keys.length; 14444 for (i = 0; i < len; i++) { 14445 fn.call(thisArg, loopable[keys[i]], keys[i]); 14446 } 14447 } 14448 }, 14449 14450 /** 14451 * Returns true if the `a0` and `a1` arrays have the same content, else returns false. 14452 * @see http://stackoverflow.com/a/14853974 14453 * @param {Array} a0 - The array to compare 14454 * @param {Array} a1 - The array to compare 14455 * @returns {Boolean} 14456 */ 14457 arrayEquals: function(a0, a1) { 14458 var i, ilen, v0, v1; 14459 14460 if (!a0 || !a1 || a0.length !== a1.length) { 14461 return false; 14462 } 14463 14464 for (i = 0, ilen = a0.length; i < ilen; ++i) { 14465 v0 = a0[i]; 14466 v1 = a1[i]; 14467 14468 if (v0 instanceof Array && v1 instanceof Array) { 14469 if (!helpers.arrayEquals(v0, v1)) { 14470 return false; 14471 } 14472 } else if (v0 !== v1) { 14473 // NOTE: two different object instances will never be equal: {x:20} != {x:20} 14474 return false; 14475 } 14476 } 14477 14478 return true; 14479 }, 14480 14481 /** 14482 * Returns a deep copy of `source` without keeping references on objects and arrays. 14483 * @param {*} source - The value to clone.
vendor: 10,304 bytes, lines 14484-14915
14484 * @returns {*} 14485 */ 14486 clone: function(source) { 14487 if (helpers.isArray(source)) { 14488 return source.map(helpers.clone); 14489 } 14490 14491 if (helpers.isObject(source)) { 14492 var target = {}; 14493 var keys = Object.keys(source); 14494 var klen = keys.length; 14495 var k = 0; 14496 14497 for (; k < klen; ++k) { 14498 target[keys[k]] = helpers.clone(source[keys[k]]); 14499 } 14500 14501 return target; 14502 } 14503 14504 return source; 14505 }, 14506 14507 /** 14508 * The default merger when Chart.helpers.merge is called without merger option. 14509 * Note(SB): this method is also used by configMerge and scaleMerge as fallback. 14510 * @private 14511 */ 14512 _merger: function(key, target, source, options) { 14513 var tval = target[key]; 14514 var sval = source[key]; 14515 14516 if (helpers.isObject(tval) && helpers.isObject(sval)) { 14517 helpers.merge(tval, sval, options); 14518 } else { 14519 target[key] = helpers.clone(sval); 14520 } 14521 }, 14522 14523 /** 14524 * Merges source[key] in target[key] only if target[key] is undefined. 14525 * @private 14526 */ 14527 _mergerIf: function(key, target, source) { 14528 var tval = target[key]; 14529 var sval = source[key]; 14530 14531 if (helpers.isObject(tval) && helpers.isObject(sval)) { 14532 helpers.mergeIf(tval, sval); 14533 } else if (!target.hasOwnProperty(key)) { 14534 target[key] = helpers.clone(sval); 14535 } 14536 }, 14537 14538 /** 14539 * Recursively deep copies `source` properties into `target` with the given `options`. 14540 * IMPORTANT: `target` is not cloned and will be updated with `source` properties. 14541 * @param {Object} target - The target object in which all sources are merged into. 14542 * @param {Object|Array(Object)} source - Object(s) to merge into `target`. 14543 * @param {Object} [options] - Merging options: 14544 * @param {Function} [options.merger] - The merge method (key, target, source, options) 14545 * @returns {Object} The `target` object. 14546 */ 14547 merge: function(target, source, options) { 14548 var sources = helpers.isArray(source) ? source : [source]; 14549 var ilen = sources.length; 14550 var merge, i, keys, klen, k; 14551 14552 if (!helpers.isObject(target)) { 14553 return target; 14554 } 14555 14556 options = options || {}; 14557 merge = options.merger || helpers._merger; 14558 14559 for (i = 0; i < ilen; ++i) { 14560 source = sources[i]; 14561 if (!helpers.isObject(source)) { 14562 continue; 14563 } 14564 14565 keys = Object.keys(source); 14566 for (k = 0, klen = keys.length; k < klen; ++k) { 14567 merge(keys[k], target, source, options); 14568 } 14569 } 14570 14571 return target; 14572 }, 14573 14574 /** 14575 * Recursively deep copies `source` properties into `target` *only* if not defined in target. 14576 * IMPORTANT: `target` is not cloned and will be updated with `source` properties. 14577 * @param {Object} target - The target object in which all sources are merged into. 14578 * @param {Object|Array(Object)} source - Object(s) to merge into `target`. 14579 * @returns {Object} The `target` object. 14580 */ 14581 mergeIf: function(target, source) { 14582 return helpers.merge(target, source, {merger: helpers._mergerIf}); 14583 }, 14584 14585 /** 14586 * Applies the contents of two or more objects together into the first object. 14587 * @param {Object} target - The target object in which all objects are merged into. 14588 * @param {Object} arg1 - Object containing additional properties to merge in target. 14589 * @param {Object} argN - Additional objects containing properties to merge in target. 14590 * @returns {Object} The `target` object. 14591 */ 14592 extend: function(target) { 14593 var setFn = function(value, key) { 14594 target[key] = value; 14595 }; 14596 for (var i = 1, ilen = arguments.length; i < ilen; ++i) { 14597 helpers.each(arguments[i], setFn); 14598 } 14599 return target; 14600 }, 14601 14602 /** 14603 * Basic javascript inheritance based on the model created in Backbone.js 14604 */ 14605 inherits: function(extensions) { 14606 var me = this; 14607 var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { 14608 return me.apply(this, arguments); 14609 }; 14610 14611 var Surrogate = function() { 14612 this.constructor = ChartElement; 14613 }; 14614 14615 Surrogate.prototype = me.prototype; 14616 ChartElement.prototype = new Surrogate(); 14617 ChartElement.extend = helpers.inherits; 14618 14619 if (extensions) { 14620 helpers.extend(ChartElement.prototype, extensions); 14621 } 14622 14623 ChartElement.__super__ = me.prototype; 14624 return ChartElement; 14625 } 14626}; 14627 14628module.exports = helpers; 14629 14630// DEPRECATIONS 14631 14632/** 14633 * Provided for backward compatibility, use Chart.helpers.callback instead. 14634 * @function Chart.helpers.callCallback 14635 * @deprecated since version 2.6.0 14636 * @todo remove at version 3 14637 * @private 14638 */ 14639helpers.callCallback = helpers.callback; 14640 14641/** 14642 * Provided for backward compatibility, use Array.prototype.indexOf instead. 14643 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ 14644 * @function Chart.helpers.indexOf 14645 * @deprecated since version 2.7.0 14646 * @todo remove at version 3 14647 * @private 14648 */ 14649helpers.indexOf = function(array, item, fromIndex) { 14650 return Array.prototype.indexOf.call(array, item, fromIndex); 14651}; 14652 14653/** 14654 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. 14655 * @function Chart.helpers.getValueOrDefault 14656 * @deprecated since version 2.7.0 14657 * @todo remove at version 3 14658 * @private 14659 */ 14660helpers.getValueOrDefault = helpers.valueOrDefault; 14661 14662/** 14663 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. 14664 * @function Chart.helpers.getValueAtIndexOrDefault 14665 * @deprecated since version 2.7.0 14666 * @todo remove at version 3 14667 * @private 14668 */ 14669helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 14670 14671},{}],43:[function(require,module,exports){ 14672'use strict'; 14673 14674var helpers = require(42); 14675 14676/** 14677 * Easing functions adapted from Robert Penner's easing equations. 14678 * @namespace Chart.helpers.easingEffects 14679 * @see http://www.robertpenner.com/easing/ 14680 */ 14681var effects = { 14682 linear: function(t) { 14683 return t; 14684 }, 14685 14686 easeInQuad: function(t) { 14687 return t * t; 14688 }, 14689 14690 easeOutQuad: function(t) { 14691 return -t * (t - 2); 14692 }, 14693 14694 easeInOutQuad: function(t) { 14695 if ((t /= 0.5) < 1) { 14696 return 0.5 * t * t; 14697 } 14698 return -0.5 * ((--t) * (t - 2) - 1); 14699 }, 14700 14701 easeInCubic: function(t) { 14702 return t * t * t; 14703 }, 14704 14705 easeOutCubic: function(t) { 14706 return (t = t - 1) * t * t + 1; 14707 }, 14708 14709 easeInOutCubic: function(t) { 14710 if ((t /= 0.5) < 1) { 14711 return 0.5 * t * t * t; 14712 } 14713 return 0.5 * ((t -= 2) * t * t + 2); 14714 }, 14715 14716 easeInQuart: function(t) { 14717 return t * t * t * t; 14718 }, 14719 14720 easeOutQuart: function(t) { 14721 return -((t = t - 1) * t * t * t - 1); 14722 }, 14723 14724 easeInOutQuart: function(t) { 14725 if ((t /= 0.5) < 1) { 14726 return 0.5 * t * t * t * t; 14727 } 14728 return -0.5 * ((t -= 2) * t * t * t - 2); 14729 }, 14730 14731 easeInQuint: function(t) { 14732 return t * t * t * t * t; 14733 }, 14734 14735 easeOutQuint: function(t) { 14736 return (t = t - 1) * t * t * t * t + 1; 14737 }, 14738 14739 easeInOutQuint: function(t) { 14740 if ((t /= 0.5) < 1) { 14741 return 0.5 * t * t * t * t * t; 14742 } 14743 return 0.5 * ((t -= 2) * t * t * t * t + 2); 14744 }, 14745 14746 easeInSine: function(t) { 14747 return -Math.cos(t * (Math.PI / 2)) + 1; 14748 }, 14749 14750 easeOutSine: function(t) { 14751 return Math.sin(t * (Math.PI / 2)); 14752 }, 14753 14754 easeInOutSine: function(t) { 14755 return -0.5 * (Math.cos(Math.PI * t) - 1); 14756 }, 14757 14758 easeInExpo: function(t) { 14759 return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); 14760 }, 14761 14762 easeOutExpo: function(t) { 14763 return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; 14764 }, 14765 14766 easeInOutExpo: function(t) { 14767 if (t === 0) { 14768 return 0; 14769 } 14770 if (t === 1) { 14771 return 1; 14772 } 14773 if ((t /= 0.5) < 1) { 14774 return 0.5 * Math.pow(2, 10 * (t - 1)); 14775 } 14776 return 0.5 * (-Math.pow(2, -10 * --t) + 2); 14777 }, 14778 14779 easeInCirc: function(t) { 14780 if (t >= 1) { 14781 return t; 14782 } 14783 return -(Math.sqrt(1 - t * t) - 1); 14784 }, 14785 14786 easeOutCirc: function(t) { 14787 return Math.sqrt(1 - (t = t - 1) * t); 14788 }, 14789 14790 easeInOutCirc: function(t) { 14791 if ((t /= 0.5) < 1) { 14792 return -0.5 * (Math.sqrt(1 - t * t) - 1); 14793 } 14794 return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); 14795 }, 14796 14797 easeInElastic: function(t) { 14798 var s = 1.70158; 14799 var p = 0; 14800 var a = 1; 14801 if (t === 0) { 14802 return 0; 14803 } 14804 if (t === 1) { 14805 return 1; 14806 } 14807 if (!p) { 14808 p = 0.3; 14809 } 14810 if (a < 1) { 14811 a = 1; 14812 s = p / 4; 14813 } else { 14814 s = p / (2 * Math.PI) * Math.asin(1 / a); 14815 } 14816 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); 14817 }, 14818 14819 easeOutElastic: function(t) { 14820 var s = 1.70158; 14821 var p = 0; 14822 var a = 1; 14823 if (t === 0) { 14824 return 0; 14825 } 14826 if (t === 1) { 14827 return 1; 14828 } 14829 if (!p) { 14830 p = 0.3; 14831 } 14832 if (a < 1) { 14833 a = 1; 14834 s = p / 4; 14835 } else { 14836 s = p / (2 * Math.PI) * Math.asin(1 / a); 14837 } 14838 return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; 14839 }, 14840 14841 easeInOutElastic: function(t) { 14842 var s = 1.70158; 14843 var p = 0; 14844 var a = 1; 14845 if (t === 0) { 14846 return 0; 14847 } 14848 if ((t /= 0.5) === 2) { 14849 return 1; 14850 } 14851 if (!p) { 14852 p = 0.45; 14853 } 14854 if (a < 1) { 14855 a = 1; 14856 s = p / 4; 14857 } else { 14858 s = p / (2 * Math.PI) * Math.asin(1 / a); 14859 } 14860 if (t < 1) { 14861 return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); 14862 } 14863 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; 14864 }, 14865 easeInBack: function(t) { 14866 var s = 1.70158; 14867 return t * t * ((s + 1) * t - s); 14868 }, 14869 14870 easeOutBack: function(t) { 14871 var s = 1.70158; 14872 return (t = t - 1) * t * ((s + 1) * t + s) + 1; 14873 }, 14874 14875 easeInOutBack: function(t) { 14876 var s = 1.70158; 14877 if ((t /= 0.5) < 1) { 14878 return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); 14879 } 14880 return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); 14881 }, 14882 14883 easeInBounce: function(t) { 14884 return 1 - effects.easeOutBounce(1 - t); 14885 }, 14886 14887 easeOutBounce: function(t) { 14888 if (t < (1 / 2.75)) { 14889 return 7.5625 * t * t; 14890 } 14891 if (t < (2 / 2.75)) { 14892 return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; 14893 } 14894 if (t < (2.5 / 2.75)) { 14895 return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; 14896 } 14897 return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; 14898 }, 14899 14900 easeInOutBounce: function(t) { 14901 if (t < 0.5) { 14902 return effects.easeInBounce(t * 2) * 0.5; 14903 } 14904 return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; 14905 } 14906}; 14907 14908module.exports = { 14909 effects: effects 14910}; 14911 14912// DEPRECATIONS 14913 14914/** 14915 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
14916 * @function Chart.helpers.easingEffects 14917 * @deprecated since version 2.7.0 14918 * @todo remove at version 3 14919 * @private 14920 */ 14921helpers.easingEffects = effects; 14922 14923},{"42":42}],44:[function(require,module,exports){ 14924'use strict'; 14925 14926var helpers = require(42); 14927 14928/** 14929 * @alias Chart.helpers.options 14930 * @namespace 14931 */ 14932module.exports = { 14933 /** 14934 * Converts the given line height `value` in pixels for a specific font `size`. 14935 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). 14936 * @param {Number} size - The font size (in pixels) used to resolve relative `value`. 14937 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). 14938 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height 14939 * @since 2.7.0 14940 */ 14941 toLineHeight: function(value, size) { 14942 var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); 14943 if (!matches || matches[1] === 'normal') { 14944 return size * 1.2; 14945 } 14946 14947 value = +matches[2]; 14948 14949 switch (matches[3]) { 14950 case 'px': 14951 return value; 14952 case '%': 14953 value /= 100; 14954 break; 14955 default: 14956 break; 14957 } 14958 14959 return size * value; 14960 }, 14961 14962 /** 14963 * Converts the given value into a padding object with pre-computed width/height. 14964 * @param {Number|Object} value - If a number, set the value to all TRBL component, 14965 * else, if and object, use defined properties and sets undefined ones to 0. 14966 * @returns {Object} The padding values (top, right, bottom, left, width, height) 14967 * @since 2.7.0 14968 */ 14969 toPadding: function(value) { 14970 var t, r, b, l; 14971 14972 if (helpers.isObject(value)) { 14973 t = +value.top || 0; 14974 r = +value.right || 0; 14975 b = +value.bottom || 0; 14976 l = +value.left || 0; 14977 } else { 14978 t = r = b = l = +value || 0; 14979 } 14980 14981 return { 14982 top: t, 14983 right: r, 14984 bottom: b, 14985 left: l, 14986 height: t + b, 14987 width: l + r 14988 }; 14989 }, 14990 14991 /** 14992 * Evaluates the given `inputs` sequentially and returns the first defined value. 14993 * @param {Array[]} inputs - An array of values, falling back to the last value. 14994 * @param {Object} [context] - If defined and the current value is a function, the value 14995 * is called with `context` as first argument and the result becomes the new input. 14996 * @param {Number} [index] - If defined and the current value is an array, the value 14997 * at `index` become the new input. 14998 * @since 2.7.0 14999 */ 15000 resolve: function(inputs, context, index) { 15001 var i, ilen, value; 15002 15003 for (i = 0, ilen = inputs.length; i < ilen; ++i) { 15004 value = inputs[i]; 15005 if (value === undefined) { 15006 continue; 15007 } 15008 if (context !== undefined && typeof value === 'function') { 15009 value = value(context); 15010 } 15011 if (index !== undefined && helpers.isArray(value)) { 15012 value = value[index]; 15013 } 15014 if (value !== undefined) { 15015 return value; 15016 } 15017 } 15018 } 15019}; 15020 15021},{"42":42}],45:[function(require,module,exports){ 15022'use strict'; 15023 15024module.exports = require(42); 15025module.exports.easing = require(43); 15026module.exports.canvas = require(41); 15027module.exports.options = require(44); 15028 15029},{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){ 15030/** 15031 * Platform fallback implementation (minimal). 15032 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 15033 */ 15034 15035module.exports = { 15036 acquireContext: function(item) { 15037 if (item && item.canvas) { 15038 // Support for any object associated to a canvas (including a context2d) 15039 item = item.canvas; 15040 } 15041 15042 return item && item.getContext('2d') || null; 15043 } 15044}; 15045 15046},{}],47:[function(require,module,exports){ 15047/** 15048 * Chart.Platform implementation for targeting a web browser 15049 */ 15050 15051'use strict'; 15052 15053var helpers = require(45); 15054 15055var EXPANDO_KEY = '$chartjs'; 15056var CSS_PREFIX = 'chartjs-'; 15057var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; 15058var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; 15059var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; 15060 15061/** 15062 * DOM event types -> Chart.js event types. 15063 * Note: only events with different types are mapped. 15064 * @see https://developer.mozilla.org/en-US/docs/Web/Events 15065 */ 15066var EVENT_TYPES = { 15067 touchstart: 'mousedown', 15068 touchmove: 'mousemove', 15069 touchend: 'mouseup', 15070 pointerenter: 'mouseenter', 15071 pointerdown: 'mousedown', 15072 pointermove: 'mousemove', 15073 pointerup: 'mouseup', 15074 pointerleave: 'mouseout', 15075 pointerout: 'mouseout' 15076}; 15077 15078/** 15079 * The "used" size is the final value of a dimension property after all calculations have 15080 * been performed. This method uses the computed style of `element` but returns undefined 15081 * if the computed style is not expressed in pixels. That can happen in some cases where 15082 * `element` has a size relative to its parent and this last one is not yet displayed, 15083 * for example because of `display: none` on a parent node. 15084 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value 15085 * @returns {Number} Size in pixels or undefined if unknown. 15086 */ 15087function readUsedSize(element, property) { 15088 var value = helpers.getStyle(element, property); 15089 var matches = value && value.match(/^(\d+)(\.\d+)?px$/); 15090 return matches ? Number(matches[1]) : undefined; 15091} 15092 15093/** 15094 * Initializes the canvas style and render size without modifying the canvas display size, 15095 * since responsiveness is handled by the controller.resize() method. The config is used 15096 * to determine the aspect ratio to apply in case no explicit height has been specified. 15097 */ 15098function initCanvas(canvas, config) { 15099 var style = canvas.style; 15100 15101 // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it 15102 // returns null or '' if no explicit value has been set to the canvas attribute. 15103 var renderHeight = canvas.getAttribute('height'); 15104 var renderWidth = canvas.getAttribute('width'); 15105 15106 // Chart.js modifies some canvas values that we want to restore on de
vendor: 4,734 bytes, lines 15106-15280
15106stroy 15107 canvas[EXPANDO_KEY] = { 15108 initial: { 15109 height: renderHeight, 15110 width: renderWidth, 15111 style: { 15112 display: style.display, 15113 height: style.height, 15114 width: style.width 15115 } 15116 } 15117 }; 15118 15119 // Force canvas to display as block to avoid extra space caused by inline 15120 // elements, which would interfere with the responsive resize process. 15121 // https://github.com/chartjs/Chart.js/issues/2538 15122 style.display = style.display || 'block'; 15123 15124 if (renderWidth === null || renderWidth === '') { 15125 var displayWidth = readUsedSize(canvas, 'width'); 15126 if (displayWidth !== undefined) { 15127 canvas.width = displayWidth; 15128 } 15129 } 15130 15131 if (renderHeight === null || renderHeight === '') { 15132 if (canvas.style.height === '') { 15133 // If no explicit render height and style height, let's apply the aspect ratio, 15134 // which one can be specified by the user but also by charts as default option 15135 // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. 15136 canvas.height = canvas.width / (config.options.aspectRatio || 2); 15137 } else { 15138 var displayHeight = readUsedSize(canvas, 'height'); 15139 if (displayWidth !== undefined) { 15140 canvas.height = displayHeight; 15141 } 15142 } 15143 } 15144 15145 return canvas; 15146} 15147 15148/** 15149 * Detects support for options object argument in addEventListener. 15150 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support 15151 * @private 15152 */ 15153var supportsEventListenerOptions = (function() { 15154 var supports = false; 15155 try { 15156 var options = Object.defineProperty({}, 'passive', { 15157 get: function() { 15158 supports = true; 15159 } 15160 }); 15161 window.addEventListener('e', null, options); 15162 } catch (e) { 15163 // continue regardless of error 15164 } 15165 return supports; 15166}()); 15167 15168// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. 15169// https://github.com/chartjs/Chart.js/issues/4287 15170var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; 15171 15172function addEventListener(node, type, listener) { 15173 node.addEventListener(type, listener, eventListenerOptions); 15174} 15175 15176function removeEventListener(node, type, listener) { 15177 node.removeEventListener(type, listener, eventListenerOptions); 15178} 15179 15180function createEvent(type, chart, x, y, nativeEvent) { 15181 return { 15182 type: type, 15183 chart: chart, 15184 native: nativeEvent || null, 15185 x: x !== undefined ? x : null, 15186 y: y !== undefined ? y : null, 15187 }; 15188} 15189 15190function fromNativeEvent(event, chart) { 15191 var type = EVENT_TYPES[event.type] || event.type; 15192 var pos = helpers.getRelativePosition(event, chart); 15193 return createEvent(type, chart, pos.x, pos.y, event); 15194} 15195 15196function throttled(fn, thisArg) { 15197 var ticking = false; 15198 var args = []; 15199 15200 return function() { 15201 args = Array.prototype.slice.call(arguments); 15202 thisArg = thisArg || this; 15203 15204 if (!ticking) { 15205 ticking = true; 15206 helpers.requestAnimFrame.call(window, function() { 15207 ticking = false; 15208 fn.apply(thisArg, args); 15209 }); 15210 } 15211 }; 15212} 15213 15214// Implementation based on https://github.com/marcj/css-element-queries 15215function createResizer(handler) { 15216 var resizer = document.createElement('div'); 15217 var cls = CSS_PREFIX + 'size-monitor'; 15218 var maxSize = 1000000; 15219 var style = 15220 'position:absolute;' + 15221 'left:0;' + 15222 'top:0;' + 15223 'right:0;' + 15224 'bottom:0;' + 15225 'overflow:hidden;' + 15226 'pointer-events:none;' + 15227 'visibility:hidden;' + 15228 'z-index:-1;'; 15229 15230 resizer.style.cssText = style; 15231 resizer.className = cls; 15232 resizer.innerHTML = 15233 '<div class="' + cls + '-expand" style="' + style + '">' + 15234 '<div style="' + 15235 'position:absolute;' + 15236 'width:' + maxSize + 'px;' + 15237 'height:' + maxSize + 'px;' + 15238 'left:0;' + 15239 'top:0">' + 15240 '</div>' + 15241 '</div>' + 15242 '<div class="' + cls + '-shrink" style="' + style + '">' + 15243 '<div style="' + 15244 'position:absolute;' + 15245 'width:200%;' + 15246 'height:200%;' + 15247 'left:0; ' + 15248 'top:0">' + 15249 '</div>' + 15250 '</div>'; 15251 15252 var expand = resizer.childNodes[0]; 15253 var shrink = resizer.childNodes[1]; 15254 15255 resizer._reset = function() { 15256 expand.scrollLeft = maxSize; 15257 expand.scrollTop = maxSize; 15258 shrink.scrollLeft = maxSize; 15259 shrink.scrollTop = maxSize; 15260 }; 15261 var onScroll = function() { 15262 resizer._reset(); 15263 handler(); 15264 }; 15265 15266 addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); 15267 addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); 15268 15269 return resizer; 15270} 15271 15272// https://davidwalsh.name/detect-node-insertion 15273function watchForRender(node, handler) { 15274 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); 15275 var proxy = expando.renderProxy = function(e) { 15276 if (e.animationName === CSS_RENDER_ANIMATION) { 15277 handler(); 15278 } 15279 }; 15280
15281 helpers.each(ANIMATION_START_EVENTS, function(type) { 15282 addEventListener(node, type, proxy); 15283 }); 15284 15285 // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class 15286 // is removed then added back immediately (same animation frame?). Accessing the 15287 // `offsetParent` property will force a reflow and re-evaluate the CSS animation. 15288 // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics 15289 // https://github.com/chartjs/Chart.js/issues/4737 15290 expando.reflow = !!node.offsetParent; 15291 15292 node.classList.add(CSS_RENDER_MONITOR); 15293} 15294 15295function unwatchForRender(node) { 15296 var expando = node[EXPANDO_KEY] || {}; 15297 var proxy = expando.renderProxy; 15298 15299 if (proxy) { 15300 helpers.each(ANIMATION_START_EVENTS, function(type) { 15301 removeEventListener(node, type, proxy); 15302 }); 15303 15304 delete expando.renderProxy; 15305 } 15306 15307 node.classList.remove(CSS_RENDER_MONITOR); 15308} 15309 15310function addResizeListener(node, listener, chart) { 15311 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); 15312 15313 // Let's keep track of this added resizer and thus avoid DOM query when removing it. 15314 var resizer = expando.resizer = createResizer(throttled(function() { 15315 if (expando.resizer) { 15316 return listener(createEvent('resize', chart)); 15317 } 15318 })); 15319 15320 // The resizer needs to be attached to the node parent, so we first need to be 15321 // sure that `node` is attached to the DOM before injecting the resizer element. 15322 watchForRender(node, function() { 15323 if (expando.resizer) { 15324 var container = node.parentNode; 15325 if (container && container !== resizer.parentNode) { 15326 container.insertBefore(resizer, container.firstChild); 15327 } 15328 15329 // The container size might have changed, let's reset the resizer state. 15330 resizer._reset(); 15331 } 15332 }); 15333} 15334 15335function removeResizeListener(node) { 15336 var expando = node[EXPANDO_KEY] || {}; 15337 var resizer = expando.resizer; 15338 15339 delete expando.resizer; 15340 unwatchForRender(node); 15341 15342 if (resizer && resizer.parentNode) { 15343 resizer.parentNode.removeChild(resizer); 15344 } 15345} 15346 15347function injectCSS(platform, css) { 15348 // http://stackoverflow.com/q/3922139 15349 var style = platform._style || document.createElement('style'); 15350 if (!platform._style) { 15351 platform._style = style; 15352 css = '/* Chart.js */\n' + css; 15353 style.setAttribute('type', 'text/css'); 15354 document.getElementsByTagName('head')[0].appendChild(style); 15355 } 15356 15357 style.appendChild(document.createTextNode(css)); 15358} 15359 15360module.exports = { 15361 /** 15362 * This property holds whether this platform is enabled for the current environment. 15363 * Currently used by platform.js to select the proper implementation. 15364 * @private 15365 */ 15366 _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', 15367 15368 initialize: function() { 15369 var keyframes = 'from{opacity:0.99}to{opacity:1}'; 15370 15371 injectCSS(this, 15372 // DOM rendering detection 15373 // https://davidwalsh.name/detect-node-insertion 15374 '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + 15375 '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + 15376 '.' + CSS_RENDER_MONITOR + '{' + 15377 '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 15378 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 15379 '}' 15380 ); 15381 }, 15382 15383 acquireContext: function(item, config) { 15384 if (typeof item === 'string') { 15385 item = document.getElementById(item); 15386 } else if (item.length) { 15387 // Support for array based queries (such as jQuery) 15388 item = item[0]; 15389 } 15390 15391 if (item && item.canvas) { 15392 // Support for any object associated to a canvas (including a context2d) 15393 item = item.canvas; 15394 } 15395 15396 // To prevent canvas fingerprinting, some add-ons undefine the getContext 15397 // method, for example: https://github.com/kkapsner/CanvasBlocker 15398 // https://github.com/chartjs/Chart.js/issues/2807 15399 var context = item && item.getContext && item.getContext('2d'); 15400 15401 // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is 15402 // inside an iframe or when running in a protected environment. We could guess the 15403 // types from their toString() value but let's keep things flexible and assume it's 15404 // a sufficient condition if the item has a context2D which has item as `canvas`. 15405 // https://github.com/chartjs/Chart.js/issues/3887 15406 // https://github.com/chartjs/Chart.js/issues/4102 15407 // https://github.com/chartjs/Chart.js/issues/4152 15408 if (context && context.canvas === item) { 15409 initCanvas(item, config); 15410 return context; 15411 } 15412 15413 return null; 15414 }, 15415 15416 releaseContext: function(context) { 15417 var canvas = context.canvas; 15418 if (!canvas[EXPANDO_KEY]) { 15419 return; 15420 } 15421 15422 var initial = canvas[EXPANDO_KEY].initial; 15423 ['height', 'width'].forEach(function(prop) { 15424 var value = initial[prop]; 15425 if (helpers.isNullOrUndef(value)) { 15426 canvas.removeAttribute(prop); 15427 } else { 15428 canvas.setAttribute(prop, value); 15429 } 15430 }); 15431 15432 helpers.each(initial.style || {}, function(value, key) { 15433 canvas.style[key] = value; 15434 }); 15435 15436 // The canvas render size might have been changed (and thus the state stack discarded),
15437 // we can't use save() and restore() to restore the initial state. So make sure that at 15438 // least the canvas context is reset to the default state by setting the canvas width. 15439 // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html 15440 canvas.width = canvas.width; 15441 15442 delete canvas[EXPANDO_KEY]; 15443 }, 15444 15445 addEventListener: function(chart, type, listener) { 15446 var canvas = chart.canvas; 15447 if (type === 'resize') { 15448 // Note: the resize event is not supported on all browsers. 15449 addResizeListener(canvas, listener, chart); 15450 return; 15451 } 15452 15453 var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); 15454 var proxies = expando.proxies || (expando.proxies = {}); 15455 var proxy = proxies[chart.id + '_' + type] = function(event) { 15456 listener(fromNativeEvent(event, chart)); 15457 }; 15458 15459 addEventListener(canvas, type, proxy); 15460 }, 15461 15462 removeEventListener: function(chart, type, listener) { 15463 var canvas = chart.canvas; 15464 if (type === 'resize') { 15465 // Note: the resize event is not supported on all browsers. 15466 removeResizeListener(canvas, listener); 15467 return; 15468 } 15469 15470 var expando = listener[EXPANDO_KEY] || {}; 15471 var proxies = expando.proxies || {}; 15472 var proxy = proxies[chart.id + '_' + type]; 15473 if (!proxy) { 15474 return; 15475 } 15476 15477 removeEventListener(canvas, type, proxy); 15478 } 15479}; 15480 15481// DEPRECATIONS 15482 15483/** 15484 * Provided for backward compatibility, use EventTarget.addEventListener instead. 15485 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ 15486 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener 15487 * @function Chart.helpers.addEvent 15488 * @deprecated since version 2.7.0 15489 * @todo remove at version 3 15490 * @private 15491 */ 15492helpers.addEvent = addEventListener; 15493 15494/** 15495 * Provided for backward compatibility, use EventTarget.removeEventListener instead. 15496 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ 15497 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener 15498 * @function Chart.helpers.removeEvent 15499 * @deprecated since version 2.7.0 15500 * @todo remove at version 3 15501 * @private 15502 */ 15503helpers.removeEvent = removeEventListener; 15504 15505},{"45":45}],48:[function(require,module,exports){ 15506'use strict'; 15507 15508var helpers = require(45); 15509var basic = require(46); 15510var dom = require(47); 15511 15512// @TODO Make possible to select another platform at build time. 15513var implementation = dom._enabled ? dom : basic; 15514 15515/** 15516 * @namespace Chart.platform 15517 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html 15518 * @since 2.4.0 15519 */ 15520module.exports = helpers.extend({ 15521 /** 15522 * @since 2.7.0 15523 */ 15524 initialize: function() {}, 15525 15526 /** 15527 * Called at chart construction time, returns a context2d instance implementing 15528 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. 15529 * @param {*} item - The native item from which to acquire context (platform specific) 15530 * @param {Object} options - The chart options 15531 * @returns {CanvasRenderingContext2D} context2d instance 15532 */ 15533 acquireContext: function() {}, 15534 15535 /** 15536 * Called at chart destruction time, releases any resources associated to the context 15537 * previously returned by the acquireContext() method. 15538 * @param {CanvasRenderingContext2D} context - The context2d instance 15539 * @returns {Boolean} true if the method succeeded, else false 15540 */ 15541 releaseContext: function() {}, 15542 15543 /** 15544 * Registers the specified listener on the given chart. 15545 * @param {Chart} chart - Chart from which to listen for event 15546 * @param {String} type - The ({@link IEvent}) type to listen for 15547 * @param {Function} listener - Receives a notification (an object that implements 15548 * the {@link IEvent} interface) when an event of the specified type occurs. 15549 */ 15550 addEventListener: function() {}, 15551 15552 /** 15553 * Removes the specified listener previously registered with addEventListener. 15554 * @param {Chart} chart -Chart from which to remove the listener 15555 * @param {String} type - The ({@link IEvent}) type to remove 15556 * @param {Function} listener - The listener function to remove from the event target. 15557 */ 15558 removeEventListener: function() {} 15559 15560}, implementation); 15561 15562/** 15563 * @interface IPlatform 15564 * Allows abstracting platform dependencies away from the chart 15565 * @borrows Chart.platform.acquireContext as acquireContext 15566 * @borrows Chart.platform.releaseContext as releaseContext 15567 * @borrows Chart.platform.addEventListener as addEventListener 15568 * @borrows Chart.platform.removeEventListener as removeEventListener 15569 */ 15570 15571/** 15572 * @interface IEvent 15573 * @prop {String} type - The event type name, possible values are: 15574 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
15575 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' 15576 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') 15577 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) 15578 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) 15579 */ 15580 15581},{"45":45,"46":46,"47":47}],49:[function(require,module,exports){ 15582'use strict'; 15583 15584module.exports = {}; 15585module.exports.filler = require(50); 15586module.exports.legend = require(51); 15587module.exports.title = require(52); 15588 15589},{"50":50,"51":51,"52":52}],50:[function(require,module,exports){ 15590/** 15591 * Plugin based on discussion from the following Chart.js issues: 15592 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 15593 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 15594 */ 15595 15596'use strict'; 15597 15598var defaults = require(25); 15599var elements = require(40); 15600var helpers = require(45); 15601 15602defaults._set('global', { 15603 plugins: { 15604 filler: { 15605 propagate: true 15606 } 15607 } 15608}); 15609 15610var mappers = { 15611 dataset: function(source) { 15612 var index = source.fill; 15613 var chart = source.chart; 15614 var meta = chart.getDatasetMeta(index); 15615 var visible = meta && chart.isDatasetVisible(index); 15616 var points = (visible && meta.dataset._children) || []; 15617 var length = points.length || 0; 15618 15619 return !length ? null : function(point, i) { 15620 return (i < length && points[i]._view) || null; 15621 }; 15622 }, 15623 15624 boundary: function(source) { 15625 var boundary = source.boundary; 15626 var x = boundary ? boundary.x : null; 15627 var y = boundary ? boundary.y : null; 15628 15629 return function(point) { 15630 return { 15631 x: x === null ? point.x : x, 15632 y: y === null ? point.y : y, 15633 }; 15634 }; 15635 } 15636}; 15637 15638// @todo if (fill[0] === '#') 15639function decodeFill(el, index, count) { 15640 var model = el._model || {}; 15641 var fill = model.fill; 15642 var target; 15643 15644 if (fill === undefined) { 15645 fill = !!model.backgroundColor; 15646 } 15647 15648 if (fill === false || fill === null) { 15649 return false; 15650 } 15651 15652 if (fill === true) { 15653 return 'origin'; 15654 } 15655 15656 target = parseFloat(fill, 10); 15657 if (isFinite(target) && Math.floor(target) === target) { 15658 if (fill[0] === '-' || fill[0] === '+') { 15659 target = index + target; 15660 } 15661 15662 if (target === index || target < 0 || target >= count) { 15663 return false; 15664 } 15665 15666 return target; 15667 } 15668 15669 switch (fill) { 15670 // compatibility 15671 case 'bottom': 15672 return 'start'; 15673 case 'top': 15674 return 'end'; 15675 case 'zero': 15676 return 'origin'; 15677 // supported boundaries 15678 case 'origin': 15679 case 'start': 15680 case 'end': 15681 return fill; 15682 // invalid fill values 15683 default: 15684 return false; 15685 } 15686} 15687 15688function computeBoundary(source) { 15689 var model = source.el._model || {}; 15690 var scale = source.el._scale || {}; 15691 var fill = source.fill; 15692 var target = null; 15693 var horizontal; 15694 15695 if (isFinite(fill)) { 15696 return null; 15697 } 15698 15699 // Backward compatibility: until v3, we still need to support boundary values set on 15700 // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and 15701 // controllers might still use it (e.g. the Smith chart). 15702 15703 if (fill === 'start') { 15704 target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; 15705 } else if (fill === 'end') { 15706 target = model.scaleTop === undefined ? scale.top : model.scaleTop; 15707 } else if (model.scaleZero !== undefined) { 15708 target = model.scaleZero; 15709 } else if (scale.getBasePosition) { 15710 target = scale.getBasePosition(); 15711 } else if (scale.getBasePixel) { 15712 target = scale.getBasePixel(); 15713 } 15714 15715 if (target !== undefined && target !== null) { 15716 if (target.x !== undefined && target.y !== undefined) { 15717 return target; 15718 } 15719 15720 if (typeof target === 'number' && isFinite(target)) { 15721 horizontal = scale.isHorizontal(); 15722 return { 15723 x: horizontal ? target : null, 15724 y: horizontal ? null : target 15725 }; 15726 } 15727 } 15728 15729 return null; 15730} 15731 15732function resolveTarget(sources, index, propagate) { 15733 var source = sources[index]; 15734 var fill = source.fill; 15735 var visited = [index]; 15736 var target; 15737 15738 if (!propagate) { 15739 return fill; 15740 } 15741 15742 while (fill !== false && visited.indexOf(fill) === -1) { 15743 if (!isFinite(fill)) { 15744 return fill; 15745 } 15746 15747 target = sources[fill]; 15748 if (!target) { 15749 return false; 15750 } 15751 15752 if (target.visible) { 15753 return fill; 15754 } 15755 15756 visited.push(fill); 15757 fill = target.fill; 15758 } 15759 15760 return false; 15761} 15762 15763function createMapper(source) { 15764 var fill = source.fill; 15765 var type = 'dataset'; 15766 15767 if (fill === false) { 15768 return null; 15769 } 15770 15771 if (!isFinite(fill)) {
15772 type = 'boundary'; 15773 } 15774 15775 return mappers[type](source); 15776} 15777 15778function isDrawable(point) { 15779 return point && !point.skip; 15780} 15781 15782function drawArea(ctx, curve0, curve1, len0, len1) { 15783 var i; 15784 15785 if (!len0 || !len1) { 15786 return; 15787 } 15788 15789 // building first area curve (normal) 15790 ctx.moveTo(curve0[0].x, curve0[0].y); 15791 for (i = 1; i < len0; ++i) { 15792 helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); 15793 } 15794 15795 // joining the two area curves 15796 ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); 15797 15798 // building opposite area curve (reverse) 15799 for (i = len1 - 1; i > 0; --i) { 15800 helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); 15801 } 15802} 15803 15804function doFill(ctx, points, mapper, view, color, loop) { 15805 var count = points.length; 15806 var span = view.spanGaps; 15807 var curve0 = []; 15808 var curve1 = []; 15809 var len0 = 0; 15810 var len1 = 0; 15811 var i, ilen, index, p0, p1, d0, d1; 15812 15813 ctx.beginPath(); 15814 15815 for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { 15816 index = i % count; 15817 p0 = points[index]._view; 15818 p1 = mapper(p0, index, view); 15819 d0 = isDrawable(p0); 15820 d1 = isDrawable(p1); 15821 15822 if (d0 && d1) { 15823 len0 = curve0.push(p0); 15824 len1 = curve1.push(p1); 15825 } else if (len0 && len1) { 15826 if (!span) { 15827 drawArea(ctx, curve0, curve1, len0, len1); 15828 len0 = len1 = 0; 15829 curve0 = []; 15830 curve1 = []; 15831 } else { 15832 if (d0) { 15833 curve0.push(p0); 15834 } 15835 if (d1) { 15836 curve1.push(p1); 15837 } 15838 } 15839 } 15840 } 15841 15842 drawArea(ctx, curve0, curve1, len0, len1); 15843 15844 ctx.closePath(); 15845 ctx.fillStyle = color; 15846 ctx.fill(); 15847} 15848 15849module.exports = { 15850 id: 'filler', 15851 15852 afterDatasetsUpdate: function(chart, options) { 15853 var count = (chart.data.datasets || []).length; 15854 var propagate = options.propagate; 15855 var sources = []; 15856 var meta, i, el, source; 15857 15858 for (i = 0; i < count; ++i) { 15859 meta = chart.getDatasetMeta(i); 15860 el = meta.dataset; 15861 source = null; 15862 15863 if (el && el._model && el instanceof elements.Line) { 15864 source = { 15865 visible: chart.isDatasetVisible(i), 15866 fill: decodeFill(el, i, count), 15867 chart: chart, 15868 el: el 15869 }; 15870 } 15871 15872 meta.$filler = source; 15873 sources.push(source); 15874 } 15875 15876 for (i = 0; i < count; ++i) { 15877 source = sources[i]; 15878 if (!source) { 15879 continue; 15880 } 15881 15882 source.fill = resolveTarget(sources, i, propagate); 15883 source.boundary = computeBoundary(source); 15884 source.mapper = createMapper(source); 15885 } 15886 }, 15887 15888 beforeDatasetDraw: function(chart, args) { 15889 var meta = args.meta.$filler; 15890 if (!meta) { 15891 return; 15892 } 15893 15894 var ctx = chart.ctx; 15895 var el = meta.el; 15896 var view = el._view; 15897 var points = el._children || []; 15898 var mapper = meta.mapper; 15899 var color = view.backgroundColor || defaults.global.defaultColor; 15900 15901 if (mapper && color && points.length) { 15902 helpers.canvas.clipArea(ctx, chart.chartArea); 15903 doFill(ctx, points, mapper, view, color, el._loop); 15904 helpers.canvas.unclipArea(ctx); 15905 } 15906 } 15907}; 15908 15909},{"25":25,"40":40,"45":45}],51:[function(require,module,exports){ 15910'use strict'; 15911 15912var defaults = require(25); 15913var Element = require(26); 15914var helpers = require(45); 15915var layouts = require(30); 15916 15917var noop = helpers.noop; 15918 15919defaults._set('global', { 15920 legend: { 15921 display: true, 15922 position: 'top', 15923 fullWidth: true, 15924 reverse: false, 15925 weight: 1000, 15926 15927 // a callback that will handle 15928 onClick: function(e, legendItem) { 15929 var index = legendItem.datasetIndex; 15930 var ci = this.chart; 15931 var meta = ci.getDatasetMeta(index); 15932 15933 // See controller.isDatasetVisible comment 15934 meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; 15935 15936 // We hid a dataset ... rerender the chart 15937 ci.update(); 15938 }, 15939 15940 onHover: null, 15941 15942 labels: { 15943 boxWidth: 40, 15944 padding: 10, 15945 // Generates labels shown in the legend 15946 // Valid properties to return: 15947 // text : text to display 15948 // fillStyle : fill of coloured box 15949 // strokeStyle: stroke of coloured box 15950 // hidden : if this legend item refers to a hidden item 15951 // lineCap : cap style for line 15952 // lineDash 15953 // lineDashOffset : 15954 // lineJoin : 15955 // lineWidth :
15956 generateLabels: function(chart) { 15957 var data = chart.data; 15958 return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { 15959 return { 15960 text: dataset.label, 15961 fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), 15962 hidden: !chart.isDatasetVisible(i), 15963 lineCap: dataset.borderCapStyle, 15964 lineDash: dataset.borderDash, 15965 lineDashOffset: dataset.borderDashOffset, 15966 lineJoin: dataset.borderJoinStyle, 15967 lineWidth: dataset.borderWidth, 15968 strokeStyle: dataset.borderColor, 15969 pointStyle: dataset.pointStyle, 15970 15971 // Below is extra data used for toggling the datasets 15972 datasetIndex: i 15973 }; 15974 }, this) : []; 15975 } 15976 } 15977 }, 15978 15979 legendCallback: function(chart) { 15980 var text = []; 15981 text.push('<ul class="' + chart.id + '-legend">'); 15982 for (var i = 0; i < chart.data.datasets.length; i++) { 15983 text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); 15984 if (chart.data.datasets[i].label) { 15985 text.push(chart.data.datasets[i].label); 15986 } 15987 text.push('</li>'); 15988 } 15989 text.push('</ul>'); 15990 return text.join(''); 15991 } 15992}); 15993 15994/** 15995 * Helper function to get the box width based on the usePointStyle option 15996 * @param labelopts {Object} the label options on the legend 15997 * @param fontSize {Number} the label font size 15998 * @return {Number} width of the color box area 15999 */ 16000function getBoxWidth(labelOpts, fontSize) { 16001 return labelOpts.usePointStyle ? 16002 fontSize * Math.SQRT2 : 16003 labelOpts.boxWidth; 16004} 16005 16006/** 16007 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! 16008 */ 16009var Legend = Element.extend({ 16010 16011 initialize: function(config) { 16012 helpers.extend(this, config); 16013 16014 // Contains hit boxes for each dataset (in dataset order) 16015 this.legendHitBoxes = []; 16016 16017 // Are we in doughnut mode which has a different data type 16018 this.doughnutMode = false; 16019 }, 16020 16021 // These methods are ordered by lifecycle. Utilities then follow. 16022 // Any function defined here is inherited by all legend types. 16023 // Any function can be extended by the legend type 16024 16025 beforeUpdate: noop, 16026 update: function(maxWidth, maxHeight, margins) { 16027 var me = this; 16028 16029 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) 16030 me.beforeUpdate(); 16031 16032 // Absorb the master measurements 16033 me.maxWidth = maxWidth; 16034 me.maxHeight = maxHeight; 16035 me.margins = margins; 16036 16037 // Dimensions 16038 me.beforeSetDimensions(); 16039 me.setDimensions(); 16040 me.afterSetDimensions(); 16041 // Labels 16042 me.beforeBuildLabels(); 16043 me.buildLabels(); 16044 me.afterBuildLabels(); 16045 16046 // Fit 16047 me.beforeFit(); 16048 me.fit(); 16049 me.afterFit(); 16050 // 16051 me.afterUpdate(); 16052 16053 return me.minSize; 16054 }, 16055 afterUpdate: noop, 16056 16057 // 16058 16059 beforeSetDimensions: noop, 16060 setDimensions: function() { 16061 var me = this; 16062 // Set the unconstrained dimension before label rotation 16063 if (me.isHorizontal()) { 16064 // Reset position before calculating rotation 16065 me.width = me.maxWidth; 16066 me.left = 0; 16067 me.right = me.width; 16068 } else { 16069 me.height = me.maxHeight; 16070 16071 // Reset position before calculating rotation 16072 me.top = 0; 16073 me.bottom = me.height; 16074 } 16075 16076 // Reset padding 16077 me.paddingLeft = 0; 16078 me.paddingTop = 0; 16079 me.paddingRight = 0; 16080 me.paddingBottom = 0; 16081 16082 // Reset minSize 16083 me.minSize = { 16084 width: 0, 16085 height: 0 16086 }; 16087 }, 16088 afterSetDimensions: noop, 16089 16090 // 16091 16092 beforeBuildLabels: noop, 16093 buildLabels: function() { 16094 var me = this; 16095 var labelOpts = me.options.labels || {}; 16096 var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; 16097 16098 if (labelOpts.filter) { 16099 legendItems = legendItems.filter(function(item) { 16100 return labelOpts.filter(item, me.chart.data); 16101 }); 16102 } 16103 16104 if (me.options.reverse) { 16105 legendItems.reverse(); 16106 } 16107 16108 me.legendItems = legendItems; 16109 }, 16110 afterBuildLabels: noop, 16111 16112 // 16113 16114 beforeFit: noop, 16115 fit: function() { 16116 var me = this; 16117 var opts = me.options; 16118 var labelOpts = opts.labels; 16119 var display = opts.display; 16120 16121 var ctx = me.ctx; 16122 16123 var globalDefault = defaults.global; 16124 var valueOrDefault = helpers.valueOrDefault; 16125 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); 16126 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); 16127 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); 16128 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16129 16130 // Reset hit boxes 16131 var hitboxes = me.legendHitBoxes = []; 16132 16133 var minSize = me.minSize; 16134 var isHorizontal = me.isHorizontal(); 16135 16136 if (isHorizontal) { 16137 minSize.width = me.maxWidth; // fill all the width 16138 minSize.height = display ? 10 : 0; 16139 } else { 16140 minSize.width = display ? 10 : 0; 16141 minSize.height = me.maxHeight; // fill all the height 16142 } 16143 16144 // Increase sizes here 16145 if (display) { 16146 ctx.font = labelFont; 16147 16148 if (isHorizontal) { 16149 // Labels 16150
16151 // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one 16152 var lineWidths = me.lineWidths = [0]; 16153 var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; 16154 16155 ctx.textAlign = 'left'; 16156 ctx.textBaseline = 'top'; 16157 16158 helpers.each(me.legendItems, function(legendItem, i) { 16159 var boxWidth = getBoxWidth(labelOpts, fontSize); 16160 var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; 16161 16162 if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { 16163 totalHeight += fontSize + (labelOpts.padding); 16164 lineWidths[lineWidths.length] = me.left; 16165 } 16166 16167 // Store the hitbox width and height here. Final position will be updated in `draw` 16168 hitboxes[i] = { 16169 left: 0, 16170 top: 0, 16171 width: width, 16172 height: fontSize 16173 }; 16174 16175 lineWidths[lineWidths.length - 1] += width + labelOpts.padding; 16176 }); 16177 16178 minSize.height += totalHeight; 16179 16180 } else { 16181 var vPadding = labelOpts.padding; 16182 var columnWidths = me.columnWidths = []; 16183 var totalWidth = labelOpts.padding; 16184 var currentColWidth = 0; 16185 var currentColHeight = 0; 16186 var itemHeight = fontSize + vPadding; 16187 16188 helpers.each(me.legendItems, function(legendItem, i) { 16189 var boxWidth = getBoxWidth(labelOpts, fontSize); 16190 var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; 16191 16192 // If too tall, go to new column 16193 if (currentColHeight + itemHeight > minSize.height) { 16194 totalWidth += currentColWidth + labelOpts.padding; 16195 columnWidths.push(currentColWidth); // previous column width 16196 16197 currentColWidth = 0; 16198 currentColHeight = 0; 16199 } 16200 16201 // Get max width 16202 currentColWidth = Math.max(currentColWidth, itemWidth); 16203 currentColHeight += itemHeight; 16204 16205 // Store the hitbox width and height here. Final position will be updated in `draw` 16206 hitboxes[i] = { 16207 left: 0, 16208 top: 0, 16209 width: itemWidth, 16210 height: fontSize 16211 }; 16212 }); 16213 16214 totalWidth += currentColWidth; 16215 columnWidths.push(currentColWidth); 16216 minSize.width += totalWidth; 16217 } 16218 } 16219 16220 me.width = minSize.width; 16221 me.height = minSize.height; 16222 }, 16223 afterFit: noop, 16224 16225 // Shared Methods 16226 isHorizontal: function() { 16227 return this.options.position === 'top' || this.options.position === 'bottom'; 16228 }, 16229 16230 // Actually draw the legend on the canvas 16231 draw: function() { 16232 var me = this; 16233 var opts = me.options; 16234 var labelOpts = opts.labels; 16235 var globalDefault = defaults.global; 16236 var lineDefault = globalDefault.elements.line; 16237 var legendWidth = me.width; 16238 var lineWidths = me.lineWidths; 16239 16240 if (opts.display) { 16241 var ctx = me.ctx; 16242 var valueOrDefault = helpers.valueOrDefault; 16243 var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); 16244 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); 16245 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); 16246 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); 16247 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16248 var cursor; 16249 16250 // Canvas setup 16251 ctx.textAlign = 'left'; 16252 ctx.textBaseline = 'middle'; 16253 ctx.lineWidth = 0.5; 16254 ctx.strokeStyle = fontColor; // for strikethrough effect 16255 ctx.fillStyle = fontColor; // render in correct colour 16256 ctx.font = labelFont; 16257 16258 var boxWidth = getBoxWidth(labelOpts, fontSize); 16259 var hitboxes = me.legendHitBoxes; 16260 16261 // current position 16262 var drawLegendBox = function(x, y, legendItem) { 16263 if (isNaN(boxWidth) || boxWidth <= 0) { 16264 return; 16265 } 16266 16267 // Set the ctx for the box 16268 ctx.save(); 16269 16270 ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); 16271 ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); 16272 ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); 16273 ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); 16274 ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
16275 ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); 16276 var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); 16277 16278 if (ctx.setLineDash) { 16279 // IE 9 and 10 do not support line dash 16280 ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); 16281 } 16282 16283 if (opts.labels && opts.labels.usePointStyle) { 16284 // Recalculate x and y for drawPoint() because its expecting 16285 // x and y to be center of figure (instead of top left) 16286 var radius = fontSize * Math.SQRT2 / 2; 16287 var offSet = radius / Math.SQRT2; 16288 var centerX = x + offSet; 16289 var centerY = y + offSet; 16290 16291 // Draw pointStyle as legend symbol 16292 helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); 16293 } else { 16294 // Draw box as legend symbol 16295 if (!isLineWidthZero) { 16296 ctx.strokeRect(x, y, boxWidth, fontSize); 16297 } 16298 ctx.fillRect(x, y, boxWidth, fontSize); 16299 } 16300 16301 ctx.restore(); 16302 }; 16303 var fillText = function(x, y, legendItem, textWidth) { 16304 var halfFontSize = fontSize / 2; 16305 var xLeft = boxWidth + halfFontSize + x; 16306 var yMiddle = y + halfFontSize; 16307 16308 ctx.fillText(legendItem.text, xLeft, yMiddle); 16309 16310 if (legendItem.hidden) { 16311 // Strikethrough the text if hidden 16312 ctx.beginPath(); 16313 ctx.lineWidth = 2; 16314 ctx.moveTo(xLeft, yMiddle); 16315 ctx.lineTo(xLeft + textWidth, yMiddle); 16316 ctx.stroke(); 16317 } 16318 }; 16319 16320 // Horizontal 16321 var isHorizontal = me.isHorizontal(); 16322 if (isHorizontal) { 16323 cursor = { 16324 x: me.left + ((legendWidth - lineWidths[0]) / 2), 16325 y: me.top + labelOpts.padding, 16326 line: 0 16327 }; 16328 } else { 16329 cursor = { 16330 x: me.left + labelOpts.padding, 16331 y: me.top + labelOpts.padding, 16332 line: 0 16333 }; 16334 } 16335 16336 var itemHeight = fontSize + labelOpts.padding; 16337 helpers.each(me.legendItems, function(legendItem, i) { 16338 var textWidth = ctx.measureText(legendItem.text).width; 16339 var width = boxWidth + (fontSize / 2) + textWidth; 16340 var x = cursor.x; 16341 var y = cursor.y; 16342 16343 if (isHorizontal) { 16344 if (x + width >= legendWidth) { 16345 y = cursor.y += itemHeight; 16346 cursor.line++; 16347 x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); 16348 } 16349 } else if (y + itemHeight > me.bottom) { 16350 x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; 16351 y = cursor.y = me.top + labelOpts.padding; 16352 cursor.line++; 16353 } 16354 16355 drawLegendBox(x, y, legendItem); 16356 16357 hitboxes[i].left = x; 16358 hitboxes[i].top = y; 16359 16360 // Fill the actual label 16361 fillText(x, y, legendItem, textWidth); 16362 16363 if (isHorizontal) { 16364 cursor.x += width + (labelOpts.padding); 16365 } else { 16366 cursor.y += itemHeight; 16367 } 16368 16369 }); 16370 } 16371 }, 16372 16373 /** 16374 * Handle an event 16375 * @private 16376 * @param {IEvent} event - The event to handle 16377 * @return {Boolean} true if a change occured 16378 */ 16379 handleEvent: function(e) { 16380 var me = this; 16381 var opts = me.options; 16382 var type = e.type === 'mouseup' ? 'click' : e.type; 16383 var changed = false; 16384 16385 if (type === 'mousemove') { 16386 if (!opts.onHover) { 16387 return; 16388 } 16389 } else if (type === 'click') { 16390 if (!opts.onClick) { 16391 return; 16392 } 16393 } else { 16394 return; 16395 } 16396 16397 // Chart event already has relative position in it 16398 var x = e.x; 16399 var y = e.y; 16400 16401 if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { 16402 // See if we are touching one of the dataset boxes 16403 var lh = me.legendHitBoxes; 16404 for (var i = 0; i < lh.length; ++i) { 16405 var hitBox = lh[i]; 16406 16407 if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { 16408 // Touching an element 16409 if (type === 'click') { 16410 // use e.native for backwards compatibility 16411 opts.onClick.call(me, e.native, me.legendItems[i]); 16412 changed = true; 16413 break; 16414 } else if (type === 'mousemove') { 16415 // use e.native for backwards compatibility 16416 opts.onHover.call(me, e.native, me.legendItems[i]); 16417 changed = true; 16418 break; 16419 } 16420 } 16421 } 16422 } 16423 16424 return changed; 16425 } 16426}); 16427 16428function createNewLegendAndAttach(chart, legendOpts) { 16429 var legend = new Legend({ 16430 ctx: chart.ctx, 16431 options: legendOpts, 16432 chart: chart 16433 }); 16434 16435 layouts.configure(chart, legend, legendOpts); 16436 layouts.addBox(chart, legend); 16437 chart.legend = legend; 16438} 16439 16440module.exports = { 16441 id: 'legend', 16442 16443 /** 16444 * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making 16445 * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of 16446 * the plugin, which one will be re-exposed in the chart.js file. 16447 * https://github.com/chartjs/Chart.js/pull/2640 16448 * @private 16449 */ 16450 _element: Legend, 16451
vendor: 5,367 bytes, lines 16452-16664
16452 beforeInit: function(chart) { 16453 var legendOpts = chart.options.legend; 16454 16455 if (legendOpts) { 16456 createNewLegendAndAttach(chart, legendOpts); 16457 } 16458 }, 16459 16460 beforeUpdate: function(chart) { 16461 var legendOpts = chart.options.legend; 16462 var legend = chart.legend; 16463 16464 if (legendOpts) { 16465 helpers.mergeIf(legendOpts, defaults.global.legend); 16466 16467 if (legend) { 16468 layouts.configure(chart, legend, legendOpts); 16469 legend.options = legendOpts; 16470 } else { 16471 createNewLegendAndAttach(chart, legendOpts); 16472 } 16473 } else if (legend) { 16474 layouts.removeBox(chart, legend); 16475 delete chart.legend; 16476 } 16477 }, 16478 16479 afterEvent: function(chart, e) { 16480 var legend = chart.legend; 16481 if (legend) { 16482 legend.handleEvent(e); 16483 } 16484 } 16485}; 16486 16487},{"25":25,"26":26,"30":30,"45":45}],52:[function(require,module,exports){ 16488'use strict'; 16489 16490var defaults = require(25); 16491var Element = require(26); 16492var helpers = require(45); 16493var layouts = require(30); 16494 16495var noop = helpers.noop; 16496 16497defaults._set('global', { 16498 title: { 16499 display: false, 16500 fontStyle: 'bold', 16501 fullWidth: true, 16502 lineHeight: 1.2, 16503 padding: 10, 16504 position: 'top', 16505 text: '', 16506 weight: 2000 // by default greater than legend (1000) to be above 16507 } 16508}); 16509 16510/** 16511 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! 16512 */ 16513var Title = Element.extend({ 16514 initialize: function(config) { 16515 var me = this; 16516 helpers.extend(me, config); 16517 16518 // Contains hit boxes for each dataset (in dataset order) 16519 me.legendHitBoxes = []; 16520 }, 16521 16522 // These methods are ordered by lifecycle. Utilities then follow. 16523 16524 beforeUpdate: noop, 16525 update: function(maxWidth, maxHeight, margins) { 16526 var me = this; 16527 16528 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) 16529 me.beforeUpdate(); 16530 16531 // Absorb the master measurements 16532 me.maxWidth = maxWidth; 16533 me.maxHeight = maxHeight; 16534 me.margins = margins; 16535 16536 // Dimensions 16537 me.beforeSetDimensions(); 16538 me.setDimensions(); 16539 me.afterSetDimensions(); 16540 // Labels 16541 me.beforeBuildLabels(); 16542 me.buildLabels(); 16543 me.afterBuildLabels(); 16544 16545 // Fit 16546 me.beforeFit(); 16547 me.fit(); 16548 me.afterFit(); 16549 // 16550 me.afterUpdate(); 16551 16552 return me.minSize; 16553 16554 }, 16555 afterUpdate: noop, 16556 16557 // 16558 16559 beforeSetDimensions: noop, 16560 setDimensions: function() { 16561 var me = this; 16562 // Set the unconstrained dimension before label rotation 16563 if (me.isHorizontal()) { 16564 // Reset position before calculating rotation 16565 me.width = me.maxWidth; 16566 me.left = 0; 16567 me.right = me.width; 16568 } else { 16569 me.height = me.maxHeight; 16570 16571 // Reset position before calculating rotation 16572 me.top = 0; 16573 me.bottom = me.height; 16574 } 16575 16576 // Reset padding 16577 me.paddingLeft = 0; 16578 me.paddingTop = 0; 16579 me.paddingRight = 0; 16580 me.paddingBottom = 0; 16581 16582 // Reset minSize 16583 me.minSize = { 16584 width: 0, 16585 height: 0 16586 }; 16587 }, 16588 afterSetDimensions: noop, 16589 16590 // 16591 16592 beforeBuildLabels: noop, 16593 buildLabels: noop, 16594 afterBuildLabels: noop, 16595 16596 // 16597 16598 beforeFit: noop, 16599 fit: function() { 16600 var me = this; 16601 var valueOrDefault = helpers.valueOrDefault; 16602 var opts = me.options; 16603 var display = opts.display; 16604 var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); 16605 var minSize = me.minSize; 16606 var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; 16607 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); 16608 var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; 16609 16610 if (me.isHorizontal()) { 16611 minSize.width = me.maxWidth; // fill all the width 16612 minSize.height = textSize; 16613 } else { 16614 minSize.width = textSize; 16615 minSize.height = me.maxHeight; // fill all the height 16616 } 16617 16618 me.width = minSize.width; 16619 me.height = minSize.height; 16620 16621 }, 16622 afterFit: noop, 16623 16624 // Shared Methods 16625 isHorizontal: function() { 16626 var pos = this.options.position; 16627 return pos === 'top' || pos === 'bottom'; 16628 }, 16629 16630 // Actually draw the title block on the canvas 16631 draw: function() { 16632 var me = this; 16633 var ctx = me.ctx; 16634 var valueOrDefault = helpers.valueOrDefault; 16635 var opts = me.options; 16636 var globalDefaults = defaults.global; 16637 16638 if (opts.display) { 16639 var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); 16640 var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); 16641 var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); 16642 var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16643 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); 16644 var offset = lineHeight / 2 + opts.padding; 16645 var rotation = 0; 16646 var top = me.top; 16647 var left = me.left; 16648 var bottom = me.bottom; 16649 var right = me.right; 16650 var maxWidth, titleX, titleY; 16651 16652 ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour 16653 ctx.font = titleFont; 16654 16655 // Horizontal 16656 if (me.isHorizontal()) { 16657 titleX = left + ((right - left) / 2); // midpoint of the width 16658 titleY = top + offset; 16659 maxWidth = right - left; 16660 } else { 16661 titleX = opts.position === 'left' ? left + offset : right - offset; 16662 titleY = top + ((bottom - top) / 2); 16663 maxWidth = bottom - top; 16664 rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
16665 } 16666 16667 ctx.save(); 16668 ctx.translate(titleX, titleY); 16669 ctx.rotate(rotation); 16670 ctx.textAlign = 'center'; 16671 ctx.textBaseline = 'middle'; 16672 16673 var text = opts.text; 16674 if (helpers.isArray(text)) { 16675 var y = 0; 16676 for (var i = 0; i < text.length; ++i) { 16677 ctx.fillText(text[i], 0, y, maxWidth); 16678 y += lineHeight; 16679 } 16680 } else { 16681 ctx.fillText(text, 0, 0, maxWidth); 16682 } 16683 16684 ctx.restore(); 16685 } 16686 } 16687}); 16688 16689function createNewTitleBlockAndAttach(chart, titleOpts) { 16690 var title = new Title({ 16691 ctx: chart.ctx, 16692 options: titleOpts, 16693 chart: chart 16694 }); 16695 16696 layouts.configure(chart, title, titleOpts); 16697 layouts.addBox(chart, title); 16698 chart.titleBlock = title; 16699} 16700 16701module.exports = { 16702 id: 'title', 16703 16704 /** 16705 * Backward compatibility: since 2.1.5, the title is registered as a plugin, making 16706 * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of 16707 * the plugin, which one will be re-exposed in the chart.js file. 16708 * https://github.com/chartjs/Chart.js/pull/2640 16709 * @private 16710 */ 16711 _element: Title, 16712 16713 beforeInit: function(chart) { 16714 var titleOpts = chart.options.title; 16715 16716 if (titleOpts) { 16717 createNewTitleBlockAndAttach(chart, titleOpts); 16718 } 16719 }, 16720 16721 beforeUpdate: function(chart) { 16722 var titleOpts = chart.options.title; 16723 var titleBlock = chart.titleBlock; 16724 16725 if (titleOpts) { 16726 helpers.mergeIf(titleOpts, defaults.global.title); 16727 16728 if (titleBlock) { 16729 layouts.configure(chart, titleBlock, titleOpts); 16730 titleBlock.options = titleOpts; 16731 } else { 16732 createNewTitleBlockAndAttach(chart, titleOpts); 16733 } 16734 } else if (titleBlock) { 16735 layouts.removeBox(chart, titleBlock); 16736 delete chart.titleBlock; 16737 } 16738 } 16739}; 16740 16741},{"25":25,"26":26,"30":30,"45":45}],53:[function(require,module,exports){ 16742'use strict'; 16743 16744module.exports = function(Chart) { 16745 16746 // Default config for a category scale 16747 var defaultConfig = { 16748 position: 'bottom' 16749 }; 16750 16751 var DatasetScale = Chart.Scale.extend({ 16752 /** 16753 * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those 16754 * else fall back to data.labels 16755 * @private 16756 */ 16757 getLabels: function() { 16758 var data = this.chart.data; 16759 return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; 16760 }, 16761 16762 determineDataLimits: function() { 16763 var me = this; 16764 var labels = me.getLabels(); 16765 me.minIndex = 0; 16766 me.maxIndex = labels.length - 1; 16767 var findIndex; 16768 16769 if (me.options.ticks.min !== undefined) { 16770 // user specified min value 16771 findIndex = labels.indexOf(me.options.ticks.min); 16772 me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; 16773 } 16774 16775 if (me.options.ticks.max !== undefined) { 16776 // user specified max value 16777 findIndex = labels.indexOf(me.options.ticks.max); 16778 me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; 16779 } 16780 16781 me.min = labels[me.minIndex]; 16782 me.max = labels[me.maxIndex]; 16783 }, 16784 16785 buildTicks: function() { 16786 var me = this; 16787 var labels = me.getLabels(); 16788 // If we are viewing some subset of labels, slice the original array 16789 me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); 16790 }, 16791 16792 getLabelForIndex: function(index, datasetIndex) { 16793 var me = this; 16794 var data = me.chart.data; 16795 var isHorizontal = me.isHorizontal(); 16796 16797 if (data.yLabels && !isHorizontal) { 16798 return me.getRightValue(data.datasets[datasetIndex].data[index]); 16799 } 16800 return me.ticks[index - me.minIndex]; 16801 }, 16802 16803 // Used to get data value locations. Value can either be an index or a numerical value 16804 getPixelForValue: function(value, index) { 16805 var me = this; 16806 var offset = me.options.offset; 16807 // 1 is added because we need the length but we have the indexes 16808 var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); 16809 16810 // If value is a data object, then index is the index in the data array, 16811 // not the index of the scale. We need to change that. 16812 var valueCategory; 16813 if (value !== undefined && value !== null) { 16814 valueCategory = me.isHorizontal() ? value.x : value.y; 16815 }
16816 if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { 16817 var labels = me.getLabels(); 16818 value = valueCategory || value; 16819 var idx = labels.indexOf(value); 16820 index = idx !== -1 ? idx : index; 16821 } 16822 16823 if (me.isHorizontal()) { 16824 var valueWidth = me.width / offsetAmt; 16825 var widthOffset = (valueWidth * (index - me.minIndex)); 16826 16827 if (offset) { 16828 widthOffset += (valueWidth / 2); 16829 } 16830 16831 return me.left + Math.round(widthOffset); 16832 } 16833 var valueHeight = me.height / offsetAmt; 16834 var heightOffset = (valueHeight * (index - me.minIndex)); 16835 16836 if (offset) { 16837 heightOffset += (valueHeight / 2); 16838 } 16839 16840 return me.top + Math.round(heightOffset); 16841 }, 16842 getPixelForTick: function(index) { 16843 return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); 16844 }, 16845 getValueForPixel: function(pixel) { 16846 var me = this; 16847 var offset = me.options.offset; 16848 var value; 16849 var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); 16850 var horz = me.isHorizontal(); 16851 var valueDimension = (horz ? me.width : me.height) / offsetAmt; 16852 16853 pixel -= horz ? me.left : me.top; 16854 16855 if (offset) { 16856 pixel -= (valueDimension / 2); 16857 } 16858 16859 if (pixel <= 0) { 16860 value = 0; 16861 } else { 16862 value = Math.round(pixel / valueDimension); 16863 } 16864 16865 return value + me.minIndex; 16866 }, 16867 getBasePixel: function() { 16868 return this.bottom; 16869 } 16870 }); 16871 16872 Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); 16873 16874}; 16875 16876},{}],54:[function(require,module,exports){ 16877'use strict'; 16878 16879var defaults = require(25); 16880var helpers = require(45); 16881var Ticks = require(34); 16882 16883module.exports = function(Chart) { 16884 16885 var defaultConfig = { 16886 position: 'left', 16887 ticks: { 16888 callback: Ticks.formatters.linear 16889 } 16890 }; 16891 16892 var LinearScale = Chart.LinearScaleBase.extend({ 16893 16894 determineDataLimits: function() { 16895 var me = this; 16896 var opts = me.options; 16897 var chart = me.chart; 16898 var data = chart.data; 16899 var datasets = data.datasets; 16900 var isHorizontal = me.isHorizontal(); 16901 var DEFAULT_MIN = 0; 16902 var DEFAULT_MAX = 1; 16903 16904 function IDMatches(meta) { 16905 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; 16906 } 16907 16908 // First Calculate the range 16909 me.min = null; 16910 me.max = null; 16911 16912 var hasStacks = opts.stacked; 16913 if (hasStacks === undefined) { 16914 helpers.each(datasets, function(dataset, datasetIndex) { 16915 if (hasStacks) { 16916 return; 16917 } 16918 16919 var meta = chart.getDatasetMeta(datasetIndex); 16920 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && 16921 meta.stack !== undefined) { 16922 hasStacks = true; 16923 } 16924 }); 16925 } 16926 16927 if (opts.stacked || hasStacks) { 16928 var valuesPerStack = {}; 16929 16930 helpers.each(datasets, function(dataset, datasetIndex) { 16931 var meta = chart.getDatasetMeta(datasetIndex); 16932 var key = [ 16933 meta.type, 16934 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined 16935 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), 16936 meta.stack 16937 ].join('.'); 16938 16939 if (valuesPerStack[key] === undefined) { 16940 valuesPerStack[key] = { 16941 positiveValues: [], 16942 negativeValues: [] 16943 }; 16944 } 16945 16946 // Store these per type 16947 var positiveValues = valuesPerStack[key].positiveValues; 16948 var negativeValues = valuesPerStack[key].negativeValues; 16949 16950 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 16951 helpers.each(dataset.data, function(rawValue, index) { 16952 var value = +me.getRightValue(rawValue); 16953 if (isNaN(value) || meta.data[index].hidden) { 16954 return; 16955 } 16956 16957 positiveValues[index] = positiveValues[index] || 0; 16958 negativeValues[index] = negativeValues[index] || 0; 16959 16960 if (opts.relativePoints) { 16961 positiveValues[index] = 100; 16962 } else if (value < 0) { 16963 negativeValues[index] += value; 16964 } else { 16965 positiveValues[index] += value; 16966 } 16967 }); 16968 } 16969 }); 16970 16971 helpers.each(valuesPerStack, function(valuesForType) { 16972 var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); 16973 var minVal = helpers.min(values); 16974 var maxVal = helpers.max(values); 16975 me.min = me.min === null ? minVal : Math.min(me.min, minVal); 16976 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); 16977 }); 16978 16979 } else { 16980 helpers.each(datasets, function(dataset, datasetIndex) { 16981 var meta = chart.getDatasetMeta(datasetIndex); 16982 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 16983 helpers.each(dataset.data, function(rawValue, index) { 16984 var value = +me.getRightValue(rawValue); 16985 if (isNaN(value) || meta.data[index].hidden) { 16986 return; 16987 } 16988 16989 if (me.min === null) { 16990 me.min = value; 16991 } else if (value < me.min) { 16992 me.min = value; 16993 } 16994 16995 if (me.max === null) { 16996 me.max = value; 16997 } else if (value > me.max) { 16998 me.max = value; 16999 } 17000 });
17001 } 17002 }); 17003 } 17004 17005 me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; 17006 me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; 17007 17008 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero 17009 this.handleTickRangeOptions(); 17010 }, 17011 getTickLimit: function() { 17012 var maxTicks; 17013 var me = this; 17014 var tickOpts = me.options.ticks; 17015 17016 if (me.isHorizontal()) { 17017 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); 17018 } else { 17019 // The factor of 2 used to scale the font size has been experimentally determined. 17020 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); 17021 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); 17022 } 17023 17024 return maxTicks; 17025 }, 17026 // Called after the ticks are built. We need 17027 handleDirectionalChanges: function() { 17028 if (!this.isHorizontal()) { 17029 // We are in a vertical orientation. The top value is the highest. So reverse the array 17030 this.ticks.reverse(); 17031 } 17032 }, 17033 getLabelForIndex: function(index, datasetIndex) { 17034 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17035 }, 17036 // Utils 17037 getPixelForValue: function(value) { 17038 // This must be called after fit has been run so that 17039 // this.left, this.top, this.right, and this.bottom have been defined 17040 var me = this; 17041 var start = me.start; 17042 17043 var rightValue = +me.getRightValue(value); 17044 var pixel; 17045 var range = me.end - start; 17046 17047 if (me.isHorizontal()) { 17048 pixel = me.left + (me.width / range * (rightValue - start)); 17049 } else { 17050 pixel = me.bottom - (me.height / range * (rightValue - start)); 17051 } 17052 return pixel; 17053 }, 17054 getValueForPixel: function(pixel) { 17055 var me = this; 17056 var isHorizontal = me.isHorizontal(); 17057 var innerDimension = isHorizontal ? me.width : me.height; 17058 var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; 17059 return me.start + ((me.end - me.start) * offset); 17060 }, 17061 getPixelForTick: function(index) { 17062 return this.getPixelForValue(this.ticksAsNumbers[index]); 17063 } 17064 }); 17065 Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); 17066 17067}; 17068 17069},{"25":25,"34":34,"45":45}],55:[function(require,module,exports){ 17070'use strict'; 17071 17072var helpers = require(45); 17073 17074/** 17075 * Generate a set of linear ticks 17076 * @param generationOptions the options used to generate the ticks 17077 * @param dataRange the range of the data 17078 * @returns {Array<Number>} array of tick values 17079 */ 17080function generateTicks(generationOptions, dataRange) { 17081 var ticks = []; 17082 // To get a "nice" value for the tick spacing, we will use the appropriately named 17083 // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks 17084 // for details. 17085 17086 var spacing; 17087 if (generationOptions.stepSize && generationOptions.stepSize > 0) { 17088 spacing = generationOptions.stepSize; 17089 } else { 17090 var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); 17091 spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); 17092 } 17093 var niceMin = Math.floor(dataRange.min / spacing) * spacing; 17094 var niceMax = Math.ceil(dataRange.max / spacing) * spacing; 17095 17096 // If min, max and stepSize is set and they make an evenly spaced scale use it. 17097 if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { 17098 // If very close to our whole number, use it. 17099 if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { 17100 niceMin = generationOptions.min; 17101 niceMax = generationOptions.max; 17102 } 17103 } 17104 17105 var numSpaces = (niceMax - niceMin) / spacing; 17106 // If very close to our rounded value, use it. 17107 if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { 17108 numSpaces = Math.round(numSpaces); 17109 } else { 17110 numSpaces = Math.ceil(numSpaces); 17111 } 17112 17113 var precision = 1; 17114 if (spacing < 1) { 17115 precision = Math.pow(10, spacing.toString().length - 2); 17116 niceMin = Math.round(niceMin * precision) / precision; 17117 niceMax = Math.round(niceMax * precision) / precision; 17118 } 17119 ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); 17120 for (var j = 1; j < numSpaces; ++j) { 17121 ticks.push(Math.round((niceMin + j * spacing) * precision) / precision); 17122 } 17123 ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); 17124 17125 return ticks; 17126} 17127 17128 17129module.exports = function(Chart) { 17130 17131 var noop = helpers.noop; 17132 17133 Chart.LinearScaleBase = Chart.Scale.extend({ 17134 getRightValue: function(value) { 17135 if (typeof value === 'string') { 17136 return +value; 17137 } 17138 return Chart.Scale.prototype.getRightValue.call(this, value); 17139 }, 17140 17141 handleTickRangeOptions: function() { 17142 var me = this; 17143 var opts = me.options; 17144 var tickOpts = opts.ticks; 17145 17146 // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, 17147 // do nothing since that would make the chart weird. If the user really wants a weird chart 17148 // axis, they can manually override it 17149 if (tickOpts.beginAtZero) { 17150 var minSign = helpers.sign(me.min); 17151 var maxSign = helpers.sign(me.max); 17152 17153 if (minSign < 0 && maxSign < 0) { 17154 // move the top up to 0 17155 me.max = 0; 17156 } else if (minSign > 0 && maxSign > 0) { 17157 // move the bottom down to 0 17158 me.min = 0; 17159 } 17160 } 17161 17162 var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; 17163 var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; 17164 17165 if (tickOpts.min !== undefined) { 17166 me.min = tickOpts.min; 17167 } else if (tickOpts.suggestedMin !== undefined) { 17168 if (me.min === null) { 17169 me.min = tickOpts.suggestedMin; 17170 } else { 17171 me.min = Math.min(me.min, tickOpts.suggestedMin);
17172 } 17173 } 17174 17175 if (tickOpts.max !== undefined) { 17176 me.max = tickOpts.max; 17177 } else if (tickOpts.suggestedMax !== undefined) { 17178 if (me.max === null) { 17179 me.max = tickOpts.suggestedMax; 17180 } else { 17181 me.max = Math.max(me.max, tickOpts.suggestedMax); 17182 } 17183 } 17184 17185 if (setMin !== setMax) { 17186 // We set the min or the max but not both. 17187 // So ensure that our range is good 17188 // Inverted or 0 length range can happen when 17189 // ticks.min is set, and no datasets are visible 17190 if (me.min >= me.max) { 17191 if (setMin) { 17192 me.max = me.min + 1; 17193 } else { 17194 me.min = me.max - 1; 17195 } 17196 } 17197 } 17198 17199 if (me.min === me.max) { 17200 me.max++; 17201 17202 if (!tickOpts.beginAtZero) { 17203 me.min--; 17204 } 17205 } 17206 }, 17207 getTickLimit: noop, 17208 handleDirectionalChanges: noop, 17209 17210 buildTicks: function() { 17211 var me = this; 17212 var opts = me.options; 17213 var tickOpts = opts.ticks; 17214 17215 // Figure out what the max number of ticks we can support it is based on the size of 17216 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 17217 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 17218 // the graph. Make sure we always have at least 2 ticks 17219 var maxTicks = me.getTickLimit(); 17220 maxTicks = Math.max(2, maxTicks); 17221 17222 var numericGeneratorOptions = { 17223 maxTicks: maxTicks, 17224 min: tickOpts.min, 17225 max: tickOpts.max, 17226 stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) 17227 }; 17228 var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); 17229 17230 me.handleDirectionalChanges(); 17231 17232 // At this point, we need to update our max and min given the tick values since we have expanded the 17233 // range of the scale 17234 me.max = helpers.max(ticks); 17235 me.min = helpers.min(ticks); 17236 17237 if (tickOpts.reverse) { 17238 ticks.reverse(); 17239 17240 me.start = me.max; 17241 me.end = me.min; 17242 } else { 17243 me.start = me.min; 17244 me.end = me.max; 17245 } 17246 }, 17247 convertTicksToLabels: function() { 17248 var me = this; 17249 me.ticksAsNumbers = me.ticks.slice(); 17250 me.zeroLineIndex = me.ticks.indexOf(0); 17251 17252 Chart.Scale.prototype.convertTicksToLabels.call(me); 17253 } 17254 }); 17255}; 17256 17257},{"45":45}],56:[function(require,module,exports){ 17258'use strict'; 17259 17260var helpers = require(45); 17261var Ticks = require(34); 17262 17263/** 17264 * Generate a set of logarithmic ticks 17265 * @param generationOptions the options used to generate the ticks 17266 * @param dataRange the range of the data 17267 * @returns {Array<Number>} array of tick values 17268 */ 17269function generateTicks(generationOptions, dataRange) { 17270 var ticks = []; 17271 var valueOrDefault = helpers.valueOrDefault; 17272 17273 // Figure out what the max number of ticks we can support it is based on the size of 17274 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 17275 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 17276 // the graph 17277 var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); 17278 17279 var endExp = Math.floor(helpers.log10(dataRange.max)); 17280 var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); 17281 var exp, significand; 17282 17283 if (tickVal === 0) { 17284 exp = Math.floor(helpers.log10(dataRange.minNotZero)); 17285 significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); 17286 17287 ticks.push(tickVal); 17288 tickVal = significand * Math.pow(10, exp); 17289 } else { 17290 exp = Math.floor(helpers.log10(tickVal)); 17291 significand = Math.floor(tickVal / Math.pow(10, exp)); 17292 } 17293 var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; 17294 17295 do { 17296 ticks.push(tickVal); 17297 17298 ++significand; 17299 if (significand === 10) { 17300 significand = 1; 17301 ++exp; 17302 precision = exp >= 0 ? 1 : precision; 17303 } 17304 17305 tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; 17306 } while (exp < endExp || (exp === endExp && significand < endSignificand)); 17307 17308 var lastTick = valueOrDefault(generationOptions.max, tickVal); 17309 ticks.push(lastTick); 17310 17311 return ticks; 17312} 17313 17314 17315module.exports = function(Chart) { 17316 17317 var defaultConfig = { 17318 position: 'left', 17319 17320 // label settings 17321 ticks: { 17322 callback: Ticks.formatters.logarithmic 17323 } 17324 }; 17325 17326 var LogarithmicScale = Chart.Scale.extend({ 17327 determineDataLimits: function() { 17328 var me = this; 17329 var opts = me.options; 17330 var chart = me.chart; 17331 var data = chart.data; 17332 var datasets = data.datasets; 17333 var isHorizontal = me.isHorizontal(); 17334 function IDMatches(meta) { 17335 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; 17336 } 17337 17338 // Calculate Range 17339 me.min = null; 17340 me.max = null; 17341 me.minNotZero = null; 17342 17343 var hasStacks = opts.stacked; 17344 if (hasStacks === undefined) { 17345 helpers.each(datasets, function(dataset, datasetIndex) { 17346 if (hasStacks) { 17347 return; 17348 } 17349 17350 var meta = chart.getDatasetMeta(datasetIndex); 17351 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && 17352 meta.stack !== undefined) { 17353 hasStacks = true; 17354 } 17355 }); 17356 } 17357 17358 if (opts.stacked || hasStacks) { 17359 var valuesPerStack = {}; 17360 17361 helpers.each(datasets, function(dataset, datasetIndex) { 17362 var meta = chart.getDatasetMeta(datasetIndex); 17363 var key = [ 17364 meta.type, 17365 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined 17366 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), 17367 meta.stack 17368 ].join('.'); 17369 17370 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17371 if (valuesPerStack[key] === undefined) { 17372 valuesPerStack[key] = []; 17373 } 17374 17375 helpers.each(dataset.data, function(rawValue, index) { 17376 var values = valuesPerStack[key]; 17377 var value = +me.getRightValue(rawValue); 17378 // invalid, hidden and negative values are ignored 17379 if (isNaN(value) || meta.data[index].hidden || value < 0) { 17380 return; 17381 } 17382 values[index] = values[index] || 0; 17383 values[index] += value; 17384 });
17385 } 17386 }); 17387 17388 helpers.each(valuesPerStack, function(valuesForType) { 17389 if (valuesForType.length > 0) { 17390 var minVal = helpers.min(valuesForType); 17391 var maxVal = helpers.max(valuesForType); 17392 me.min = me.min === null ? minVal : Math.min(me.min, minVal); 17393 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); 17394 } 17395 }); 17396 17397 } else { 17398 helpers.each(datasets, function(dataset, datasetIndex) { 17399 var meta = chart.getDatasetMeta(datasetIndex); 17400 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17401 helpers.each(dataset.data, function(rawValue, index) { 17402 var value = +me.getRightValue(rawValue); 17403 // invalid, hidden and negative values are ignored 17404 if (isNaN(value) || meta.data[index].hidden || value < 0) { 17405 return; 17406 } 17407 17408 if (me.min === null) { 17409 me.min = value; 17410 } else if (value < me.min) { 17411 me.min = value; 17412 } 17413 17414 if (me.max === null) { 17415 me.max = value; 17416 } else if (value > me.max) { 17417 me.max = value; 17418 } 17419 17420 if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { 17421 me.minNotZero = value; 17422 } 17423 }); 17424 } 17425 }); 17426 } 17427 17428 // Common base implementation to handle ticks.min, ticks.max 17429 this.handleTickRangeOptions(); 17430 }, 17431 handleTickRangeOptions: function() { 17432 var me = this; 17433 var opts = me.options; 17434 var tickOpts = opts.ticks; 17435 var valueOrDefault = helpers.valueOrDefault; 17436 var DEFAULT_MIN = 1; 17437 var DEFAULT_MAX = 10; 17438 17439 me.min = valueOrDefault(tickOpts.min, me.min); 17440 me.max = valueOrDefault(tickOpts.max, me.max); 17441 17442 if (me.min === me.max) { 17443 if (me.min !== 0 && me.min !== null) { 17444 me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); 17445 me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); 17446 } else { 17447 me.min = DEFAULT_MIN; 17448 me.max = DEFAULT_MAX; 17449 } 17450 } 17451 if (me.min === null) { 17452 me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1); 17453 } 17454 if (me.max === null) { 17455 me.max = me.min !== 0 17456 ? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1) 17457 : DEFAULT_MAX; 17458 } 17459 if (me.minNotZero === null) { 17460 if (me.min > 0) { 17461 me.minNotZero = me.min; 17462 } else if (me.max < 1) { 17463 me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max))); 17464 } else { 17465 me.minNotZero = DEFAULT_MIN; 17466 } 17467 } 17468 }, 17469 buildTicks: function() { 17470 var me = this; 17471 var opts = me.options; 17472 var tickOpts = opts.ticks; 17473 var reverse = !me.isHorizontal(); 17474 17475 var generationOptions = { 17476 min: tickOpts.min, 17477 max: tickOpts.max 17478 }; 17479 var ticks = me.ticks = generateTicks(generationOptions, me); 17480 17481 // At this point, we need to update our max and min given the tick values since we have expanded the 17482 // range of the scale 17483 me.max = helpers.max(ticks); 17484 me.min = helpers.min(ticks); 17485 17486 if (tickOpts.reverse) { 17487 reverse = !reverse; 17488 me.start = me.max; 17489 me.end = me.min; 17490 } else { 17491 me.start = me.min; 17492 me.end = me.max; 17493 } 17494 if (reverse) { 17495 ticks.reverse(); 17496 } 17497 }, 17498 convertTicksToLabels: function() { 17499 this.tickValues = this.ticks.slice(); 17500 17501 Chart.Scale.prototype.convertTicksToLabels.call(this); 17502 }, 17503 // Get the correct tooltip label 17504 getLabelForIndex: function(index, datasetIndex) { 17505 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17506 }, 17507 getPixelForTick: function(index) { 17508 return this.getPixelForValue(this.tickValues[index]); 17509 }, 17510 /** 17511 * Returns the value of the first tick. 17512 * @param {Number} value - The minimum not zero value. 17513 * @return {Number} The first tick value. 17514 * @private 17515 */ 17516 _getFirstTickValue: function(value) { 17517 var exp = Math.floor(helpers.log10(value)); 17518 var significand = Math.floor(value / Math.pow(10, exp)); 17519 17520 return significand * Math.pow(10, exp); 17521 }, 17522 getPixelForValue: function(value) { 17523 var me = this; 17524 var reverse = me.options.ticks.reverse; 17525 var log10 = helpers.log10; 17526 var firstTickValue = me._getFirstTickValue(me.minNotZero); 17527 var offset = 0; 17528 var innerDimension, pixel, start, end, sign; 17529 17530 value = +me.getRightValue(value); 17531 if (reverse) { 17532 start = me.end; 17533 end = me.start; 17534 sign = -1; 17535 } else { 17536 start = me.start; 17537 end = me.end; 17538 sign = 1; 17539 } 17540 if (me.isHorizontal()) { 17541 innerDimension = me.width; 17542 pixel = reverse ? me.right : me.left; 17543 } else { 17544 innerDimension = me.height; 17545 sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0) 17546 pixel = reverse ? me.top : me.bottom; 17547 } 17548 if (value !== start) { 17549 if (start === 0) { // include zero tick 17550 offset = helpers.getValueOrDefault( 17551 me.options.ticks.fontSize, 17552 Chart.defaults.global.defaultFontSize 17553 ); 17554 innerDimension -= offset; 17555 start = firstTickValue; 17556 } 17557 if (value !== 0) { 17558 offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start)); 17559 } 17560 pixel += sign * offset; 17561 } 17562 return pixel; 17563 }, 17564 getValueForPixel: function(pixel) { 17565 var me = this; 17566 var reverse = me.options.ticks.reverse; 17567 var log10 = helpers.log10; 17568 var firstTickValue = me._getFirstTickValue(me.minNotZero); 17569 var innerDimension, start, end, value; 17570 17571 if (reverse) { 17572 start = me.end; 17573 end = me.start; 17574 } else { 17575 start = me.start; 17576 end = me.end; 17577 } 17578 if (me.isHorizontal()) { 17579 innerDimension = me.width; 17580 value = reverse ? me.right - pixel : pixel - me.left; 17581 } else { 17582 innerDimension = me.height; 17583 value = reverse ? pixel - me.top : me.bottom - pixel; 17584 } 17585 if (value !== start) { 17586 if (start === 0) { // include zero tick 17587 var offset = helpers.getValueOrDefault( 17588 me.options.ticks.fontSize, 17589 Chart.defaults.global.defaultFontSize 17590 ); 17591 value -= offset; 17592 innerDimension -= offset; 17593 start = firstTickValue; 17594 } 17595 value *= log10(end) - log10(start); 17596 value /= innerDimension; 17597 value = Math.pow(10, log10(start) + value); 17598 } 17599 return value; 17600 } 17601 }); 17602 Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); 17603 17604}; 17605 17606},{"34":34,"45":45}],57:[function(require,module,exports){ 17607'use strict'; 17608 17609var defaults = require(25); 17610var helpers = require(45); 17611var Ticks = require(34); 17612 17613module.exports = function(Chart) { 17614 17615 var globalDefaults = defaults.global; 17616 17617 var defaultConfig = { 17618 display: true, 17619 17620 // Boolean - Whether to animate scaling the chart from the centre 17621 animate: true, 17622 position: 'chartArea', 17623 17624 angleLines: { 17625 display: true,
17626 color: 'rgba(0, 0, 0, 0.1)', 17627 lineWidth: 1 17628 }, 17629 17630 gridLines: { 17631 circular: false 17632 }, 17633 17634 // label settings 17635 ticks: { 17636 // Boolean - Show a backdrop to the scale label 17637 showLabelBackdrop: true, 17638 17639 // String - The colour of the label backdrop 17640 backdropColor: 'rgba(255,255,255,0.75)', 17641 17642 // Number - The backdrop padding above & below the label in pixels 17643 backdropPaddingY: 2, 17644 17645 // Number - The backdrop padding to the side of the label in pixels 17646 backdropPaddingX: 2, 17647 17648 callback: Ticks.formatters.linear 17649 }, 17650 17651 pointLabels: { 17652 // Boolean - if true, show point labels 17653 display: true, 17654 17655 // Number - Point label font size in pixels 17656 fontSize: 10, 17657 17658 // Function - Used to convert point labels 17659 callback: function(label) { 17660 return label; 17661 } 17662 } 17663 }; 17664 17665 function getValueCount(scale) { 17666 var opts = scale.options; 17667 return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; 17668 } 17669 17670 function getPointLabelFontOptions(scale) { 17671 var pointLabelOptions = scale.options.pointLabels; 17672 var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); 17673 var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); 17674 var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); 17675 var font = helpers.fontString(fontSize, fontStyle, fontFamily); 17676 17677 return { 17678 size: fontSize, 17679 style: fontStyle, 17680 family: fontFamily, 17681 font: font 17682 }; 17683 } 17684 17685 function measureLabelSize(ctx, fontSize, label) { 17686 if (helpers.isArray(label)) { 17687 return { 17688 w: helpers.longestText(ctx, ctx.font, label), 17689 h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) 17690 }; 17691 } 17692 17693 return { 17694 w: ctx.measureText(label).width, 17695 h: fontSize 17696 }; 17697 } 17698 17699 function determineLimits(angle, pos, size, min, max) { 17700 if (angle === min || angle === max) { 17701 return { 17702 start: pos - (size / 2), 17703 end: pos + (size / 2) 17704 }; 17705 } else if (angle < min || angle > max) { 17706 return { 17707 start: pos - size - 5, 17708 end: pos 17709 }; 17710 } 17711 17712 return { 17713 start: pos, 17714 end: pos + size + 5 17715 }; 17716 } 17717 17718 /** 17719 * Helper function to fit a radial linear scale with point labels 17720 */ 17721 function fitWithPointLabels(scale) { 17722 /* 17723 * Right, this is really confusing and there is a lot of maths going on here 17724 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 17725 * 17726 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif 17727 * 17728 * Solution: 17729 * 17730 * We assume the radius of the polygon is half the size of the canvas at first 17731 * at each index we check if the text overlaps. 17732 * 17733 * Where it does, we store that angle and that index. 17734 * 17735 * After finding the largest index and angle we calculate how much we need to remove 17736 * from the shape radius to move the point inwards by that x. 17737 * 17738 * We average the left and right distances to get the maximum shape radius that can fit in the box 17739 * along with labels. 17740 * 17741 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion 17742 * on each side, removing that from the size, halving it and adding the left x protrusion width. 17743 * 17744 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels 17745 * and position it in the most space efficient manner 17746 * 17747 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif 17748 */ 17749 17750 var plFont = getPointLabelFontOptions(scale); 17751 17752 // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. 17753 // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points 17754 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); 17755 var furthestLimits = { 17756 r: scale.width, 17757 l: 0, 17758 t: scale.height, 17759 b: 0 17760 }; 17761 var furthestAngles = {}; 17762 var i, textSize, pointPosition; 17763 17764 scale.ctx.font = plFont.font; 17765 scale._pointLabelSizes = []; 17766 17767 var valueCount = getValueCount(scale); 17768 for (i = 0; i < valueCount; i++) { 17769 pointPosition = scale.getPointPosition(i, largestPossibleRadius); 17770 textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); 17771 scale._pointLabelSizes[i] = textSize; 17772 17773 // Add quarter circle to make degree 0 mean top of circle 17774 var angleRadians = scale.getIndexAngle(i); 17775 var angle = helpers.toDegrees(angleRadians) % 360; 17776 var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); 17777 var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); 17778 17779 if (hLimits.start < furthestLimits.l) { 17780 furthestLimits.l = hLimits.start; 17781 furthestAngles.l = angleRadians; 17782 } 17783 17784 if (hLimits.end > furthestLimits.r) { 17785 furthestLimits.r = hLimits.end; 17786 furthestAngles.r = angleRadians; 17787 } 17788 17789 if (vLimits.start < furthestLimits.t) { 17790 furthestLimits.t = vLimits.start; 17791 furthestAngles.t = angleRadians; 17792 } 17793 17794 if (vLimits.end > furthestLimits.b) { 17795 furthestLimits.b = vLimits.end; 17796 furthestAngles.b = angleRadians; 17797 } 17798 } 17799 17800 scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); 17801 } 17802 17803 /** 17804 * Helper function to fit a radial linear scale with no point labels 17805 */ 17806 function fit(scale) { 17807 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); 17808 scale.drawingArea = Math.round(largestPossibleRadius); 17809 scale.setCenterPoint(0, 0, 0, 0); 17810 } 17811 17812 function getTextAlignForAngle(angle) { 17813 if (angle === 0 || angle === 180) { 17814 return 'center'; 17815 } else if (angle < 180) { 17816 return 'left'; 17817 } 17818 17819 return 'right'; 17820 } 17821 17822 function fillText(ctx, text, position, fontSize) { 17823 if (helpers.isArray(text)) { 17824 var y = position.y; 17825 var spacing = 1.5 * fontSize; 17826 17827 for (var i = 0; i < text.length; ++i) { 17828 ctx.fillText(text[i], position.x, y); 17829 y += spacing; 17830 } 17831 } else { 17832 ctx.fillText(text, position.x, position.y); 17833 } 17834 } 17835 17836 function adjustPointPositionForLabelHeight(angle, textSize, position) { 17837 if (angle === 90 || angle === 270) { 17838 position.y -= (textSize.h / 2); 17839 } else if (angle > 270 || angle < 90) { 17840 position.y -= textSize.h; 17841 } 17842 } 17843 17844 function drawPointLabels(scale) { 17845 var ctx = scale.ctx; 17846 var opts = scale.options; 17847 var angleLineOpts = opts.angleLines; 17848 var pointLabelOpts = opts.pointLabels;
17849 17850 ctx.lineWidth = angleLineOpts.lineWidth; 17851 ctx.strokeStyle = angleLineOpts.color; 17852 17853 var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); 17854 17855 // Point Label Font 17856 var plFont = getPointLabelFontOptions(scale); 17857 17858 ctx.textBaseline = 'top'; 17859 17860 for (var i = getValueCount(scale) - 1; i >= 0; i--) { 17861 if (angleLineOpts.display) { 17862 var outerPosition = scale.getPointPosition(i, outerDistance); 17863 ctx.beginPath(); 17864 ctx.moveTo(scale.xCenter, scale.yCenter); 17865 ctx.lineTo(outerPosition.x, outerPosition.y); 17866 ctx.stroke(); 17867 ctx.closePath(); 17868 } 17869 17870 if (pointLabelOpts.display) { 17871 // Extra 3px out for some label spacing 17872 var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); 17873 17874 // Keep this in loop since we may support array properties here 17875 var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor); 17876 ctx.font = plFont.font; 17877 ctx.fillStyle = pointLabelFontColor; 17878 17879 var angleRadians = scale.getIndexAngle(i); 17880 var angle = helpers.toDegrees(angleRadians); 17881 ctx.textAlign = getTextAlignForAngle(angle); 17882 adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); 17883 fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); 17884 } 17885 } 17886 } 17887 17888 function drawRadiusLine(scale, gridLineOpts, radius, index) { 17889 var ctx = scale.ctx; 17890 ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); 17891 ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); 17892 17893 if (scale.options.gridLines.circular) { 17894 // Draw circular arcs between the points 17895 ctx.beginPath(); 17896 ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); 17897 ctx.closePath(); 17898 ctx.stroke(); 17899 } else { 17900 // Draw straight lines connecting each index 17901 var valueCount = getValueCount(scale); 17902 17903 if (valueCount === 0) { 17904 return; 17905 } 17906 17907 ctx.beginPath(); 17908 var pointPosition = scale.getPointPosition(0, radius); 17909 ctx.moveTo(pointPosition.x, pointPosition.y); 17910 17911 for (var i = 1; i < valueCount; i++) { 17912 pointPosition = scale.getPointPosition(i, radius); 17913 ctx.lineTo(pointPosition.x, pointPosition.y); 17914 } 17915 17916 ctx.closePath(); 17917 ctx.stroke(); 17918 } 17919 } 17920 17921 function numberOrZero(param) { 17922 return helpers.isNumber(param) ? param : 0; 17923 } 17924 17925 var LinearRadialScale = Chart.LinearScaleBase.extend({ 17926 setDimensions: function() { 17927 var me = this; 17928 var opts = me.options; 17929 var tickOpts = opts.ticks; 17930 // Set the unconstrained dimension before label rotation 17931 me.width = me.maxWidth; 17932 me.height = me.maxHeight; 17933 me.xCenter = Math.round(me.width / 2); 17934 me.yCenter = Math.round(me.height / 2); 17935
17936 var minSize = helpers.min([me.height, me.width]); 17937 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 17938 me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); 17939 }, 17940 determineDataLimits: function() { 17941 var me = this; 17942 var chart = me.chart; 17943 var min = Number.POSITIVE_INFINITY; 17944 var max = Number.NEGATIVE_INFINITY; 17945 17946 helpers.each(chart.data.datasets, function(dataset, datasetIndex) { 17947 if (chart.isDatasetVisible(datasetIndex)) { 17948 var meta = chart.getDatasetMeta(datasetIndex); 17949 17950 helpers.each(dataset.data, function(rawValue, index) { 17951 var value = +me.getRightValue(rawValue); 17952 if (isNaN(value) || meta.data[index].hidden) { 17953 return; 17954 } 17955 17956 min = Math.min(value, min); 17957 max = Math.max(value, max); 17958 }); 17959 } 17960 }); 17961 17962 me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); 17963 me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); 17964 17965 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero 17966 me.handleTickRangeOptions(); 17967 }, 17968 getTickLimit: function() { 17969 var tickOpts = this.options.ticks; 17970 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 17971 return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); 17972 }, 17973 convertTicksToLabels: function() { 17974 var me = this; 17975 17976 Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); 17977 17978 // Point labels 17979 me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); 17980 }, 17981 getLabelForIndex: function(index, datasetIndex) { 17982 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17983 }, 17984 fit: function() { 17985 if (this.options.pointLabels.display) { 17986 fitWithPointLabels(this); 17987 } else { 17988 fit(this); 17989 } 17990 }, 17991 /** 17992 * Set radius reductions and determine new radius and center point 17993 * @private 17994 */ 17995 setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { 17996 var me = this; 17997 var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); 17998 var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); 17999 var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); 18000 var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); 18001 18002 radiusReductionLeft = numberOrZero(radiusReductionLeft); 18003 radiusReductionRight = numberOrZero(radiusReductionRight); 18004 radiusReductionTop = numberOrZero(radiusReductionTop); 18005 radiusReductionBottom = numberOrZero(radiusReductionBottom); 18006 18007 me.drawingArea = Math.min( 18008 Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), 18009 Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); 18010 me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); 18011 }, 18012 setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { 18013 var me = this; 18014 var maxRight = me.width - rightMovement - me.drawingArea; 18015 var maxLeft = leftMovement + me.drawingArea; 18016 var maxTop = topMovement + me.drawingArea; 18017 var maxBottom = me.height - bottomMovement - me.drawingArea; 18018 18019 me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); 18020 me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); 18021 }, 18022 18023 getIndexAngle: function(index) { 18024 var angleMultiplier = (Math.PI * 2) / getValueCount(this); 18025 var startAngle = this.chart.options && this.chart.options.startAngle ? 18026 this.chart.options.startAngle : 18027 0; 18028 18029 var startAngleRadians = startAngle * Math.PI * 2 / 360; 18030 18031 // Start from the top instead of right, so remove a quarter of the circle 18032 return index * angleMultiplier + startAngleRadians; 18033 }, 18034 getDistanceFromCenterForValue: function(value) { 18035 var me = this; 18036 18037 if (value === null) { 18038 return 0; // null always in center 18039 } 18040 18041 // Take into account half font size + the yPadding of the top value 18042 var scalingFactor = me.drawingArea / (me.max - me.min); 18043 if (me.options.ticks.reverse) { 18044 return (me.max - value) * scalingFactor; 18045 } 18046 return (value - me.min) * scalingFactor; 18047 }, 18048 getPointPosition: function(index, distanceFromCenter) { 18049 var me = this; 18050 var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); 18051 return { 18052 x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, 18053 y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter 18054 }; 18055 }, 18056 getPointPositionForValue: function(index, value) { 18057 return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); 18058 }, 18059 18060 getBasePosition: function() { 18061 var me = this; 18062 var min = me.min; 18063 var max = me.max; 18064 18065 return me.getPointPositionForValue(0, 18066 me.beginAtZero ? 0 : 18067 min < 0 && max < 0 ? max : 18068 min > 0 && max > 0 ? min : 18069 0); 18070 }, 18071 18072 draw: function() { 18073 var me = this; 18074 var opts = me.options; 18075 var gridLineOpts = opts.gridLines; 18076 var tickOpts = opts.ticks; 18077 var valueOrDefault = helpers.valueOrDefault; 18078 18079 if (opts.display) { 18080 var ctx = me.ctx; 18081 var startAngle = this.getIndexAngle(0); 18082 18083 // Tick Font
18084 var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 18085 var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); 18086 var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); 18087 var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); 18088 18089 helpers.each(me.ticks, function(label, index) { 18090 // Don't draw a centre value (if it is minimum) 18091 if (index > 0 || tickOpts.reverse) { 18092 var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); 18093 18094 // Draw circular lines around the scale 18095 if (gridLineOpts.display && index !== 0) { 18096 drawRadiusLine(me, gridLineOpts, yCenterOffset, index); 18097 } 18098 18099 if (tickOpts.display) { 18100 var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); 18101 ctx.font = tickLabelFont; 18102 18103 ctx.save(); 18104 ctx.translate(me.xCenter, me.yCenter); 18105 ctx.rotate(startAngle); 18106 18107 if (tickOpts.showLabelBackdrop) { 18108 var labelWidth = ctx.measureText(label).width; 18109 ctx.fillStyle = tickOpts.backdropColor; 18110 ctx.fillRect( 18111 -labelWidth / 2 - tickOpts.backdropPaddingX, 18112 -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, 18113 labelWidth + tickOpts.backdropPaddingX * 2, 18114 tickFontSize + tickOpts.backdropPaddingY * 2 18115 ); 18116 } 18117 18118 ctx.textAlign = 'center'; 18119 ctx.textBaseline = 'middle'; 18120 ctx.fillStyle = tickFontColor; 18121 ctx.fillText(label, 0, -yCenterOffset); 18122 ctx.restore(); 18123 } 18124 } 18125 }); 18126 18127 if (opts.angleLines.display || opts.pointLabels.display) { 18128 drawPointLabels(me); 18129 } 18130 } 18131 } 18132 }); 18133 Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); 18134 18135}; 18136 18137},{"25":25,"34":34,"45":45}],58:[function(require,module,exports){ 18138/* global window: false */ 18139'use strict'; 18140 18141var moment = require(6); 18142moment = typeof moment === 'function' ? moment : window.moment; 18143 18144var defaults = require(25); 18145var helpers = require(45); 18146 18147// Integer constants are from the ES6 spec. 18148var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; 18149var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; 18150 18151var INTERVALS = { 18152 millisecond: { 18153 common: true, 18154 size: 1, 18155 steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] 18156 }, 18157 second: { 18158 common: true, 18159 size: 1000, 18160 steps: [1, 2, 5, 10, 30] 18161 }, 18162 minute: { 18163 common: true, 18164 size: 60000, 18165 steps: [1, 2, 5, 10, 30] 18166 }, 18167 hour: { 18168 common: true, 18169 size: 3600000, 18170 steps: [1, 2, 3, 6, 12] 18171 }, 18172 day: { 18173 common: true, 18174 size: 86400000, 18175 steps: [1, 2, 5] 18176 }, 18177 week: { 18178 common: false, 18179 size: 604800000, 18180 steps: [1, 2, 3, 4] 18181 }, 18182 month: { 18183 common: true, 18184 size: 2.628e9, 18185 steps: [1, 2, 3] 18186 }, 18187 quarter: { 18188 common: false, 18189 size: 7.884e9, 18190 steps: [1, 2, 3, 4] 18191 }, 18192 year: { 18193 common: true, 18194 size: 3.154e10 18195 } 18196}; 18197 18198var UNITS = Object.keys(INTERVALS); 18199 18200function sorter(a, b) { 18201 return a - b; 18202} 18203 18204function arrayUnique(items) { 18205 var hash = {}; 18206 var out = []; 18207 var i, ilen, item; 18208 18209 for (i = 0, ilen = items.length; i < ilen; ++i) { 18210 item = items[i]; 18211 if (!hash[item]) { 18212 hash[item] = true; 18213 out.push(item); 18214 } 18215 } 18216 18217 return out; 18218} 18219 18220/** 18221 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position 18222 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is 18223 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other 18224 * extremity (left + width or top + height). Note that it would be more optimized to directly 18225 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need 18226 * to create the lookup table. The table ALWAYS contains at least two items: min and max. 18227 * 18228 * @param {Number[]} timestamps - timestamps sorted from lowest to highest. 18229 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min 18230 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. 18231 * If 'series', timestamps will be positioned at the same distance from each other. In this 18232 * case, only timestamps that break the time linearity are registered, meaning that in the 18233 * best case, all timestamps are linear, the table contains only min and max. 18234 */ 18235function buildLookupTable(timestamps, min, max, distribution) { 18236 if (distribution === 'linear' || !timestamps.length) { 18237 return [ 18238 {time: min, pos: 0}, 18239 {time: max, pos: 1} 18240 ]; 18241 } 18242 18243 var table = []; 18244 var items = [min]; 18245 var i, ilen, prev, curr, next; 18246 18247 for (i = 0, ilen = timestamps.length; i < ilen; ++i) { 18248 curr = timestamps[i]; 18249 if (curr > min && curr < max) { 18250 items.push(curr); 18251 } 18252 } 18253 18254 items.push(max); 18255 18256 for (i = 0, ilen = items.length; i < ilen; ++i) { 18257 next = items[i + 1];
18258 prev = items[i - 1]; 18259 curr = items[i]; 18260 18261 // only add points that breaks the scale linearity 18262 if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { 18263 table.push({time: curr, pos: i / (ilen - 1)}); 18264 } 18265 } 18266 18267 return table; 18268} 18269 18270// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ 18271function lookup(table, key, value) { 18272 var lo = 0; 18273 var hi = table.length - 1; 18274 var mid, i0, i1; 18275 18276 while (lo >= 0 && lo <= hi) { 18277 mid = (lo + hi) >> 1; 18278 i0 = table[mid - 1] || null; 18279 i1 = table[mid]; 18280 18281 if (!i0) { 18282 // given value is outside table (before first item) 18283 return {lo: null, hi: i1}; 18284 } else if (i1[key] < value) { 18285 lo = mid + 1; 18286 } else if (i0[key] > value) { 18287 hi = mid - 1; 18288 } else { 18289 return {lo: i0, hi: i1}; 18290 } 18291 } 18292 18293 // given value is outside table (after last item) 18294 return {lo: i1, hi: null}; 18295} 18296 18297/** 18298 * Linearly interpolates the given source `value` using the table items `skey` values and 18299 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') 18300 * returns the position for a timestamp equal to 42. If value is out of bounds, values at 18301 * index [0, 1] or [n - 1, n] are used for the interpolation. 18302 */ 18303function interpolate(table, skey, sval, tkey) { 18304 var range = lookup(table, skey, sval); 18305 18306 // Note: the lookup table ALWAYS contains at least 2 items (min and max) 18307 var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; 18308 var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; 18309 18310 var span = next[skey] - prev[skey]; 18311 var ratio = span ? (sval - prev[skey]) / span : 0; 18312 var offset = (next[tkey] - prev[tkey]) * ratio; 18313 18314 return prev[tkey] + offset; 18315} 18316 18317/** 18318 * Convert the given value to a moment object using the given time options. 18319 * @see http://momentjs.com/docs/#/parsing/ 18320 */ 18321function momentify(value, options) { 18322 var parser = options.parser; 18323 var format = options.parser || options.format; 18324 18325 if (typeof parser === 'function') { 18326 return parser(value); 18327 } 18328 18329 if (typeof value === 'string' && typeof format === 'string') { 18330 return moment(value, format); 18331 } 18332 18333 if (!(value instanceof moment)) { 18334 value = moment(value); 18335 } 18336 18337 if (value.isValid()) { 18338 return value; 18339 } 18340 18341 // Labels are in an incompatible moment format and no `parser` has been provided. 18342 // The user might still use the deprecated `format` option to convert his inputs. 18343 if (typeof format === 'function') { 18344 return format(value); 18345 } 18346 18347 return value; 18348} 18349 18350function parse(input, scale) { 18351 if (helpers.isNullOrUndef(input)) { 18352 return null; 18353 } 18354 18355 var options = scale.options.time; 18356 var value = momentify(scale.getRightValue(input), options); 18357 if (!value.isValid()) { 18358 return null; 18359 } 18360 18361 if (options.round) { 18362 value.startOf(options.round); 18363 } 18364 18365 return value.valueOf(); 18366} 18367 18368/** 18369 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks 18370 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. 18371 */ 18372function determineStepSize(min, max, unit, capacity) { 18373 var range = max - min; 18374 var interval = INTERVALS[unit]; 18375 var milliseconds = interval.size; 18376 var steps = interval.steps; 18377 var i, ilen, factor; 18378 18379 if (!steps) { 18380 return Math.ceil(range / (capacity * milliseconds)); 18381 } 18382 18383 for (i = 0, ilen = steps.length; i < ilen; ++i) { 18384 factor = steps[i]; 18385 if (Math.ceil(range / (milliseconds * factor)) <= capacity) { 18386 break; 18387 } 18388 } 18389 18390 return factor; 18391} 18392 18393/** 18394 * Figures out what unit results in an appropriate number of auto-generated ticks 18395 */ 18396function determineUnitForAutoTicks(minUnit, min, max, capacity) { 18397 var ilen = UNITS.length; 18398 var i, interval, factor; 18399 18400 for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { 18401 interval = INTERVALS[UNITS[i]]; 18402 factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; 18403 18404 if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { 18405 return UNITS[i]; 18406 } 18407 } 18408 18409 return UNITS[ilen - 1]; 18410} 18411 18412/** 18413 * Figures out what unit to format a set of ticks with 18414 */ 18415function determineUnitForFormatting(ticks, minUnit, min, max) { 18416 var duration = moment.duration(moment(max).diff(moment(min))); 18417 var ilen = UNITS.length; 18418 var i, unit; 18419 18420 for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { 18421 unit = UNITS[i]; 18422 if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) { 18423 return unit; 18424 } 18425 } 18426 18427 return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; 18428} 18429 18430function determineMajorUnit(unit) { 18431 for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { 18432 if (INTERVALS[UNITS[i]].common) { 18433 return UNITS[i]; 18434 } 18435 } 18436} 18437 18438/** 18439 * Generates a maximum of `capacity` timestamps between min and max, rounded to the 18440 * `minor` unit, aligned on the `major` unit and using the given scale time `options`. 18441 * Important: this method can return ticks outside the min and max range, it's the 18442 * responsibility of the calling code to clamp values if needed. 18443 */ 18444function generate(min, max, capacity, options) { 18445 var timeOpts = options.time; 18446 var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); 18447 var major = determineMajorUnit(minor); 18448 var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); 18449 var weekday = minor === 'week' ? timeOpts.isoWeekday : false; 18450 var majorTicksEnabled = options.ticks.major.enabled; 18451 var interval = INTERVALS[minor]; 18452 var first = moment(min); 18453 var last = moment(max); 18454 var ticks = []; 18455 var time; 18456 18457 if (!stepSize) { 18458 stepSize = determineStepSize(min, max, minor, capacity); 18459 } 18460 18461 // For 'week' unit, handle the first day of week option 18462 if (weekday) { 18463 first = first.isoWeekday(weekday); 18464 last = last.isoWeekday(weekday); 18465 } 18466 18467 // Align first/last ticks on unit 18468 first = first.startOf(weekday ? 'day' : minor); 18469 last = last.startOf(weekday ? 'day' : minor); 18470 18471 // Make sure that the last tick include max 18472 if (last < max) { 18473 last.add(1, minor); 18474 } 18475 18476 time = moment(first); 18477 18478 if (majorTicksEnabled && major && !weekday && !timeOpts.round) { 18479 // Align the first tick on the previous `minor` unit aligned on the `major` unit: 18480 // we first aligned time on the previous `major` unit then add the number of full 18481 // stepSize there is between first and the previous major time. 18482 time.startOf(major); 18483 time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); 18484 } 18485 18486 for (; time < last; time.add(stepSize, minor)) { 18487 ticks.push(+time); 18488 } 18489 18490 ticks.push(+time); 18491 18492 return ticks; 18493} 18494 18495/** 18496 * Returns the right and left offsets from edges in the form of {left, right}. 18497 * Offsets are added when the `offset` option is true. 18498 */ 18499function computeOffsets(table, ticks, min, max, options) { 18500 var left = 0; 18501 var right = 0; 18502 var upper, lower; 18503 18504 if (options.offset && ticks.length) { 18505 if (!options.time.min) { 18506 upper = ticks.length > 1 ? ticks[1] : max; 18507 lower = ticks[0]; 18508 left = ( 18509 interpolate(table, 'time', upper, 'pos') - 18510 interpolate(table, 'time', lower, 'pos') 18511 ) / 2; 18512 } 18513 if (!options.time.max) { 18514 upper = ticks[ticks.length - 1]; 18515 lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; 18516 right = ( 18517 interpolate(table, 'time', upper, 'pos') - 18518 interpolate(table, 'time', lower, 'pos') 18519 ) / 2; 18520 } 18521 } 18522 18523 return {left: left, right: right}; 18524} 18525 18526function ticksFromTimestamps(values, majorUnit) { 18527 var ticks = []; 18528 var i, ilen, value, major; 18529 18530 for (i = 0, ilen = values.length; i < ilen; ++i) { 18531 value = values[i]; 18532 major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; 18533 18534 ticks.push({ 18535 value: value, 18536 major: major 18537 }); 18538 } 18539 18540 return ticks; 18541} 18542 18543function determineLabelFormat(data, timeOpts) { 18544 var i, momentDate, hasTime; 18545 var ilen = data.length; 18546 18547 // find the label with the most parts (milliseconds, minutes, etc.) 18548 // format all labels with the same level of detail as the most specific label 18549 for (i = 0; i < ilen; i++) { 18550 momentDate = momentify(data[i], timeOpts); 18551 if (momentDate.millisecond() !== 0) { 18552 return 'MMM D, YYYY h:mm:ss.SSS a'; 18553 } 18554 if (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) { 18555 hasTime = true; 18556 } 18557 } 18558 if (hasTime) { 18559 return 'MMM D, YYYY h:mm:ss a'; 18560 } 18561 return 'MMM D, YYYY'; 18562} 18563 18564module.exports = function(Chart) { 18565 18566 var defaultConfig = { 18567 position: 'bottom', 18568 18569 /** 18570 * Data distribution along the scale: 18571 * - 'linear': data are spread according to their time (distances can vary), 18572 * - 'series': data are spread at the same distance from each other. 18573 * @see https://github.com/chartjs/Chart.js/pull/4507 18574 * @since 2.7.0 18575 */ 18576 distribution: 'linear', 18577 18578 /** 18579 * Scale boundary strategy (bypassed by min/max time options) 18580 * - `data`: make sure data are fully visible, ticks outside are removed 18581 * - `ticks`: make sure ticks are fully visible, data outside are truncated 18582 * @see https://github.com/chartjs/Chart.js/pull/4556 18583 * @since 2.7.0 18584 */ 18585 bounds: 'data', 18586 18587 time: { 18588 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 18589 format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ 18590 unit: false, // false == automatic or override with week, month, year, etc.
18591 round: false, // none, or override with week, month, year, etc. 18592 displayFormat: false, // DEPRECATED 18593 isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ 18594 minUnit: 'millisecond', 18595 18596 // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ 18597 displayFormats: { 18598 millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, 18599 second: 'h:mm:ss a', // 11:20:01 AM 18600 minute: 'h:mm a', // 11:20 AM 18601 hour: 'hA', // 5PM 18602 day: 'MMM D', // Sep 4 18603 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? 18604 month: 'MMM YYYY', // Sept 2015 18605 quarter: '[Q]Q - YYYY', // Q3 18606 year: 'YYYY' // 2015 18607 }, 18608 }, 18609 ticks: { 18610 autoSkip: false, 18611 18612 /** 18613 * Ticks generation input values: 18614 * - 'auto': generates "optimal" ticks based on scale size and time options. 18615 * - 'data': generates ticks from data (including labels from data {t|x|y} objects). 18616 * - 'labels': generates ticks from user given `data.labels` values ONLY. 18617 * @see https://github.com/chartjs/Chart.js/pull/4507 18618 * @since 2.7.0 18619 */ 18620 source: 'auto', 18621 18622 major: { 18623 enabled: false 18624 } 18625 } 18626 }; 18627 18628 var TimeScale = Chart.Scale.extend({ 18629 initialize: function() { 18630 if (!moment) { 18631 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'); 18632 } 18633 18634 this.mergeTicksOptions(); 18635 18636 Chart.Scale.prototype.initialize.call(this); 18637 }, 18638 18639 update: function() { 18640 var me = this; 18641 var options = me.options; 18642 18643 // DEPRECATIONS: output a message only one time per update 18644 if (options.time && options.time.format) { 18645 console.warn('options.time.format is deprecated and replaced by options.time.parser.'); 18646 } 18647 18648 return Chart.Scale.prototype.update.apply(me, arguments); 18649 }, 18650 18651 /** 18652 * Allows data to be referenced via 't' attribute 18653 */ 18654 getRightValue: function(rawValue) { 18655 if (rawValue && rawValue.t !== undefined) { 18656 rawValue = rawValue.t; 18657 } 18658 return Chart.Scale.prototype.getRightValue.call(this, rawValue); 18659 }, 18660 18661 determineDataLimits: function() { 18662 var me = this; 18663 var chart = me.chart; 18664 var timeOpts = me.options.time; 18665 var unit = timeOpts.unit || 'day'; 18666 var min = MAX_INTEGER; 18667 var max = MIN_INTEGER; 18668 var timestamps = []; 18669 var datasets = []; 18670 var labels = []; 18671 var i, j, ilen, jlen, data, timestamp; 18672 18673 // Convert labels to timestamps 18674 for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { 18675 labels.push(parse(chart.data.labels[i], me)); 18676 } 18677 18678 // Convert data to timestamps 18679 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 18680 if (chart.isDatasetVisible(i)) { 18681 data = chart.data.datasets[i].data; 18682 18683 // Let's consider that all data have the same format. 18684 if (helpers.isObject(data[0])) { 18685 datasets[i] = []; 18686 18687 for (j = 0, jlen = data.length; j < jlen; ++j) { 18688 timestamp = parse(data[j], me); 18689 timestamps.push(timestamp); 18690 datasets[i][j] = timestamp; 18691 } 18692 } else { 18693 timestamps.push.apply(timestamps, labels); 18694 datasets[i] = labels.slice(0); 18695 } 18696 } else { 18697 datasets[i] = []; 18698 } 18699 } 18700 18701 if (labels.length) { 18702 // Sort labels **after** data have been converted 18703 labels = arrayUnique(labels).sort(sorter); 18704 min = Math.min(min, labels[0]); 18705 max = Math.max(max, labels[labels.length - 1]); 18706 } 18707 18708 if (timestamps.length) { 18709 timestamps = arrayUnique(timestamps).sort(sorter); 18710 min = Math.min(min, timestamps[0]); 18711 max = Math.max(max, timestamps[timestamps.length - 1]); 18712 } 18713 18714 min = parse(timeOpts.min, me) || min; 18715 max = parse(timeOpts.max, me) || max; 18716 18717 // In case there is no valid min/max, set limits based on unit time option 18718 min = min === MAX_INTEGER ? +moment().startOf(unit) : min; 18719 max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max; 18720 18721 // Make sure that max is strictly higher than min (required by the lookup table) 18722 me.min = Math.min(min, max); 18723 me.max = Math.max(min + 1, max); 18724 18725 // PRIVATE 18726 me._horizontal = me.isHorizontal(); 18727 me._table = []; 18728 me._timestamps = { 18729 data: timestamps, 18730 datasets: datasets, 18731 labels: labels 18732 }; 18733 }, 18734 18735 buildTicks: function() { 18736 var me = this; 18737 var min = me.min; 18738 var max = me.max; 18739 var options = me.options; 18740 var timeOpts = options.time; 18741 var timestamps = []; 18742 var ticks = []; 18743 var i, ilen, timestamp; 18744 18745 switch (options.ticks.source) { 18746 case 'data': 18747 timestamps = me._timestamps.data; 18748 break; 18749 case 'labels': 18750 timestamps = me._timestamps.labels; 18751 break; 18752 case 'auto': 18753 default: 18754 timestamps = generate(min, max, me.getLabelCapacity(min), options); 18755 } 18756 18757 if (options.bounds === 'ticks' && timestamps.length) { 18758 min = timestamps[0]; 18759 max = timestamps[timestamps.length - 1]; 18760 } 18761 18762 // Enforce limits with user min/max options 18763 min = parse(timeOpts.min, me) || min; 18764 max = parse(timeOpts.max, me) || max; 18765 18766 // Remove ticks outside the min/max range 18767 for (i = 0, ilen = timestamps.length; i < ilen; ++i) { 18768 timestamp = timestamps[i]; 18769 if (timestamp >= min && timestamp <= max) { 18770 ticks.push(timestamp); 18771 } 18772 } 18773 18774 me.min = min; 18775 me.max = max; 18776 18777 // PRIVATE 18778 me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); 18779 me._majorUnit = determineMajorUnit(me._unit); 18780 me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); 18781 me._offsets = computeOffsets(me._table, ticks, min, max, options); 18782 me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts); 18783 18784 return ticksFromTimestamps(ticks, me._majorUnit); 18785 }, 18786 18787 getLabelForIndex: function(index, datasetIndex) { 18788 var me = this; 18789 var data = me.chart.data; 18790 var timeOpts = me.options.time; 18791 var label = data.labels && index < data.labels.length ? data.labels[index] : ''; 18792 var value = data.datasets[datasetIndex].data[index]; 18793 18794 if (helpers.isObject(value)) { 18795 label = me.getRightValue(value); 18796 } 18797 if (timeOpts.tooltipFormat) { 18798 return momentify(label, timeOpts).format(timeOpts.tooltipFormat); 18799 } 18800 if (typeof label === 'string') { 18801 return label; 18802 } 18803 18804 return momentify(label, timeOpts).format(me._labelFormat); 18805 }, 18806 18807 /** 18808 * Function to format an individual tick mark 18809 * @private 18810 */ 18811 tickFormatFunction: function(tick, index, ticks, formatOverride) { 18812 var me = this; 18813 var options = me.options; 18814 var time = tick.valueOf(); 18815 var formats = options.time.displayFormats; 18816 var minorFormat = formats[me._unit]; 18817 var majorUnit = me._majorUnit; 18818 var majorFormat = formats[majorUnit]; 18819 var majorTime = tick.clone().startOf(majorUnit).valueOf(); 18820 var majorTickOpts = options.ticks.major; 18821 var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; 18822 var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat); 18823 var tickOpts = major ? majorTickOpts : options.ticks.minor; 18824 var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); 18825 18826 return formatter ? formatter(label, index, ticks) : label; 18827 }, 18828 18829 convertTicksToLabels: function(ticks) { 18830 var labels = []; 18831 var i, ilen; 18832 18833 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 18834 labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); 18835 } 18836 18837 return labels; 18838 }, 18839 18840 /** 18841 * @private 18842 */ 18843 getPixelForOffset: function(time) { 18844 var me = this; 18845 var size = me._horizontal ? me.width : me.height; 18846 var start = me._horizontal ? me.left : me.top; 18847 var pos = interpolate(me._table, 'time', time, 'pos'); 18848 18849 return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); 18850 }, 18851 18852 getPixelForValue: function(value, index, datasetIndex) { 18853 var me = this; 18854 var time = null; 18855 18856 if (index !== undefined && datasetIndex !== undefined) { 18857 time = me._timestamps.datasets[datasetIndex][index]; 18858 } 18859 18860 if (time === null) { 18861 time = parse(value, me); 18862 } 18863 18864 if (time !== null) { 18865 return me.getPixelForOffset(time); 18866 } 18867 }, 18868 18869 getPixelForTick: function(index) { 18870 var ticks = this.getTicks(); 18871 return index >= 0 && index < ticks.length ? 18872 this.getPixelForOffset(ticks[index].value) : 18873 null; 18874 }, 18875 18876 getValueForPixel: function(pixel) { 18877 var me = this; 18878 var size = me._horizontal ? me.width : me.height; 18879 var start = me._horizontal ? me.left : me.top; 18880 var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; 18881 var time = interpolate(me._table, 'pos', pos, 'time'); 18882 18883 return moment(time); 18884 }, 18885 18886 /** 18887 * Crude approximation of what the label width might be 18888 * @private 18889 */ 18890 getLabelWidth: function(label) { 18891 var me = this; 18892 var ticksOpts = me.options.ticks; 18893 var tickLabelWidth = me.ctx.measureText(label).width; 18894 var angle = helpers.toRadians(ticksOpts.maxRotation); 18895 var cosRotation = Math.cos(angle); 18896 var sinRotation = Math.sin(angle);
18897 var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); 18898 18899 return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); 18900 }, 18901 18902 /** 18903 * @private 18904 */ 18905 getLabelCapacity: function(exampleTime) { 18906 var me = this; 18907 18908 var formatOverride = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation 18909 18910 var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride); 18911 var tickLabelWidth = me.getLabelWidth(exampleLabel); 18912 var innerWidth = me.isHorizontal() ? me.width : me.height; 18913 18914 var capacity = Math.floor(innerWidth / tickLabelWidth); 18915 return capacity > 0 ? capacity : 1; 18916 } 18917 }); 18918 18919 Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); 18920}; 18921 18922},{"25":25,"45":45,"6":6}]},{},[7])(7) 18923});
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.