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: 14,231 bytes, lines 155-759
155 if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { 156 return rgbaString(rgba, alpha); 157 } 158 return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; 159} 160 161function rgbaString(rgba, alpha) { 162 if (alpha === undefined) { 163 alpha = (rgba[3] !== undefined ? rgba[3] : 1); 164 } 165 return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] 166 + ", " + alpha + ")"; 167} 168 169function percentString(rgba, alpha) { 170 if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { 171 return percentaString(rgba, alpha); 172 } 173 var r = Math.round(rgba[0]/255 * 100), 174 g = Math.round(rgba[1]/255 * 100), 175 b = Math.round(rgba[2]/255 * 100); 176 177 return "rgb(" + r + "%, " + g + "%, " + b + "%)"; 178} 179 180function percentaString(rgba, alpha) { 181 var r = Math.round(rgba[0]/255 * 100), 182 g = Math.round(rgba[1]/255 * 100), 183 b = Math.round(rgba[2]/255 * 100); 184 return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; 185} 186 187function hslString(hsla, alpha) { 188 if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { 189 return hslaString(hsla, alpha); 190 } 191 return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; 192} 193 194function hslaString(hsla, alpha) { 195 if (alpha === undefined) { 196 alpha = (hsla[3] !== undefined ? hsla[3] : 1); 197 } 198 return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " 199 + alpha + ")"; 200} 201 202// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax 203// (hwb have alpha optional & 1 is default value) 204function hwbString(hwb, alpha) { 205 if (alpha === undefined) { 206 alpha = (hwb[3] !== undefined ? hwb[3] : 1); 207 } 208 return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" 209 + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; 210} 211 212function keyword(rgb) { 213 return reverseNames[rgb.slice(0, 3)]; 214} 215 216// helpers 217function scale(num, min, max) { 218 return Math.min(Math.max(min, num), max); 219} 220 221function hexDouble(num) { 222 var str = num.toString(16).toUpperCase(); 223 return (str.length < 2) ? "0" + str : str; 224} 225 226 227//create a list of reverse color names 228var reverseNames = {}; 229for (var name in colorNames) { 230 reverseNames[colorNames[name]] = name; 231} 232 233},{"5":5}],2:[function(require,module,exports){ 234/* MIT license */ 235var convert = require(4); 236var string = require(1); 237 238var Color = function (obj) { 239 if (obj instanceof Color) { 240 return obj; 241 } 242 if (!(this instanceof Color)) { 243 return new Color(obj); 244 } 245 246 this.valid = false; 247 this.values = { 248 rgb: [0, 0, 0], 249 hsl: [0, 0, 0], 250 hsv: [0, 0, 0], 251 hwb: [0, 0, 0], 252 cmyk: [0, 0, 0, 0], 253 alpha: 1 254 }; 255 256 // parse Color() argument 257 var vals; 258 if (typeof obj === 'string') { 259 vals = string.getRgba(obj); 260 if (vals) { 261 this.setValues('rgb', vals); 262 } else if (vals = string.getHsla(obj)) { 263 this.setValues('hsl', vals); 264 } else if (vals = string.getHwb(obj)) { 265 this.setValues('hwb', vals); 266 } 267 } else if (typeof obj === 'object') { 268 vals = obj; 269 if (vals.r !== undefined || vals.red !== undefined) { 270 this.setValues('rgb', vals); 271 } else if (vals.l !== undefined || vals.lightness !== undefined) { 272 this.setValues('hsl', vals); 273 } else if (vals.v !== undefined || vals.value !== undefined) { 274 this.setValues('hsv', vals); 275 } else if (vals.w !== undefined || vals.whiteness !== undefined) { 276 this.setValues('hwb', vals); 277 } else if (vals.c !== undefined || vals.cyan !== undefined) { 278 this.setValues('cmyk', vals); 279 } 280 } 281}; 282 283Color.prototype = { 284 isValid: function () { 285 return this.valid; 286 }, 287 rgb: function () { 288 return this.setSpace('rgb', arguments); 289 }, 290 hsl: function () { 291 return this.setSpace('hsl', arguments); 292 }, 293 hsv: function () { 294 return this.setSpace('hsv', arguments); 295 }, 296 hwb: function () { 297 return this.setSpace('hwb', arguments); 298 }, 299 cmyk: function () { 300 return this.setSpace('cmyk', arguments); 301 }, 302 303 rgbArray: function () { 304 return this.values.rgb; 305 }, 306 hslArray: function () { 307 return this.values.hsl; 308 }, 309 hsvArray: function () { 310 return this.values.hsv; 311 }, 312 hwbArray: function () { 313 var values = this.values; 314 if (values.alpha !== 1) { 315 return values.hwb.concat([values.alpha]); 316 } 317 return values.hwb; 318 }, 319 cmykArray: function () { 320 return this.values.cmyk; 321 }, 322 rgbaArray: function () { 323 var values = this.values; 324 return values.rgb.concat([values.alpha]); 325 }, 326 hslaArray: function () { 327 var values = this.values; 328 return values.hsl.concat([values.alpha]); 329 }, 330 alpha: function (val) { 331 if (val === undefined) { 332 return this.values.alpha; 333 } 334 this.setValues('alpha', val); 335 return this; 336 }, 337 338 red: function (val) { 339 return this.setChannel('rgb', 0, val); 340 }, 341 green: function (val) { 342 return this.setChannel('rgb', 1, val); 343 }, 344 blue: function (val) { 345 return this.setChannel('rgb', 2, val); 346 }, 347 hue: function (val) { 348 if (val) { 349 val %= 360; 350 val = val < 0 ? 360 + val : val; 351 } 352 return this.setChannel('hsl', 0, val); 353 }, 354 saturation: function (val) { 355 return this.setChannel('hsl', 1, val); 356 }, 357 lightness: function (val) { 358 return this.setChannel('hsl', 2, val); 359 }, 360 saturationv: function (val) { 361 return this.setChannel('hsv', 1, val); 362 }, 363 whiteness: function (val) { 364 return this.setChannel('hwb', 1, val); 365 }, 366 blackness: function (val) { 367 return this.setChannel('hwb', 2, val); 368 }, 369 value: function (val) { 370 return this.setChannel('hsv', 2, val); 371 }, 372 cyan: function (val) { 373 return this.setChannel('cmyk', 0, val); 374 }, 375 magenta: function (val) { 376 return this.setChannel('cmyk', 1, val); 377 }, 378 yellow: function (val) { 379 return this.setChannel('cmyk', 2, val); 380 }, 381 black: function (val) { 382 return this.setChannel('cmyk', 3, val); 383 }, 384 385 hexString: function () { 386 return string.hexString(this.values.rgb); 387 }, 388 rgbString: function () { 389 return string.rgbString(this.values.rgb, this.values.alpha); 390 }, 391 rgbaString: function () { 392 return string.rgbaString(this.values.rgb, this.values.alpha); 393 }, 394 percentString: function () { 395 return string.percentString(this.values.rgb, this.values.alpha); 396 }, 397 hslString: function () { 398 return string.hslString(this.values.hsl, this.values.alpha); 399 }, 400 hslaString: function () { 401 return string.hslaString(this.values.hsl, this.values.alpha); 402 }, 403 hwbString: function () { 404 return string.hwbString(this.values.hwb, this.values.alpha); 405 }, 406 keyword: function () { 407 return string.keyword(this.values.rgb, this.values.alpha); 408 }, 409 410 rgbNumber: function () { 411 var rgb = this.values.rgb; 412 return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; 413 }, 414 415 luminosity: function () { 416 // http://www.w3.org/TR/WCAG20/#relativeluminancedef 417 var rgb = this.values.rgb; 418 var lum = []; 419 for (var i = 0; i < rgb.length; i++) { 420 var chan = rgb[i] / 255; 421 lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4); 422 } 423 return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2]; 424 }, 425 426 contrast: function (color2) { 427 // http://www.w3.org/TR/WCAG20/#contrast-ratiodef 428 var lum1 = this.luminosity(); 429 var lum2 = color2.luminosity(); 430 if (lum1 > lum2) { 431 return (lum1 + 0.05) / (lum2 + 0.05); 432 } 433 return (lum2 + 0.05) / (lum1 + 0.05); 434 }, 435 436 level: function (color2) { 437 var contrastRatio = this.contrast(color2); 438 if (contrastRatio >= 7.1) { 439 return 'AAA'; 440 } 441 442 return (contrastRatio >= 4.5) ? 'AA' : ''; 443 }, 444 445 dark: function () { 446 // YIQ equation from http://24ways.org/2010/calculating-color-contrast 447 var rgb = this.values.rgb; 448 var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; 449 return yiq < 128; 450 }, 451 452 light: function () { 453 return !this.dark(); 454 }, 455 456 negate: function () { 457 var rgb = []; 458 for (var i = 0; i < 3; i++) { 459 rgb[i] = 255 - this.values.rgb[i]; 460 } 461 this.setValues('rgb', rgb); 462 return this; 463 }, 464 465 lighten: function (ratio) { 466 var hsl = this.values.hsl; 467 hsl[2] += hsl[2] * ratio; 468 this.setValues('hsl', hsl); 469 return this; 470 }, 471 472 darken: function (ratio) { 473 var hsl = this.values.hsl; 474 hsl[2] -= hsl[2] * ratio; 475 this.setValues('hsl', hsl); 476 return this; 477 }, 478 479 saturate: function (ratio) { 480 var hsl = this.values.hsl; 481 hsl[1] += hsl[1] * ratio; 482 this.setValues('hsl', hsl); 483 return this; 484 }, 485 486 desaturate: function (ratio) { 487 var hsl = this.values.hsl; 488 hsl[1] -= hsl[1] * ratio; 489 this.setValues('hsl', hsl); 490 return this; 491 }, 492 493 whiten: function (ratio) { 494 var hwb = this.values.hwb; 495 hwb[1] += hwb[1] * ratio; 496 this.setValues('hwb', hwb); 497 return this; 498 }, 499 500 blacken: function (ratio) { 501 var hwb = this.values.hwb; 502 hwb[2] += hwb[2] * ratio; 503 this.setValues('hwb', hwb); 504 return this; 505 }, 506 507 greyscale: function () { 508 var rgb = this.values.rgb; 509 // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale 510 var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; 511 this.setValues('rgb', [val, val, val]); 512 return this; 513 }, 514 515 clearer: function (ratio) { 516 var alpha = this.values.alpha; 517 this.setValues('alpha', alpha - (alpha * ratio)); 518 return this; 519 }, 520 521 opaquer: function (ratio) { 522 var alpha = this.values.alpha; 523 this.setValues('alpha', alpha + (alpha * ratio)); 524 return this; 525 }, 526 527 rotate: function (degrees) { 528 var hsl = this.values.hsl; 529 var hue = (hsl[0] + degrees) % 360; 530 hsl[0] = hue < 0 ? 360 + hue : hue; 531 this.setValues('hsl', hsl); 532 return this; 533 }, 534 535 /** 536 * Ported from sass implementation in C 537 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 538 */ 539 mix: function (mixinColor, weight) { 540 var color1 = this; 541 var color2 = mixinColor; 542 var p = weight === undefined ? 0.5 : weight; 543 544 var w = 2 * p - 1; 545 var a = color1.alpha() - color2.alpha(); 546 547 var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; 548 var w2 = 1 - w1; 549 550 return this 551 .rgb( 552 w1 * color1.red() + w2 * color2.red(), 553 w1 * color1.green() + w2 * color2.green(), 554 w1 * color1.blue() + w2 * color2.blue() 555 ) 556 .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); 557 }, 558 559 toJSON: function () { 560 return this.rgb(); 561 }, 562 563 clone: function () { 564 // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, 565 // making the final build way to big to embed in Chart.js. So let's do it manually, 566 // assuming that values to clone are 1 dimension arrays containing only numbers, 567 // except 'alpha' which is a number. 568 var result = new Color(); 569 var source = this.values; 570 var target = result.values; 571 var value, type; 572 573 for (var prop in source) { 574 if (source.hasOwnProperty(prop)) { 575 value = source[prop]; 576 type = ({}).toString.call(value); 577 if (type === '[object Array]') { 578 target[prop] = value.slice(0); 579 } else if (type === '[object Number]') { 580 target[prop] = value; 581 } else { 582 console.error('unexpected color value:', value); 583 } 584 } 585 } 586 587 return result; 588 } 589}; 590 591Color.prototype.spaces = { 592 rgb: ['red', 'green', 'blue'], 593 hsl: ['hue', 'saturation', 'lightness'], 594 hsv: ['hue', 'saturation', 'value'], 595 hwb: ['hue', 'whiteness', 'blackness'], 596 cmyk: ['cyan', 'magenta', 'yellow', 'black'] 597}; 598 599Color.prototype.maxes = { 600 rgb: [255, 255, 255], 601 hsl: [360, 100, 100], 602 hsv: [360, 100, 100], 603 hwb: [360, 100, 100], 604 cmyk: [100, 100, 100, 100] 605}; 606 607Color.prototype.getValues = function (space) { 608 var values = this.values; 609 var vals = {}; 610 611 for (var i = 0; i < space.length; i++) { 612 vals[space.charAt(i)] = values[space][i]; 613 } 614 615 if (values.alpha !== 1) { 616 vals.a = values.alpha; 617 } 618 619 // {r: 255, g: 255, b: 255, a: 0.4} 620 return vals; 621}; 622 623Color.prototype.setValues = function (space, vals) { 624 var values = this.values; 625 var spaces = this.spaces; 626 var maxes = this.maxes; 627 var alpha = 1; 628 var i; 629 630 this.valid = true; 631 632 if (space === 'alpha') { 633 alpha = vals; 634 } else if (vals.length) { 635 // [10, 10, 10] 636 values[space] = vals.slice(0, space.length); 637 alpha = vals[space.length]; 638 } else if (vals[space.charAt(0)] !== undefined) { 639 // {r: 10, g: 10, b: 10} 640 for (i = 0; i < space.length; i++) { 641 values[space][i] = vals[space.charAt(i)]; 642 } 643 644 alpha = vals.a; 645 } else if (vals[spaces[space][0]] !== undefined) { 646 // {red: 10, green: 10, blue: 10} 647 var chans = spaces[space]; 648 649 for (i = 0; i < space.length; i++) { 650 values[space][i] = vals[chans[i]]; 651 } 652 653 alpha = vals.alpha; 654 } 655 656 values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); 657 658 if (space === 'alpha') { 659 return false; 660 } 661 662 var capped; 663 664 // cap values of the space prior converting all values 665 for (i = 0; i < space.length; i++) { 666 capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); 667 values[space][i] = Math.round(capped); 668 } 669 670 // convert to all the other color spaces 671 for (var sname in spaces) { 672 if (sname !== space) { 673 values[sname] = convert[space][sname](values[space]); 674 } 675 } 676 677 return true; 678}; 679 680Color.prototype.setSpace = function (space, args) { 681 var vals = args[0]; 682 683 if (vals === undefined) { 684 // color.rgb() 685 return this.getValues(space); 686 } 687 688 // color.rgb(10, 10, 10) 689 if (typeof vals === 'number') { 690 vals = Array.prototype.slice.call(args); 691 } 692 693 this.setValues(space, vals); 694 return this; 695}; 696 697Color.prototype.setChannel = function (space, index, val) { 698 var svalues = this.values[space]; 699 if (val === undefined) { 700 // color.red() 701 return svalues[index]; 702 } else if (val === svalues[index]) { 703 // color.red(color.red()) 704 return this; 705 } 706 707 // color.red(100) 708 svalues[index] = val; 709 this.setValues(space, svalues); 710 711 return this; 712}; 713 714if (typeof window !== 'undefined') { 715 window.Color = Color; 716} 717 718module.exports = Color; 719 720},{"1":1,"4":4}],3:[function(require,module,exports){ 721/* MIT license */ 722 723module.exports = { 724 rgb2hsl: rgb2hsl, 725 rgb2hsv: rgb2hsv, 726 rgb2hwb: rgb2hwb, 727 rgb2cmyk: rgb2cmyk, 728 rgb2keyword: rgb2keyword, 729 rgb2xyz: rgb2xyz, 730 rgb2lab: rgb2lab, 731 rgb2lch: rgb2lch, 732 733 hsl2rgb: hsl2rgb, 734 hsl2hsv: hsl2hsv, 735 hsl2hwb: hsl2hwb, 736 hsl2cmyk: hsl2cmyk, 737 hsl2keyword: hsl2keyword, 738 739 hsv2rgb: hsv2rgb, 740 hsv2hsl: hsv2hsl, 741 hsv2hwb: hsv2hwb, 742 hsv2cmyk: hsv2cmyk, 743 hsv2keyword: hsv2keyword, 744 745 hwb2rgb: hwb2rgb, 746 hwb2hsl: hwb2hsl, 747 hwb2hsv: hwb2hsv, 748 hwb2cmyk: hwb2cmyk, 749 hwb2keyword: hwb2keyword, 750 751 cmyk2rgb: cmyk2rgb, 752 cmyk2hsl: cmyk2hsl, 753 cmyk2hsv: cmyk2hsv, 754 cmyk2hwb: cmyk2hwb, 755 cmyk2keyword: cmyk2keyword, 756 757 keyword2rgb: keyword2rgb, 758 keyword2hsl: keyword2hsl, 759 keyword2hsv: keyword2hsv,
760 keyword2hwb: keyword2hwb, 761 keyword2cmyk: keyword2cmyk, 762 keyword2lab: keyword2lab, 763 keyword2xyz: keyword2xyz, 764 765 xyz2rgb: xyz2rgb, 766 xyz2lab: xyz2lab, 767 xyz2lch: xyz2lch, 768 769 lab2xyz: lab2xyz, 770 lab2rgb: lab2rgb, 771 lab2lch: lab2lch, 772 773 lch2lab: lch2lab, 774 lch2xyz: lch2xyz, 775 lch2rgb: lch2rgb 776} 777 778 779function rgb2hsl(rgb) { 780 var r = rgb[0]/255, 781 g = rgb[1]/255, 782 b = rgb[2]/255, 783 min = Math.min(r, g, b), 784 max = Math.max(r, g, b), 785 delta = max - min, 786 h, s, l; 787 788 if (max == min) 789 h = 0; 790 else if (r == max) 791 h = (g - b) / delta; 792 else if (g == max) 793 h = 2 + (b - r) / delta; 794 else if (b == max) 795 h = 4 + (r - g)/ delta; 796 797 h = Math.min(h * 60, 360); 798 799 if (h < 0) 800 h += 360; 801 802 l = (min + max) / 2; 803 804 if (max == min) 805 s = 0; 806 else if (l <= 0.5) 807 s = delta / (max + min); 808 else 809 s = delta / (2 - max - min); 810 811 return [h, s * 100, l * 100]; 812} 813 814function rgb2hsv(rgb) { 815 var r = rgb[0], 816 g = rgb[1], 817 b = rgb[2], 818 min = Math.min(r, g, b), 819 max = Math.max(r, g, b), 820 delta = max - min, 821 h, s, v; 822 823 if (max == 0) 824 s = 0; 825 else 826 s = (delta/max * 1000)/10; 827 828 if (max == min) 829 h = 0; 830 else if (r == max) 831 h = (g - b) / delta; 832 else if (g == max) 833 h = 2 + (b - r) / delta; 834 else if (b == max) 835 h = 4 + (r - g) / delta; 836 837 h = Math.min(h * 60, 360); 838 839 if (h < 0) 840 h += 360; 841 842 v = ((max / 255) * 1000) / 10; 843 844 return [h, s, v]; 845} 846 847function rgb2hwb(rgb) { 848 var r = rgb[0], 849 g = rgb[1], 850 b = rgb[2], 851 h = rgb2hsl(rgb)[0], 852 w = 1/255 * Math.min(r, Math.min(g, b)), 853 b = 1 - 1/255 * Math.max(r, Math.max(g, b)); 854 855 return [h, w * 100, b * 100]; 856} 857 858function rgb2cmyk(rgb) { 859 var r = rgb[0] / 255, 860 g = rgb[1] / 255, 861 b = rgb[2] / 255, 862 c, m, y, k; 863 864 k = Math.min(1 - r, 1 - g, 1 - b); 865 c = (1 - r - k) / (1 - k) || 0; 866 m = (1 - g - k) / (1 - k) || 0; 867 y = (1 - b - k) / (1 - k) || 0; 868 return [c * 100, m * 100, y * 100, k * 100]; 869} 870 871function rgb2keyword(rgb) { 872 return reverseKeywords[JSON.stringify(rgb)]; 873} 874 875function rgb2xyz(rgb) { 876 var r = rgb[0] / 255, 877 g = rgb[1] / 255, 878 b = rgb[2] / 255; 879 880 // assume sRGB 881 r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); 882 g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); 883 b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); 884 885 var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); 886 var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); 887 var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); 888 889 return [x * 100, y *100, z * 100]; 890} 891 892function rgb2lab(rgb) { 893 var xyz = rgb2xyz(rgb), 894 x = xyz[0], 895 y = xyz[1], 896 z = xyz[2], 897 l, a, b; 898 899 x /= 95.047; 900 y /= 100; 901 z /= 108.883; 902 903 x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); 904 y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); 905 z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); 906 907 l = (116 * y) - 16; 908 a = 500 * (x - y); 909 b = 200 * (y - z); 910 911 return [l, a, b]; 912} 913 914function rgb2lch(args) { 915 return lab2lch(rgb2lab(args)); 916} 917 918function hsl2rgb(hsl) { 919 var h = hsl[0] / 360, 920 s = hsl[1] / 100, 921 l = hsl[2] / 100, 922 t1, t2, t3, rgb, val; 923 924 if (s == 0) { 925 val = l * 255; 926 return [val, val, val]; 927 } 928 929 if (l < 0.5) 930 t2 = l * (1 + s); 931 else 932 t2 = l + s - l * s; 933 t1 = 2 * l - t2; 934 935 rgb = [0, 0, 0]; 936 for (var i = 0; i < 3; i++) { 937 t3 = h + 1 / 3 * - (i - 1); 938 t3 < 0 && t3++; 939 t3 > 1 && t3--; 940 941 if (6 * t3 < 1) 942 val = t1 + (t2 - t1) * 6 * t3; 943 else if (2 * t3 < 1) 944 val = t2; 945 else if (3 * t3 < 2) 946 val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; 947 else 948 val = t1; 949 950 rgb[i] = val * 255; 951 } 952 953 return rgb; 954} 955 956function hsl2hsv(hsl) { 957 var h = hsl[0], 958 s = hsl[1] / 100, 959 l = hsl[2] / 100, 960 sv, v; 961 962 if(l === 0) { 963 // no need to do calc on black 964 // also avoids divide by 0 error 965 return [0, 0, 0]; 966 } 967 968 l *= 2; 969 s *= (l <= 1) ? l : 2 - l; 970 v = (l + s) / 2; 971 sv = (2 * s) / (l + s); 972 return [h, sv * 100, v * 100]; 973} 974 975function hsl2hwb(args) { 976 return rgb2hwb(hsl2rgb(args)); 977} 978 979function hsl2cmyk(args) { 980 return rgb2cmyk(hsl2rgb(args)); 981} 982 983function hsl2keyword(args) { 984 return rgb2keyword(hsl2rgb(args)); 985} 986 987 988function hsv2rgb(hsv) {
989 var h = hsv[0] / 60, 990 s = hsv[1] / 100, 991 v = hsv[2] / 100, 992 hi = Math.floor(h) % 6; 993 994 var f = h - Math.floor(h), 995 p = 255 * v * (1 - s), 996 q = 255 * v * (1 - (s * f)), 997 t = 255 * v * (1 - (s * (1 - f))), 998 v = 255 * v; 999 1000 switch(hi) { 1001 case 0: 1002 return [v, t, p]; 1003 case 1: 1004 return [q, v, p]; 1005 case 2: 1006 return [p, v, t]; 1007 case 3: 1008 return [p, q, v]; 1009 case 4: 1010 return [t, p, v]; 1011 case 5: 1012 return [v, p, q]; 1013 } 1014} 1015 1016function hsv2hsl(hsv) { 1017 var h = hsv[0], 1018 s = hsv[1] / 100, 1019 v = hsv[2] / 100, 1020 sl, l; 1021 1022 l = (2 - s) * v; 1023 sl = s * v; 1024 sl /= (l <= 1) ? l : 2 - l; 1025 sl = sl || 0; 1026 l /= 2; 1027 return [h, sl * 100, l * 100]; 1028} 1029 1030function hsv2hwb(args) { 1031 return rgb2hwb(hsv2rgb(args)) 1032} 1033 1034function hsv2cmyk(args) { 1035 return rgb2cmyk(hsv2rgb(args)); 1036} 1037 1038function hsv2keyword(args) { 1039 return rgb2keyword(hsv2rgb(args)); 1040} 1041 1042// http://dev.w3.org/csswg/css-color/#hwb-to-rgb 1043function hwb2rgb(hwb) { 1044 var h = hwb[0] / 360, 1045 wh = hwb[1] / 100, 1046 bl = hwb[2] / 100, 1047 ratio = wh + bl, 1048 i, v, f, n; 1049 1050 // wh + bl cant be > 1 1051 if (ratio > 1) { 1052 wh /= ratio; 1053 bl /= ratio; 1054 } 1055 1056 i = Math.floor(6 * h); 1057 v = 1 - bl; 1058 f = 6 * h - i; 1059 if ((i & 0x01) != 0) { 1060 f = 1 - f; 1061 } 1062 n = wh + f * (v - wh); // linear interpolation 1063 1064 switch (i) { 1065 default: 1066 case 6: 1067 case 0: r = v; g = n; b = wh; break; 1068 case 1: r = n; g = v; b = wh; break; 1069 case 2: r = wh; g = v; b = n; break; 1070 case 3: r = wh; g = n; b = v; break; 1071 case 4: r = n; g = wh; b = v; break; 1072 case 5: r = v; g = wh; b = n; break; 1073 } 1074 1075 return [r * 255, g * 255, b * 255]; 1076} 1077 1078function hwb2hsl(args) { 1079 return rgb2hsl(hwb2rgb(args)); 1080} 1081 1082function hwb2hsv(args) { 1083 return rgb2hsv(hwb2rgb(args)); 1084} 1085 1086function hwb2cmyk(args) { 1087 return rgb2cmyk(hwb2rgb(args)); 1088} 1089 1090function hwb2keyword(args) { 1091 return rgb2keyword(hwb2rgb(args)); 1092} 1093 1094function cmyk2rgb(cmyk) { 1095 var c = cmyk[0] / 100, 1096 m = cmyk[1] / 100, 1097 y = cmyk[2] / 100, 1098 k = cmyk[3] / 100, 1099 r, g, b; 1100 1101 r = 1 - Math.min(1, c * (1 - k) + k); 1102 g = 1 - Math.min(1, m * (1 - k) + k); 1103 b = 1 - Math.min(1, y * (1 - k) + k); 1104 return [r * 255, g * 255, b * 255]; 1105} 1106 1107function cmyk2hsl(args) { 1108 return rgb2hsl(cmyk2rgb(args)); 1109} 1110 1111function cmyk2hsv(args) { 1112 return rgb2hsv(cmyk2rgb(args)); 1113} 1114 1115function cmyk2hwb(args) { 1116 return rgb2hwb(cmyk2rgb(args)); 1117} 1118 1119function cmyk2keyword(args) { 1120 return rgb2keyword(cmyk2rgb(args)); 1121} 1122 1123 1124function xyz2rgb(xyz) { 1125 var x = xyz[0] / 100, 1126 y = xyz[1] / 100, 1127 z = xyz[2] / 100, 1128 r, g, b; 1129 1130 r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); 1131 g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); 1132 b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); 1133 1134 // assume sRGB 1135 r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) 1136 : r = (r * 12.92); 1137 1138 g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) 1139 : g = (g * 12.92); 1140 1141 b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) 1142 : b = (b * 12.92); 1143 1144 r = Math.min(Math.max(0, r), 1); 1145 g = Math.min(Math.max(0, g), 1); 1146 b = Math.min(Math.max(0, b), 1); 1147 1148 return [r * 255, g * 255, b * 255]; 1149} 1150 1151function xyz2lab(xyz) { 1152 var x = xyz[0], 1153 y = xyz[1], 1154 z = xyz[2], 1155 l, a, b; 1156 1157 x /= 95.047; 1158 y /= 100; 1159 z /= 108.883; 1160 1161 x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); 1162 y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); 1163 z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); 1164 1165 l = (116 * y) - 16; 1166 a = 500 * (x - y); 1167 b = 200 * (y - z); 1168 1169 return [l, a, b]; 1170} 1171 1172function xyz2lch(args) { 1173 return lab2lch(xyz2lab(args)); 1174} 1175 1176function lab2xyz(lab) { 1177 var l = lab[0], 1178 a = lab[1], 1179 b = lab[2], 1180 x, y, z, y2; 1181 1182 if (l <= 8) { 1183 y = (l * 100) / 903.3; 1184 y2 = (7.787 * (y / 100)) + (16 / 116); 1185 } else { 1186 y = 100 * Math.pow((l + 16) / 116, 3); 1187 y2 = Math.pow(y / 100, 1/3); 1188 } 1189 1190 x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); 1191 1192 z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); 1193 1194 return [x, y, z]; 1195} 1196 1197function lab2lch(lab) { 1198 var l = lab[0], 1199 a = lab[1], 1200 b = lab[2], 1201 hr, h, c; 1202 1203 hr = Math.atan2(b, a);
1204 h = hr * 360 / 2 / Math.PI; 1205 if (h < 0) { 1206 h += 360; 1207 } 1208 c = Math.sqrt(a * a + b * b); 1209 return [l, c, h]; 1210} 1211 1212function lab2rgb(args) { 1213 return xyz2rgb(lab2xyz(args)); 1214} 1215 1216function lch2lab(lch) { 1217 var l = lch[0], 1218 c = lch[1], 1219 h = lch[2], 1220 a, b, hr; 1221 1222 hr = h / 360 * 2 * Math.PI; 1223 a = c * Math.cos(hr); 1224 b = c * Math.sin(hr); 1225 return [l, a, b]; 1226} 1227 1228function lch2xyz(args) { 1229 return lab2xyz(lch2lab(args)); 1230} 1231 1232function lch2rgb(args) { 1233 return lab2rgb(lch2lab(args)); 1234} 1235 1236function keyword2rgb(keyword) { 1237 return cssKeywords[keyword]; 1238} 1239 1240function keyword2hsl(args) { 1241 return rgb2hsl(keyword2rgb(args)); 1242} 1243 1244function keyword2hsv(args) { 1245 return rgb2hsv(keyword2rgb(args)); 1246} 1247 1248function keyword2hwb(args) { 1249 return rgb2hwb(keyword2rgb(args)); 1250} 1251 1252function keyword2cmyk(args) { 1253 return rgb2cmyk(keyword2rgb(args)); 1254} 1255 1256function keyword2lab(args) { 1257 return rgb2lab(keyword2rgb(args)); 1258} 1259 1260function keyword2xyz(args) { 1261 return rgb2xyz(keyword2rgb(args)); 1262} 1263 1264var cssKeywords = { 1265 aliceblue: [240,248,255], 1266 antiquewhite: [250,235,215], 1267 aqua: [0,255,255], 1268 aquamarine: [127,255,212], 1269 azure: [240,255,255], 1270 beige: [245,245,220], 1271 bisque: [255,228,196], 1272 black: [0,0,0], 1273 blanchedalmond: [255,235,205], 1274 blue: [0,0,255], 1275 blueviolet: [138,43,226], 1276 brown: [165,42,42], 1277 burlywood: [222,184,135], 1278 cadetblue: [95,158,160], 1279 chartreuse: [127,255,0], 1280 chocolate: [210,105,30], 1281 coral: [255,127,80], 1282 cornflowerblue: [100,149,237], 1283 cornsilk: [255,248,220], 1284 crimson: [220,20,60], 1285 cyan: [0,255,255], 1286 darkblue: [0,0,139], 1287 darkcyan: [0,139,139], 1288 darkgoldenrod: [184,134,11], 1289 darkgray: [169,169,169], 1290 darkgreen: [0,100,0], 1291 darkgrey: [169,169,169], 1292 darkkhaki: [189,183,107], 1293 darkmagenta: [139,0,139], 1294 darkolivegreen: [85,107,47], 1295 darkorange: [255,140,0], 1296 darkorchid: [153,50,204], 1297 darkred: [139,0,0], 1298 darksalmon: [233,150,122], 1299 darkseagreen: [143,188,143], 1300 darkslateblue: [72,61,139], 1301 darkslategray: [47,79,79], 1302 darkslategrey: [47,79,79], 1303 darkturquoise: [0,206,209], 1304 darkviolet: [148,0,211], 1305 deeppink: [255,20,147], 1306 deepskyblue: [0,191,255], 1307 dimgray: [105,105,105], 1308 dimgrey: [105,105,105], 1309 dodgerblue: [30,144,255], 1310 firebrick: [178,34,34], 1311 floralwhite: [255,250,240], 1312 forestgreen: [34,139,34], 1313 fuchsia: [255,0,255], 1314 gainsboro: [220,220,220], 1315 ghostwhite: [248,248,255], 1316 gold: [255,215,0], 1317 goldenrod: [218,165,32], 1318 gray: [128,128,128], 1319 green: [0,128,0], 1320 greenyellow: [173,255,47], 1321 grey: [128,128,128], 1322 honeydew: [240,255,240], 1323 hotpink: [255,105,180], 1324 indianred: [205,92,92], 1325 indigo: [75,0,130], 1326 ivory: [255,255,240], 1327 khaki: [240,230,140], 1328 lavender: [230,230,250], 1329 lavenderblush: [255,240,245], 1330 lawngreen: [124,252,0], 1331 lemonchiffon: [255,250,205], 1332 lightblue: [173,216,230], 1333 lightcoral: [240,128,128], 1334 lightcyan: [224,255,255], 1335 lightgoldenrodyellow: [250,250,210], 1336 lightgray: [211,211,211], 1337 lightgreen: [144,238,144], 1338 lightgrey: [211,211,211], 1339 lightpink: [255,182,193], 1340 lightsalmon: [255,160,122], 1341 lightseagreen: [32,178,170], 1342 lightskyblue: [135,206,250], 1343 lightslategray: [119,136,153], 1344 lightslategrey: [119,136,153], 1345 lightsteelblue: [176,196,222], 1346 lightyellow: [255,255,224], 1347 lime: [0,255,0], 1348 limegreen: [50,205,50], 1349 linen: [250,240,230], 1350 magenta: [255,0,255], 1351 maroon: [128,0,0], 1352 mediumaquamarine: [102,205,170], 1353 mediumblue: [0,0,205], 1354 mediumorchid: [186,85,211], 1355 mediumpurple: [147,112,219], 1356 mediumseagreen: [60,179,113], 1357 mediumslateblue: [123,104,238], 1358 mediumspringgreen: [0,250,154], 1359 mediumturquoise: [72,209,204], 1360 mediumvioletred: [199,21,133], 1361 midnightblue: [25,25,112], 1362 mintcream: [245,255,250], 1363 mistyrose: [255,228,225], 1364 moccasin: [255,228,181], 1365 navajowhite: [255,222,173], 1366 navy: [0,0,128], 1367 oldlace: [253,245,230], 1368 olive: [128,128,0], 1369 olivedrab: [107,142,35], 1370 orange: [255,165,0], 1371 orangered: [255,69,0], 1372 orchid: [218,112,214], 1373 palegoldenrod: [238,232,170], 1374 palegreen: [152,251,152], 1375 paleturquoise: [175,238,238], 1376 palevioletred: [219,112,147], 1377 papayawhip: [255,239,213], 1378 peachpuff: [255,218,185], 1379 peru: [205,133,63], 1380 pink: [255,192,203], 1381 plum: [221,160,221], 1382 powderblue: [176,224,230], 1383 purple: [128,0,128], 1384 rebeccapurple: [102, 51, 153], 1385 red: [255,0,0], 1386 rosybrown: [188,143,143], 1387 royalblue: [65,105,225], 1388 saddlebrown: [139,69,19], 1389 salmon: [250,128,114], 1390 sandybrown: [244,164,96],
1391 seagreen: [46,139,87], 1392 seashell: [255,245,238], 1393 sienna: [160,82,45], 1394 silver: [192,192,192], 1395 skyblue: [135,206,235], 1396 slateblue: [106,90,205], 1397 slategray: [112,128,144], 1398 slategrey: [112,128,144], 1399 snow: [255,250,250], 1400 springgreen: [0,255,127], 1401 steelblue: [70,130,180], 1402 tan: [210,180,140], 1403 teal: [0,128,128], 1404 thistle: [216,191,216], 1405 tomato: [255,99,71], 1406 turquoise: [64,224,208], 1407 violet: [238,130,238], 1408 wheat: [245,222,179], 1409 white: [255,255,255], 1410 whitesmoke: [245,245,245], 1411 yellow: [255,255,0], 1412 yellowgreen: [154,205,50] 1413}; 1414 1415var reverseKeywords = {}; 1416for (var key in cssKeywords) { 1417 reverseKeywords[JSON.stringify(cssKeywords[key])] = key; 1418} 1419 1420},{}],4:[function(require,module,exports){ 1421var conversions = require(3); 1422 1423var convert = function() { 1424 return new Converter(); 1425} 1426 1427for (var func in conversions) { 1428 // export Raw versions 1429 convert[func + "Raw"] = (function(func) { 1430 // accept array or plain args 1431 return function(arg) { 1432 if (typeof arg == "number") 1433 arg = Array.prototype.slice.call(arguments); 1434 return conversions[func](arg); 1435 } 1436 })(func); 1437 1438 var pair = /(\w+)2(\w+)/.exec(func), 1439 from = pair[1], 1440 to = pair[2]; 1441 1442 // export rgb2hsl and ["rgb"]["hsl"] 1443 convert[from] = convert[from] || {}; 1444 1445 convert[from][to] = convert[func] = (function(func) { 1446 return function(arg) { 1447 if (typeof arg == "number") 1448 arg = Array.prototype.slice.call(arguments); 1449 1450 var val = conversions[func](arg); 1451 if (typeof val == "string" || val === undefined) 1452 return val; // keyword 1453 1454 for (var i = 0; i < val.length; i++) 1455 val[i] = Math.round(val[i]); 1456 return val; 1457 } 1458 })(func); 1459} 1460 1461 1462/* Converter does lazy conversion and caching */ 1463var Converter = function() { 1464 this.convs = {}; 1465}; 1466 1467/* Either get the values for a space or 1468 set the values for a space, depending on args */ 1469Converter.prototype.routeSpace = function(space, args) { 1470 var values = args[0]; 1471 if (values === undefined) { 1472 // color.rgb() 1473 return this.getValues(space); 1474 } 1475 // color.rgb(10, 10, 10) 1476 if (typeof values == "number") { 1477 values = Array.prototype.slice.call(args); 1478 } 1479 1480 return this.setValues(space, values); 1481}; 1482 1483/* Set the values for a space, invalidating cache */ 1484Converter.prototype.setValues = function(space, values) { 1485 this.space = space; 1486 this.convs = {}; 1487 this.convs[space] = values; 1488 return this; 1489}; 1490 1491/* Get the values for a space. If there's already 1492 a conversion for the space, fetch it, otherwise 1493 compute it */ 1494Converter.prototype.getValues = function(space) { 1495 var vals = this.convs[space]; 1496 if (!vals) { 1497 var fspace = this.space, 1498 from = this.convs[fspace]; 1499 vals = convert[fspace][space](from); 1500 1501 this.convs[space] = vals; 1502 } 1503 return vals; 1504}; 1505 1506["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { 1507 Converter.prototype[space] = function(vals) { 1508 return this.routeSpace(space, arguments); 1509 } 1510}); 1511 1512module.exports = convert; 1513},{"3":3}],5:[function(require,module,exports){ 1514'use strict' 1515 1516module.exports = { 1517 "aliceblue": [240, 248, 255], 1518 "antiquewhite": [250, 235, 215], 1519 "aqua": [0, 255, 255], 1520 "aquamarine": [127, 255, 212], 1521 "azure": [240, 255, 255], 1522 "beige": [245, 245, 220], 1523 "bisque": [255, 228, 196], 1524 "black": [0, 0, 0], 1525 "blanchedalmond": [255, 235, 205], 1526 "blue": [0, 0, 255], 1527 "blueviolet": [138, 43, 226], 1528 "brown": [165, 42, 42], 1529 "burlywood": [222, 184, 135], 1530 "cadetblue": [95, 158, 160], 1531 "chartreuse": [127, 255, 0], 1532 "chocolate": [210, 105, 30], 1533 "coral": [255, 127, 80], 1534 "cornflowerblue": [100, 149, 237], 1535 "cornsilk": [255, 248, 220], 1536 "crimson": [220, 20, 60], 1537 "cyan": [0, 255, 255], 1538 "darkblue": [0, 0, 139],
1539 "darkcyan": [0, 139, 139], 1540 "darkgoldenrod": [184, 134, 11], 1541 "darkgray": [169, 169, 169], 1542 "darkgreen": [0, 100, 0], 1543 "darkgrey": [169, 169, 169], 1544 "darkkhaki": [189, 183, 107], 1545 "darkmagenta": [139, 0, 139], 1546 "darkolivegreen": [85, 107, 47], 1547 "darkorange": [255, 140, 0], 1548 "darkorchid": [153, 50, 204], 1549 "darkred": [139, 0, 0], 1550 "darksalmon": [233, 150, 122], 1551 "darkseagreen": [143, 188, 143], 1552 "darkslateblue": [72, 61, 139], 1553 "darkslategray": [47, 79, 79], 1554 "darkslategrey": [47, 79, 79], 1555 "darkturquoise": [0, 206, 209], 1556 "darkviolet": [148, 0, 211], 1557 "deeppink": [255, 20, 147], 1558 "deepskyblue": [0, 191, 255], 1559 "dimgray": [105, 105, 105], 1560 "dimgrey": [105, 105, 105], 1561 "dodgerblue": [30, 144, 255], 1562 "firebrick": [178, 34, 34], 1563 "floralwhite": [255, 250, 240], 1564 "forestgreen": [34, 139, 34], 1565 "fuchsia": [255, 0, 255], 1566 "gainsboro": [220, 220, 220], 1567 "ghostwhite": [248, 248, 255], 1568 "gold": [255, 215, 0], 1569 "goldenrod": [218, 165, 32], 1570 "gray": [128, 128, 128], 1571 "green": [0, 128, 0], 1572 "greenyellow": [173, 255, 47], 1573 "grey": [128, 128, 128], 1574 "honeydew": [240, 255, 240], 1575 "hotpink": [255, 105, 180], 1576 "indianred": [205, 92, 92], 1577 "indigo": [75, 0, 130], 1578 "ivory": [255, 255, 240], 1579 "khaki": [240, 230, 140], 1580 "lavender": [230, 230, 250], 1581 "lavenderblush": [255, 240, 245], 1582 "lawngreen": [124, 252, 0], 1583 "lemonchiffon": [255, 250, 205], 1584 "lightblue": [173, 216, 230], 1585 "lightcoral": [240, 128, 128], 1586 "lightcyan": [224, 255, 255], 1587 "lightgoldenrodyellow": [250, 250, 210], 1588 "lightgray": [211, 211, 211], 1589 "lightgreen": [144, 238, 144], 1590 "lightgrey": [211, 211, 211], 1591 "lightpink": [255, 182, 193], 1592 "lightsalmon": [255, 160, 122], 1593 "lightseagreen": [32, 178, 170], 1594 "lightskyblue": [135, 206, 250], 1595 "lightslategray": [119, 136, 153], 1596 "lightslategrey": [119, 136, 153], 1597 "lightsteelblue": [176, 196, 222], 1598 "lightyellow": [255, 255, 224], 1599 "lime": [0, 255, 0], 1600 "limegreen": [50, 205, 50], 1601 "linen": [250, 240, 230], 1602 "magenta": [255, 0, 255], 1603 "maroon": [128, 0, 0], 1604 "mediumaquamarine": [102, 205, 170], 1605 "mediumblue": [0, 0, 205], 1606 "mediumorchid": [186, 85, 211], 1607 "mediumpurple": [147, 112, 219], 1608 "mediumseagreen": [60, 179, 113], 1609 "mediumslateblue": [123, 104, 238], 1610 "mediumspringgreen": [0, 250, 154], 1611 "mediumturquoise": [72, 209, 204], 1612 "mediumvioletred": [199, 21, 133], 1613 "midnightblue": [25, 25, 112], 1614 "mintcream": [245, 255, 250], 1615 "mistyrose": [255, 228, 225], 1616 "moccasin": [255, 228, 181], 1617 "navajowhite": [255, 222, 173], 1618 "navy": [0, 0, 128], 1619 "oldlace": [253, 245, 230], 1620 "olive": [128, 128, 0], 1621 "olivedrab": [107, 142, 35], 1622 "orange": [255, 165, 0], 1623 "orangered": [255, 69, 0], 1624 "orchid": [218, 112, 214], 1625 "palegoldenrod": [238, 232, 170], 1626 "palegreen": [152, 251, 152], 1627 "paleturquoise": [175, 238, 238], 1628 "palevioletred": [219, 112, 147], 1629 "papayawhip": [255, 239, 213], 1630 "peachpuff": [255, 218, 185], 1631 "peru": [205, 133, 63], 1632 "pink": [255, 192, 203], 1633 "plum": [221, 160, 221], 1634 "powderblue": [176, 224, 230], 1635 "purple": [128, 0, 128], 1636 "rebeccapurple": [102, 51, 153], 1637 "red": [255, 0, 0], 1638 "rosybrown": [188, 143, 143], 1639 "royalblue": [65, 105, 225], 1640 "saddlebrown": [139, 69, 19], 1641 "salmon": [250, 128, 114], 1642 "sandybrown": [244, 164, 96], 1643 "seagreen": [46, 139, 87], 1644 "seashell": [255, 245, 238], 1645 "sienna": [160, 82, 45], 1646 "silver": [192, 192, 192], 1647 "skyblue": [135, 206, 235], 1648 "slateblue": [106, 90, 205], 1649 "slategray": [112, 128, 144], 1650 "slategrey": [112, 128, 144], 1651 "snow": [255, 250, 250], 1652 "springgreen": [0, 255, 127], 1653 "steelblue": [70, 130, 180], 1654 "tan": [210, 180, 140], 1655 "teal": [0, 128, 128], 1656 "thistle": [216, 191, 216], 1657 "tomato": [255, 99, 71], 1658 "turquoise": [64, 224, 208], 1659 "violet": [238, 130, 238], 1660 "wheat": [245, 222, 179], 1661 "white": [255, 255, 255], 1662 "whitesmoke": [245, 245, 245], 1663 "yellow": [255, 255, 0], 1664 "yellowgreen": [154, 205, 50] 1665}; 1666 1667},{}],6:[function(require,module,exports){ 1668//! moment.js 1669//! version : 2.20.1 1670//! authors : Tim Wood, Iskren Chernev, Moment.js contributors 1671//! license : MIT 1672//! momentjs.com 1673 1674;(function (global, factory) { 1675 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 1676 typeof define === 'function' && define.amd ? define(factory) : 1677 global.moment = factory() 1678}(this, (function () { 'use strict'; 1679 1680var hookCallback; 1681 1682function hooks () { 1683 return hookCallback.apply(null, arguments); 1684} 1685 1686// This is done to register the method called with moment() 1687// without creating circular dependencies. 1688function setHookCallback (callback) { 1689 hookCallback = callback; 1690} 1691 1692function isArray(input) { 1693 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; 1694} 1695 1696function isObject(input) { 1697 // IE8 will treat undefined and null as object if it wasn't for 1698 // input != null 1699 return input != null && Object.prototype.toString.call(input) === '[object Object]'; 1700} 1701 1702function isObjectEmpty(obj) { 1703 if (Object.getOwnPropertyNames) { 1704 return (Object.getOwnPropertyNames(obj).length === 0); 1705 } else { 1706 var k; 1707 for (k in obj) { 1708 if (obj.hasOwnProperty(k)) { 1709 return false;
vendor: 22,951 bytes, lines 1710-2587
1710 } 1711 } 1712 return true; 1713 } 1714} 1715 1716function isUndefined(input) { 1717 return input === void 0; 1718} 1719 1720function isNumber(input) { 1721 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; 1722} 1723 1724function isDate(input) { 1725 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; 1726} 1727 1728function map(arr, fn) { 1729 var res = [], i; 1730 for (i = 0; i < arr.length; ++i) { 1731 res.push(fn(arr[i], i)); 1732 } 1733 return res; 1734} 1735 1736function hasOwnProp(a, b) { 1737 return Object.prototype.hasOwnProperty.call(a, b); 1738} 1739 1740function extend(a, b) { 1741 for (var i in b) { 1742 if (hasOwnProp(b, i)) { 1743 a[i] = b[i]; 1744 } 1745 } 1746 1747 if (hasOwnProp(b, 'toString')) { 1748 a.toString = b.toString; 1749 } 1750 1751 if (hasOwnProp(b, 'valueOf')) { 1752 a.valueOf = b.valueOf; 1753 } 1754 1755 return a; 1756} 1757 1758function createUTC (input, format, locale, strict) { 1759 return createLocalOrUTC(input, format, locale, strict, true).utc(); 1760} 1761 1762function defaultParsingFlags() { 1763 // We need to deep clone this object. 1764 return { 1765 empty : false, 1766 unusedTokens : [], 1767 unusedInput : [], 1768 overflow : -2, 1769 charsLeftOver : 0, 1770 nullInput : false, 1771 invalidMonth : null, 1772 invalidFormat : false, 1773 userInvalidated : false, 1774 iso : false, 1775 parsedDateParts : [], 1776 meridiem : null, 1777 rfc2822 : false, 1778 weekdayMismatch : false 1779 }; 1780} 1781 1782function getParsingFlags(m) { 1783 if (m._pf == null) { 1784 m._pf = defaultParsingFlags(); 1785 } 1786 return m._pf; 1787} 1788 1789var some; 1790if (Array.prototype.some) { 1791 some = Array.prototype.some; 1792} else { 1793 some = function (fun) { 1794 var t = Object(this); 1795 var len = t.length >>> 0; 1796 1797 for (var i = 0; i < len; i++) { 1798 if (i in t && fun.call(this, t[i], i, t)) { 1799 return true; 1800 } 1801 } 1802 1803 return false; 1804 }; 1805} 1806 1807function isValid(m) { 1808 if (m._isValid == null) { 1809 var flags = getParsingFlags(m); 1810 var parsedParts = some.call(flags.parsedDateParts, function (i) { 1811 return i != null; 1812 }); 1813 var isNowValid = !isNaN(m._d.getTime()) && 1814 flags.overflow < 0 && 1815 !flags.empty && 1816 !flags.invalidMonth && 1817 !flags.invalidWeekday && 1818 !flags.weekdayMismatch && 1819 !flags.nullInput && 1820 !flags.invalidFormat && 1821 !flags.userInvalidated && 1822 (!flags.meridiem || (flags.meridiem && parsedParts)); 1823 1824 if (m._strict) { 1825 isNowValid = isNowValid && 1826 flags.charsLeftOver === 0 && 1827 flags.unusedTokens.length === 0 && 1828 flags.bigHour === undefined; 1829 } 1830 1831 if (Object.isFrozen == null || !Object.isFrozen(m)) { 1832 m._isValid = isNowValid; 1833 } 1834 else { 1835 return isNowValid; 1836 } 1837 } 1838 return m._isValid; 1839} 1840 1841function createInvalid (flags) { 1842 var m = createUTC(NaN); 1843 if (flags != null) { 1844 extend(getParsingFlags(m), flags); 1845 } 1846 else { 1847 getParsingFlags(m).userInvalidated = true; 1848 } 1849 1850 return m; 1851} 1852 1853// Plugins that add properties should also add the key here (null value), 1854// so we can properly clone ourselves. 1855var momentProperties = hooks.momentProperties = []; 1856 1857function copyConfig(to, from) { 1858 var i, prop, val; 1859 1860 if (!isUndefined(from._isAMomentObject)) { 1861 to._isAMomentObject = from._isAMomentObject; 1862 } 1863 if (!isUndefined(from._i)) { 1864 to._i = from._i; 1865 } 1866 if (!isUndefined(from._f)) { 1867 to._f = from._f; 1868 } 1869 if (!isUndefined(from._l)) { 1870 to._l = from._l; 1871 } 1872 if (!isUndefined(from._strict)) { 1873 to._strict = from._strict; 1874 } 1875 if (!isUndefined(from._tzm)) { 1876 to._tzm = from._tzm; 1877 } 1878 if (!isUndefined(from._isUTC)) { 1879 to._isUTC = from._isUTC; 1880 } 1881 if (!isUndefined(from._offset)) { 1882 to._offset = from._offset; 1883 } 1884 if (!isUndefined(from._pf)) { 1885 to._pf = getParsingFlags(from); 1886 } 1887 if (!isUndefined(from._locale)) { 1888 to._locale = from._locale; 1889 } 1890 1891 if (momentProperties.length > 0) { 1892 for (i = 0; i < momentProperties.length; i++) { 1893 prop = momentProperties[i]; 1894 val = from[prop]; 1895 if (!isUndefined(val)) { 1896 to[prop] = val; 1897 } 1898 } 1899 } 1900 1901 return to; 1902} 1903 1904var updateInProgress = false; 1905 1906// Moment prototype object 1907function Moment(config) { 1908 copyConfig(this, config); 1909 this._d = new Date(config._d != null ? config._d.getTime() : NaN); 1910 if (!this.isValid()) { 1911 this._d = new Date(NaN); 1912 } 1913 // Prevent infinite loop in case updateOffset creates new moment 1914 // objects. 1915 if (updateInProgress === false) { 1916 updateInProgress = true; 1917 hooks.updateOffset(this); 1918 updateInProgress = false; 1919 } 1920} 1921 1922function isMoment (obj) { 1923 return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); 1924} 1925 1926function absFloor (number) { 1927 if (number < 0) { 1928 // -0 -> 0 1929 return Math.ceil(number) || 0; 1930 } else { 1931 return Math.floor(number); 1932 } 1933} 1934 1935function toInt(argumentForCoercion) { 1936 var coercedNumber = +argumentForCoercion, 1937 value = 0; 1938 1939 if (coercedNumber !== 0 && isFinite(coercedNumber)) { 1940 value = absFloor(coercedNumber); 1941 } 1942 1943 return value; 1944} 1945 1946// compare two arrays, return the number of differences 1947function compareArrays(array1, array2, dontConvert) { 1948 var len = Math.min(array1.length, array2.length), 1949 lengthDiff = Math.abs(array1.length - array2.length), 1950 diffs = 0, 1951 i; 1952 for (i = 0; i < len; i++) { 1953 if ((dontConvert && array1[i] !== array2[i]) || 1954 (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { 1955 diffs++; 1956 } 1957 } 1958 return diffs + lengthDiff; 1959} 1960 1961function warn(msg) { 1962 if (hooks.suppressDeprecationWarnings === false && 1963 (typeof console !== 'undefined') && console.warn) { 1964 console.warn('Deprecation warning: ' + msg); 1965 } 1966} 1967 1968function deprecate(msg, fn) { 1969 var firstTime = true; 1970 1971 return extend(function () { 1972 if (hooks.deprecationHandler != null) { 1973 hooks.deprecationHandler(null, msg); 1974 } 1975 if (firstTime) { 1976 var args = []; 1977 var arg; 1978 for (var i = 0; i < arguments.length; i++) { 1979 arg = ''; 1980 if (typeof arguments[i] === 'object') { 1981 arg += '\n[' + i + '] '; 1982 for (var key in arguments[0]) { 1983 arg += key + ': ' + arguments[0][key] + ', '; 1984 } 1985 arg = arg.slice(0, -2); // Remove trailing comma and space 1986 } else { 1987 arg = arguments[i]; 1988 } 1989 args.push(arg); 1990 } 1991 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); 1992 firstTime = false; 1993 } 1994 return fn.apply(this, arguments); 1995 }, fn); 1996} 1997 1998var deprecations = {}; 1999 2000function deprecateSimple(name, msg) { 2001 if (hooks.deprecationHandler != null) { 2002 hooks.deprecationHandler(name, msg); 2003 } 2004 if (!deprecations[name]) { 2005 warn(msg); 2006 deprecations[name] = true; 2007 } 2008} 2009 2010hooks.suppressDeprecationWarnings = false; 2011hooks.deprecationHandler = null; 2012 2013function isFunction(input) { 2014 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; 2015} 2016 2017function set (config) { 2018 var prop, i; 2019 for (i in config) { 2020 prop = config[i]; 2021 if (isFunction(prop)) { 2022 this[i] = prop; 2023 } else { 2024 this['_' + i] = prop; 2025 } 2026 } 2027 this._config = config; 2028 // Lenient ordinal parsing accepts just a number in addition to 2029 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. 2030 // TODO: Remove "ordinalParse" fallback in next major release. 2031 this._dayOfMonthOrdinalParseLenient = new RegExp( 2032 (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + 2033 '|' + (/\d{1,2}/).source); 2034} 2035 2036function mergeConfigs(parentConfig, childConfig) { 2037 var res = extend({}, parentConfig), prop; 2038 for (prop in childConfig) { 2039 if (hasOwnProp(childConfig, prop)) { 2040 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { 2041 res[prop] = {}; 2042 extend(res[prop], parentConfig[prop]); 2043 extend(res[prop], childConfig[prop]); 2044 } else if (childConfig[prop] != null) { 2045 res[prop] = childConfig[prop]; 2046 } else { 2047 delete res[prop]; 2048 } 2049 } 2050 } 2051 for (prop in parentConfig) { 2052 if (hasOwnProp(parentConfig, prop) && 2053 !hasOwnProp(childConfig, prop) && 2054 isObject(parentConfig[prop])) { 2055 // make sure changes to properties don't modify parent config 2056 res[prop] = extend({}, res[prop]); 2057 } 2058 } 2059 return res; 2060} 2061 2062function Locale(config) { 2063 if (config != null) { 2064 this.set(config); 2065 } 2066} 2067 2068var keys; 2069 2070if (Object.keys) { 2071 keys = Object.keys; 2072} else { 2073 keys = function (obj) { 2074 var i, res = []; 2075 for (i in obj) { 2076 if (hasOwnProp(obj, i)) { 2077 res.push(i); 2078 } 2079 } 2080 return res; 2081 }; 2082} 2083 2084var defaultCalendar = { 2085 sameDay : '[Today at] LT', 2086 nextDay : '[Tomorrow at] LT', 2087 nextWeek : 'dddd [at] LT', 2088 lastDay : '[Yesterday at] LT', 2089 lastWeek : '[Last] dddd [at] LT', 2090 sameElse : 'L' 2091}; 2092 2093function calendar (key, mom, now) { 2094 var output = this._calendar[key] || this._calendar['sameElse']; 2095 return isFunction(output) ? output.call(mom, now) : output; 2096} 2097 2098var defaultLongDateFormat = { 2099 LTS : 'h:mm:ss A', 2100 LT : 'h:mm A', 2101 L : 'MM/DD/YYYY', 2102 LL : 'MMMM D, YYYY', 2103 LLL : 'MMMM D, YYYY h:mm A', 2104 LLLL : 'dddd, MMMM D, YYYY h:mm A' 2105}; 2106 2107function longDateFormat (key) { 2108 var format = this._longDateFormat[key], 2109 formatUpper = this._longDateFormat[key.toUpperCase()]; 2110 2111 if (format || !formatUpper) { 2112 return format; 2113 } 2114 2115 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { 2116 return val.slice(1); 2117 }); 2118 2119 return this._longDateFormat[key]; 2120} 2121 2122var defaultInvalidDate = 'Invalid date'; 2123 2124function invalidDate () { 2125 return this._invalidDate; 2126} 2127 2128var defaultOrdinal = '%d'; 2129var defaultDayOfMonthOrdinalParse = /\d{1,2}/; 2130 2131function ordinal (number) { 2132 return this._ordinal.replace('%d', number); 2133} 2134 2135var defaultRelativeTime = { 2136 future : 'in %s', 2137 past : '%s ago', 2138 s : 'a few seconds', 2139 ss : '%d seconds', 2140 m : 'a minute', 2141 mm : '%d minutes', 2142 h : 'an hour', 2143 hh : '%d hours', 2144 d : 'a day', 2145 dd : '%d days', 2146 M : 'a month', 2147 MM : '%d months', 2148 y : 'a year', 2149 yy : '%d years' 2150}; 2151 2152function relativeTime (number, withoutSuffix, string, isFuture) { 2153 var output = this._relativeTime[string]; 2154 return (isFunction(output)) ? 2155 output(number, withoutSuffix, string, isFuture) : 2156 output.replace(/%d/i, number); 2157} 2158 2159function pastFuture (diff, output) { 2160 var format = this._relativeTime[diff > 0 ? 'future' : 'past']; 2161 return isFunction(format) ? format(output) : format.replace(/%s/i, output); 2162} 2163 2164var aliases = {}; 2165 2166function addUnitAlias (unit, shorthand) { 2167 var lowerCase = unit.toLowerCase(); 2168 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; 2169} 2170 2171function normalizeUnits(units) { 2172 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; 2173} 2174 2175function normalizeObjectUnits(inputObject) { 2176 var normalizedInput = {}, 2177 normalizedProp, 2178 prop; 2179 2180 for (prop in inputObject) { 2181 if (hasOwnProp(inputObject, prop)) { 2182 normalizedProp = normalizeUnits(prop); 2183 if (normalizedProp) { 2184 normalizedInput[normalizedProp] = inputObject[prop]; 2185 } 2186 } 2187 } 2188 2189 return normalizedInput; 2190} 2191 2192var priorities = {}; 2193 2194function addUnitPriority(unit, priority) { 2195 priorities[unit] = priority; 2196} 2197 2198function getPrioritizedUnits(unitsObj) { 2199 var units = []; 2200 for (var u in unitsObj) { 2201 units.push({unit: u, priority: priorities[u]}); 2202 } 2203 units.sort(function (a, b) { 2204 return a.priority - b.priority; 2205 }); 2206 return units; 2207} 2208 2209function zeroFill(number, targetLength, forceSign) { 2210 var absNumber = '' + Math.abs(number), 2211 zerosToFill = targetLength - absNumber.length, 2212 sign = number >= 0; 2213 return (sign ? (forceSign ? '+' : '') : '-') + 2214 Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; 2215} 2216 2217var 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; 2218 2219var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; 2220 2221var formatFunctions = {}; 2222 2223var formatTokenFunctions = {}; 2224 2225// token: 'M' 2226// padded: ['MM', 2] 2227// ordinal: 'Mo' 2228// callback: function () { this.month() + 1 } 2229function addFormatToken (token, padded, ordinal, callback) { 2230 var func = callback; 2231 if (typeof callback === 'string') { 2232 func = function () { 2233 return this[callback](); 2234 }; 2235 } 2236 if (token) { 2237 formatTokenFunctions[token] = func; 2238 } 2239 if (padded) { 2240 formatTokenFunctions[padded[0]] = function () { 2241 return zeroFill(func.apply(this, arguments), padded[1], padded[2]); 2242 }; 2243 } 2244 if (ordinal) { 2245 formatTokenFunctions[ordinal] = function () { 2246 return this.localeData().ordinal(func.apply(this, arguments), token); 2247 }; 2248 } 2249} 2250 2251function removeFormattingTokens(input) { 2252 if (input.match(/\[[\s\S]/)) { 2253 return input.replace(/^\[|\]$/g, ''); 2254 } 2255 return input.replace(/\\/g, ''); 2256} 2257 2258function makeFormatFunction(format) { 2259 var array = format.match(formattingTokens), i, length; 2260 2261 for (i = 0, length = array.length; i < length; i++) { 2262 if (formatTokenFunctions[array[i]]) { 2263 array[i] = formatTokenFunctions[array[i]]; 2264 } else { 2265 array[i] = removeFormattingTokens(array[i]); 2266 } 2267 } 2268 2269 return function (mom) { 2270 var output = '', i; 2271 for (i = 0; i < length; i++) { 2272 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; 2273 } 2274 return output; 2275 }; 2276} 2277 2278// format date using native date object 2279function formatMoment(m, format) { 2280 if (!m.isValid()) { 2281 return m.localeData().invalidDate(); 2282 } 2283 2284 format = expandFormat(format, m.localeData()); 2285 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); 2286 2287 return formatFunctions[format](m); 2288} 2289 2290function expandFormat(format, locale) { 2291 var i = 5; 2292 2293 function replaceLongDateFormatTokens(input) { 2294 return locale.longDateFormat(input) || input; 2295 } 2296 2297 localFormattingTokens.lastIndex = 0; 2298 while (i >= 0 && localFormattingTokens.test(format)) { 2299 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); 2300 localFormattingTokens.lastIndex = 0; 2301 i -= 1; 2302 } 2303 2304 return format; 2305} 2306 2307var match1 = /\d/; // 0 - 9 2308var match2 = /\d\d/; // 00 - 99 2309var match3 = /\d{3}/; // 000 - 999 2310var match4 = /\d{4}/; // 0000 - 9999 2311var match6 = /[+-]?\d{6}/; // -999999 - 999999 2312var match1to2 = /\d\d?/; // 0 - 99 2313var match3to4 = /\d\d\d\d?/; // 999 - 9999 2314var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 2315var match1to3 = /\d{1,3}/; // 0 - 999 2316var match1to4 = /\d{1,4}/; // 0 - 9999 2317var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 2318 2319var matchUnsigned = /\d+/; // 0 - inf 2320var matchSigned = /[+-]?\d+/; // -inf - inf 2321 2322var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z 2323var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z 2324 2325var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 2326 2327// any word (or two) characters or numbers including two/three word month in arabic. 2328// includes scottish gaelic two word and hyphenated months 2329var 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; 2330 2331 2332var regexes = {}; 2333 2334function addRegexToken (token, regex, strictRegex) { 2335 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { 2336 return (isStrict && strictRegex) ? strictRegex : regex; 2337 }; 2338} 2339 2340function getParseRegexForToken (token, config) { 2341 if (!hasOwnProp(regexes, token)) { 2342 return new RegExp(unescapeFormat(token)); 2343 } 2344 2345 return regexes[token](config._strict, config._locale); 2346} 2347 2348// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript 2349function unescapeFormat(s) { 2350 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { 2351 return p1 || p2 || p3 || p4; 2352 })); 2353} 2354 2355function regexEscape(s) { 2356 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 2357} 2358 2359var tokens = {}; 2360 2361function addParseToken (token, callback) { 2362 var i, func = callback; 2363 if (typeof token === 'string') { 2364 token = [token]; 2365 } 2366 if (isNumber(callback)) { 2367 func = function (input, array) { 2368 array[callback] = toInt(input); 2369 }; 2370 } 2371 for (i = 0; i < token.length; i++) { 2372 tokens[token[i]] = func; 2373 } 2374} 2375 2376function addWeekParseToken (token, callback) { 2377 addParseToken(token, function (input, array, config, token) { 2378 config._w = config._w || {}; 2379 callback(input, config._w, config, token); 2380 }); 2381} 2382 2383function addTimeToArrayFromToken(token, input, config) { 2384 if (input != null && hasOwnProp(tokens, token)) { 2385 tokens[token](input, config._a, config, token); 2386 } 2387} 2388 2389var YEAR = 0; 2390var MONTH = 1; 2391var DATE = 2; 2392var HOUR = 3; 2393var MINUTE = 4; 2394var SECOND = 5; 2395var MILLISECOND = 6; 2396var WEEK = 7; 2397var WEEKDAY = 8; 2398 2399// FORMATTING 2400 2401addFormatToken('Y', 0, 0, function () { 2402 var y = this.year(); 2403 return y <= 9999 ? '' + y : '+' + y; 2404}); 2405 2406addFormatToken(0, ['YY', 2], 0, function () { 2407 return this.year() % 100; 2408}); 2409 2410addFormatToken(0, ['YYYY', 4], 0, 'year'); 2411addFormatToken(0, ['YYYYY', 5], 0, 'year'); 2412addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); 2413 2414// ALIASES 2415 2416addUnitAlias('year', 'y'); 2417 2418// PRIORITIES 2419 2420addUnitPriority('year', 1); 2421 2422// PARSING 2423 2424addRegexToken('Y', matchSigned); 2425addRegexToken('YY', match1to2, match2); 2426addRegexToken('YYYY', match1to4, match4); 2427addRegexToken('YYYYY', match1to6, match6); 2428addRegexToken('YYYYYY', match1to6, match6); 2429 2430addParseToken(['YYYYY', 'YYYYYY'], YEAR); 2431addParseToken('YYYY', function (input, array) { 2432 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); 2433}); 2434addParseToken('YY', function (input, array) { 2435 array[YEAR] = hooks.parseTwoDigitYear(input); 2436}); 2437addParseToken('Y', function (input, array) { 2438 array[YEAR] = parseInt(input, 10); 2439}); 2440 2441// HELPERS 2442 2443function daysInYear(year) { 2444 return isLeapYear(year) ? 366 : 365; 2445} 2446 2447function isLeapYear(year) { 2448 return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; 2449} 2450 2451// HOOKS 2452 2453hooks.parseTwoDigitYear = function (input) { 2454 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); 2455}; 2456 2457// MOMENTS 2458 2459var getSetYear = makeGetSet('FullYear', true); 2460 2461function getIsLeapYear () { 2462 return isLeapYear(this.year()); 2463} 2464 2465function makeGetSet (unit, keepTime) { 2466 return function (value) { 2467 if (value != null) { 2468 set$1(this, unit, value); 2469 hooks.updateOffset(this, keepTime); 2470 return this; 2471 } else { 2472 return get(this, unit); 2473 } 2474 }; 2475} 2476 2477function get (mom, unit) { 2478 return mom.isValid() ? 2479 mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; 2480} 2481 2482function set$1 (mom, unit, value) { 2483 if (mom.isValid() && !isNaN(value)) { 2484 if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { 2485 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); 2486 } 2487 else { 2488 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); 2489 } 2490 } 2491} 2492 2493// MOMENTS 2494 2495function stringGet (units) { 2496 units = normalizeUnits(units); 2497 if (isFunction(this[units])) { 2498 return this[units](); 2499 } 2500 return this; 2501} 2502 2503 2504function stringSet (units, value) { 2505 if (typeof units === 'object') { 2506 units = normalizeObjectUnits(units); 2507 var prioritized = getPrioritizedUnits(units); 2508 for (var i = 0; i < prioritized.length; i++) { 2509 this[prioritized[i].unit](units[prioritized[i].unit]); 2510 } 2511 } else { 2512 units = normalizeUnits(units); 2513 if (isFunction(this[units])) { 2514 return this[units](value); 2515 } 2516 } 2517 return this; 2518} 2519 2520function mod(n, x) { 2521 return ((n % x) + x) % x; 2522} 2523 2524var indexOf; 2525 2526if (Array.prototype.indexOf) { 2527 indexOf = Array.prototype.indexOf; 2528} else { 2529 indexOf = function (o) { 2530 // I know 2531 var i; 2532 for (i = 0; i < this.length; ++i) { 2533 if (this[i] === o) { 2534 return i; 2535 } 2536 } 2537 return -1; 2538 }; 2539} 2540 2541function daysInMonth(year, month) { 2542 if (isNaN(year) || isNaN(month)) { 2543 return NaN; 2544 } 2545 var modMonth = mod(month, 12); 2546 year += (month - modMonth) / 12; 2547 return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); 2548} 2549 2550// FORMATTING 2551 2552addFormatToken('M', ['MM', 2], 'Mo', function () { 2553 return this.month() + 1; 2554}); 2555 2556addFormatToken('MMM', 0, 0, function (format) { 2557 return this.localeData().monthsShort(this, format); 2558}); 2559 2560addFormatToken('MMMM', 0, 0, function (format) { 2561 return this.localeData().months(this, format); 2562}); 2563 2564// ALIASES 2565 2566addUnitAlias('month', 'M'); 2567 2568// PRIORITY 2569 2570addUnitPriority('month', 8); 2571 2572// PARSING 2573 2574addRegexToken('M', match1to2); 2575addRegexToken('MM', match1to2, match2); 2576addRegexToken('MMM', function (isStrict, locale) { 2577 return locale.monthsShortRegex(isStrict); 2578}); 2579addRegexToken('MMMM', function (isStrict, locale) { 2580 return locale.monthsRegex(isStrict); 2581}); 2582 2583addParseToken(['M', 'MM'], function (input, array) { 2584 array[MONTH] = toInt(input) - 1; 2585}); 2586 2587addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
vendor: 24,092 bytes, lines 2588-3372
2588 var month = config._locale.monthsParse(input, token, config._strict); 2589 // if we didn't find a month name, mark the date as invalid. 2590 if (month != null) { 2591 array[MONTH] = month; 2592 } else { 2593 getParsingFlags(config).invalidMonth = input; 2594 } 2595}); 2596 2597// LOCALES 2598 2599var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; 2600var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); 2601function localeMonths (m, format) { 2602 if (!m) { 2603 return isArray(this._months) ? this._months : 2604 this._months['standalone']; 2605 } 2606 return isArray(this._months) ? this._months[m.month()] : 2607 this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; 2608} 2609 2610var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); 2611function localeMonthsShort (m, format) { 2612 if (!m) { 2613 return isArray(this._monthsShort) ? this._monthsShort : 2614 this._monthsShort['standalone']; 2615 } 2616 return isArray(this._monthsShort) ? this._monthsShort[m.month()] : 2617 this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; 2618} 2619 2620function handleStrictParse(monthName, format, strict) { 2621 var i, ii, mom, llc = monthName.toLocaleLowerCase(); 2622 if (!this._monthsParse) { 2623 // this is not used 2624 this._monthsParse = []; 2625 this._longMonthsParse = []; 2626 this._shortMonthsParse = []; 2627 for (i = 0; i < 12; ++i) { 2628 mom = createUTC([2000, i]); 2629 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); 2630 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); 2631 } 2632 } 2633 2634 if (strict) { 2635 if (format === 'MMM') { 2636 ii = indexOf.call(this._shortMonthsParse, llc); 2637 return ii !== -1 ? ii : null; 2638 } else { 2639 ii = indexOf.call(this._longMonthsParse, llc); 2640 return ii !== -1 ? ii : null; 2641 } 2642 } else { 2643 if (format === 'MMM') { 2644 ii = indexOf.call(this._shortMonthsParse, llc); 2645 if (ii !== -1) { 2646 return ii; 2647 } 2648 ii = indexOf.call(this._longMonthsParse, llc); 2649 return ii !== -1 ? ii : null; 2650 } else { 2651 ii = indexOf.call(this._longMonthsParse, llc); 2652 if (ii !== -1) { 2653 return ii; 2654 } 2655 ii = indexOf.call(this._shortMonthsParse, llc); 2656 return ii !== -1 ? ii : null; 2657 } 2658 } 2659} 2660 2661function localeMonthsParse (monthName, format, strict) { 2662 var i, mom, regex; 2663 2664 if (this._monthsParseExact) { 2665 return handleStrictParse.call(this, monthName, format, strict); 2666 } 2667 2668 if (!this._monthsParse) { 2669 this._monthsParse = []; 2670 this._longMonthsParse = []; 2671 this._shortMonthsParse = []; 2672 } 2673 2674 // TODO: add sorting 2675 // Sorting makes sure if one month (or abbr) is a prefix of another 2676 // see sorting in computeMonthsParse 2677 for (i = 0; i < 12; i++) { 2678 // make the regex if we don't have it already 2679 mom = createUTC([2000, i]); 2680 if (strict && !this._longMonthsParse[i]) { 2681 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); 2682 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); 2683 } 2684 if (!strict && !this._monthsParse[i]) { 2685 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); 2686 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); 2687 } 2688 // test the regex 2689 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { 2690 return i; 2691 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { 2692 return i; 2693 } else if (!strict && this._monthsParse[i].test(monthName)) { 2694 return i; 2695 } 2696 } 2697} 2698 2699// MOMENTS 2700 2701function setMonth (mom, value) { 2702 var dayOfMonth; 2703 2704 if (!mom.isValid()) { 2705 // No op 2706 return mom; 2707 } 2708 2709 if (typeof value === 'string') { 2710 if (/^\d+$/.test(value)) { 2711 value = toInt(value); 2712 } else { 2713 value = mom.localeData().monthsParse(value); 2714 // TODO: Another silent failure? 2715 if (!isNumber(value)) { 2716 return mom; 2717 } 2718 } 2719 } 2720 2721 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); 2722 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); 2723 return mom; 2724} 2725 2726function getSetMonth (value) { 2727 if (value != null) { 2728 setMonth(this, value); 2729 hooks.updateOffset(this, true); 2730 return this; 2731 } else { 2732 return get(this, 'Month'); 2733 } 2734} 2735 2736function getDaysInMonth () { 2737 return daysInMonth(this.year(), this.month()); 2738} 2739 2740var defaultMonthsShortRegex = matchWord; 2741function monthsShortRegex (isStrict) { 2742 if (this._monthsParseExact) { 2743 if (!hasOwnProp(this, '_monthsRegex')) { 2744 computeMonthsParse.call(this); 2745 } 2746 if (isStrict) { 2747 return this._monthsShortStrictRegex; 2748 } else { 2749 return this._monthsShortRegex; 2750 } 2751 } else { 2752 if (!hasOwnProp(this, '_monthsShortRegex')) { 2753 this._monthsShortRegex = defaultMonthsShortRegex; 2754 } 2755 return this._monthsShortStrictRegex && isStrict ? 2756 this._monthsShortStrictRegex : this._monthsShortRegex; 2757 } 2758} 2759 2760var defaultMonthsRegex = matchWord; 2761function monthsRegex (isStrict) { 2762 if (this._monthsParseExact) { 2763 if (!hasOwnProp(this, '_monthsRegex')) { 2764 computeMonthsParse.call(this); 2765 } 2766 if (isStrict) { 2767 return this._monthsStrictRegex; 2768 } else { 2769 return this._monthsRegex; 2770 } 2771 } else { 2772 if (!hasOwnProp(this, '_monthsRegex')) { 2773 this._monthsRegex = defaultMonthsRegex; 2774 } 2775 return this._monthsStrictRegex && isStrict ? 2776 this._monthsStrictRegex : this._monthsRegex; 2777 } 2778} 2779 2780function computeMonthsParse () { 2781 function cmpLenRev(a, b) { 2782 return b.length - a.length; 2783 } 2784 2785 var shortPieces = [], longPieces = [], mixedPieces = [], 2786 i, mom; 2787 for (i = 0; i < 12; i++) { 2788 // make the regex if we don't have it already 2789 mom = createUTC([2000, i]); 2790 shortPieces.push(this.monthsShort(mom, '')); 2791 longPieces.push(this.months(mom, '')); 2792 mixedPieces.push(this.months(mom, '')); 2793 mixedPieces.push(this.monthsShort(mom, '')); 2794 } 2795 // Sorting makes sure if one month (or abbr) is a prefix of another it 2796 // will match the longer piece. 2797 shortPieces.sort(cmpLenRev); 2798 longPieces.sort(cmpLenRev); 2799 mixedPieces.sort(cmpLenRev); 2800 for (i = 0; i < 12; i++) { 2801 shortPieces[i] = regexEscape(shortPieces[i]); 2802 longPieces[i] = regexEscape(longPieces[i]); 2803 } 2804 for (i = 0; i < 24; i++) { 2805 mixedPieces[i] = regexEscape(mixedPieces[i]); 2806 } 2807 2808 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 2809 this._monthsShortRegex = this._monthsRegex; 2810 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 2811 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 2812} 2813 2814function createDate (y, m, d, h, M, s, ms) { 2815 // can't just apply() to create a date: 2816 // https://stackoverflow.com/q/181348 2817 var date = new Date(y, m, d, h, M, s, ms); 2818 2819 // the date constructor remaps years 0-99 to 1900-1999 2820 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { 2821 date.setFullYear(y); 2822 } 2823 return date; 2824} 2825 2826function createUTCDate (y) { 2827 var date = new Date(Date.UTC.apply(null, arguments)); 2828 2829 // the Date.UTC function remaps years 0-99 to 1900-1999 2830 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { 2831 date.setUTCFullYear(y); 2832 } 2833 return date; 2834} 2835 2836// start-of-first-week - start-of-year 2837function firstWeekOffset(year, dow, doy) { 2838 var // first-week day -- which january is always in the first week (4 for iso, 1 for other) 2839 fwd = 7 + dow - doy, 2840 // first-week day local weekday -- which local weekday is fwd 2841 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; 2842 2843 return -fwdlw + fwd - 1; 2844} 2845 2846// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday 2847function dayOfYearFromWeeks(year, week, weekday, dow, doy) { 2848 var localWeekday = (7 + weekday - dow) % 7, 2849 weekOffset = firstWeekOffset(year, dow, doy), 2850 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, 2851 resYear, resDayOfYear; 2852 2853 if (dayOfYear <= 0) { 2854 resYear = year - 1; 2855 resDayOfYear = daysInYear(resYear) + dayOfYear; 2856 } else if (dayOfYear > daysInYear(year)) { 2857 resYear = year + 1; 2858 resDayOfYear = dayOfYear - daysInYear(year); 2859 } else { 2860 resYear = year; 2861 resDayOfYear = dayOfYear; 2862 } 2863 2864 return { 2865 year: resYear, 2866 dayOfYear: resDayOfYear 2867 }; 2868} 2869 2870function weekOfYear(mom, dow, doy) { 2871 var weekOffset = firstWeekOffset(mom.year(), dow, doy), 2872 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, 2873 resWeek, resYear; 2874 2875 if (week < 1) { 2876 resYear = mom.year() - 1; 2877 resWeek = week + weeksInYear(resYear, dow, doy); 2878 } else if (week > weeksInYear(mom.year(), dow, doy)) { 2879 resWeek = week - weeksInYear(mom.year(), dow, doy); 2880 resYear = mom.year() + 1; 2881 } else { 2882 resYear = mom.year(); 2883 resWeek = week; 2884 } 2885 2886 return { 2887 week: resWeek, 2888 year: resYear 2889 }; 2890} 2891 2892function weeksInYear(year, dow, doy) { 2893 var weekOffset = firstWeekOffset(year, dow, doy), 2894 weekOffsetNext = firstWeekOffset(year + 1, dow, doy); 2895 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; 2896} 2897 2898// FORMATTING 2899 2900addFormatToken('w', ['ww', 2], 'wo', 'week'); 2901addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); 2902 2903// ALIASES 2904 2905addUnitAlias('week', 'w'); 2906addUnitAlias('isoWeek', 'W'); 2907 2908// PRIORITIES 2909 2910addUnitPriority('week', 5); 2911addUnitPriority('isoWeek', 5); 2912 2913// PARSING 2914 2915addRegexToken('w', match1to2); 2916addRegexToken('ww', match1to2, match2); 2917addRegexToken('W', match1to2); 2918addRegexToken('WW', match1to2, match2); 2919 2920addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { 2921 week[token.substr(0, 1)] = toInt(input); 2922}); 2923 2924// HELPERS 2925 2926// LOCALES 2927 2928function localeWeek (mom) { 2929 return weekOfYear(mom, this._week.dow, this._week.doy).week; 2930} 2931 2932var defaultLocaleWeek = { 2933 dow : 0, // Sunday is the first day of the week. 2934 doy : 6 // The week that contains Jan 1st is the first week of the year. 2935}; 2936 2937function localeFirstDayOfWeek () { 2938 return this._week.dow; 2939} 2940 2941function localeFirstDayOfYear () { 2942 return this._week.doy; 2943} 2944 2945// MOMENTS 2946 2947function getSetWeek (input) { 2948 var week = this.localeData().week(this); 2949 return input == null ? week : this.add((input - week) * 7, 'd'); 2950} 2951 2952function getSetISOWeek (input) { 2953 var week = weekOfYear(this, 1, 4).week; 2954 return input == null ? week : this.add((input - week) * 7, 'd'); 2955} 2956 2957// FORMATTING 2958 2959addFormatToken('d', 0, 'do', 'day'); 2960 2961addFormatToken('dd', 0, 0, function (format) { 2962 return this.localeData().weekdaysMin(this, format); 2963}); 2964 2965addFormatToken('ddd', 0, 0, function (format) { 2966 return this.localeData().weekdaysShort(this, format); 2967}); 2968 2969addFormatToken('dddd', 0, 0, function (format) { 2970 return this.localeData().weekdays(this, format); 2971}); 2972 2973addFormatToken('e', 0, 0, 'weekday'); 2974addFormatToken('E', 0, 0, 'isoWeekday'); 2975 2976// ALIASES 2977 2978addUnitAlias('day', 'd'); 2979addUnitAlias('weekday', 'e'); 2980addUnitAlias('isoWeekday', 'E'); 2981 2982// PRIORITY 2983addUnitPriority('day', 11); 2984addUnitPriority('weekday', 11); 2985addUnitPriority('isoWeekday', 11); 2986 2987// PARSING 2988 2989addRegexToken('d', match1to2); 2990addRegexToken('e', match1to2); 2991addRegexToken('E', match1to2); 2992addRegexToken('dd', function (isStrict, locale) { 2993 return locale.weekdaysMinRegex(isStrict); 2994}); 2995addRegexToken('ddd', function (isStrict, locale) { 2996 return locale.weekdaysShortRegex(isStrict); 2997}); 2998addRegexToken('dddd', function (isStrict, locale) { 2999 return locale.weekdaysRegex(isStrict); 3000}); 3001 3002addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { 3003 var weekday = config._locale.weekdaysParse(input, token, config._strict); 3004 // if we didn't get a weekday name, mark the date as invalid 3005 if (weekday != null) { 3006 week.d = weekday; 3007 } else { 3008 getParsingFlags(config).invalidWeekday = input; 3009 } 3010}); 3011 3012addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { 3013 week[token] = toInt(input); 3014}); 3015 3016// HELPERS 3017 3018function parseWeekday(input, locale) { 3019 if (typeof input !== 'string') { 3020 return input; 3021 } 3022 3023 if (!isNaN(input)) { 3024 return parseInt(input, 10); 3025 } 3026 3027 input = locale.weekdaysParse(input); 3028 if (typeof input === 'number') { 3029 return input; 3030 } 3031 3032 return null; 3033} 3034 3035function parseIsoWeekday(input, locale) { 3036 if (typeof input === 'string') { 3037 return locale.weekdaysParse(input) % 7 || 7; 3038 } 3039 return isNaN(input) ? null : input; 3040} 3041 3042// LOCALES 3043 3044var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); 3045function localeWeekdays (m, format) { 3046 if (!m) { 3047 return isArray(this._weekdays) ? this._weekdays : 3048 this._weekdays['standalone']; 3049 } 3050 return isArray(this._weekdays) ? this._weekdays[m.day()] : 3051 this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; 3052} 3053 3054var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); 3055function localeWeekdaysShort (m) { 3056 return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; 3057} 3058 3059var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); 3060function localeWeekdaysMin (m) { 3061 return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; 3062} 3063 3064function handleStrictParse$1(weekdayName, format, strict) { 3065 var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); 3066 if (!this._weekdaysParse) { 3067 this._weekdaysParse = []; 3068 this._shortWeekdaysParse = []; 3069 this._minWeekdaysParse = []; 3070 3071 for (i = 0; i < 7; ++i) { 3072 mom = createUTC([2000, 1]).day(i); 3073 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); 3074 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); 3075 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); 3076 } 3077 } 3078 3079 if (strict) { 3080 if (format === 'dddd') { 3081 ii = indexOf.call(this._weekdaysParse, llc); 3082 return ii !== -1 ? ii : null; 3083 } else if (format === 'ddd') { 3084 ii = indexOf.call(this._shortWeekdaysParse, llc); 3085 return ii !== -1 ? ii : null; 3086 } else { 3087 ii = indexOf.call(this._minWeekdaysParse, llc); 3088 return ii !== -1 ? ii : null; 3089 } 3090 } else { 3091 if (format === 'dddd') { 3092 ii = indexOf.call(this._weekdaysParse, llc); 3093 if (ii !== -1) { 3094 return ii; 3095 } 3096 ii = indexOf.call(this._shortWeekdaysParse, llc); 3097 if (ii !== -1) { 3098 return ii; 3099 } 3100 ii = indexOf.call(this._minWeekdaysParse, llc); 3101 return ii !== -1 ? ii : null; 3102 } else if (format === 'ddd') { 3103 ii = indexOf.call(this._shortWeekdaysParse, llc); 3104 if (ii !== -1) { 3105 return ii; 3106 } 3107 ii = indexOf.call(this._weekdaysParse, llc); 3108 if (ii !== -1) { 3109 return ii; 3110 } 3111 ii = indexOf.call(this._minWeekdaysParse, llc); 3112 return ii !== -1 ? ii : null; 3113 } else { 3114 ii = indexOf.call(this._minWeekdaysParse, llc); 3115 if (ii !== -1) { 3116 return ii; 3117 } 3118 ii = indexOf.call(this._weekdaysParse, llc); 3119 if (ii !== -1) { 3120 return ii; 3121 } 3122 ii = indexOf.call(this._shortWeekdaysParse, llc); 3123 return ii !== -1 ? ii : null; 3124 } 3125 } 3126} 3127 3128function localeWeekdaysParse (weekdayName, format, strict) { 3129 var i, mom, regex; 3130 3131 if (this._weekdaysParseExact) { 3132 return handleStrictParse$1.call(this, weekdayName, format, strict); 3133 } 3134 3135 if (!this._weekdaysParse) { 3136 this._weekdaysParse = []; 3137 this._minWeekdaysParse = []; 3138 this._shortWeekdaysParse = []; 3139 this._fullWeekdaysParse = []; 3140 } 3141 3142 for (i = 0; i < 7; i++) { 3143 // make the regex if we don't have it already 3144 3145 mom = createUTC([2000, 1]).day(i); 3146 if (strict && !this._fullWeekdaysParse[i]) { 3147 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); 3148 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); 3149 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); 3150 } 3151 if (!this._weekdaysParse[i]) { 3152 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); 3153 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); 3154 } 3155 // test the regex 3156 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { 3157 return i; 3158 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { 3159 return i; 3160 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { 3161 return i; 3162 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { 3163 return i; 3164 } 3165 } 3166} 3167 3168// MOMENTS 3169 3170function getSetDayOfWeek (input) { 3171 if (!this.isValid()) { 3172 return input != null ? this : NaN; 3173 } 3174 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); 3175 if (input != null) { 3176 input = parseWeekday(input, this.localeData()); 3177 return this.add(input - day, 'd'); 3178 } else { 3179 return day; 3180 } 3181} 3182 3183function getSetLocaleDayOfWeek (input) { 3184 if (!this.isValid()) { 3185 return input != null ? this : NaN; 3186 } 3187 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; 3188 return input == null ? weekday : this.add(input - weekday, 'd'); 3189} 3190 3191function getSetISODayOfWeek (input) { 3192 if (!this.isValid()) { 3193 return input != null ? this : NaN; 3194 } 3195 3196 // behaves the same as moment#day except 3197 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) 3198 // as a setter, sunday should belong to the previous week. 3199 3200 if (input != null) { 3201 var weekday = parseIsoWeekday(input, this.localeData()); 3202 return this.day(this.day() % 7 ? weekday : weekday - 7); 3203 } else { 3204 return this.day() || 7; 3205 } 3206} 3207 3208var defaultWeekdaysRegex = matchWord; 3209function weekdaysRegex (isStrict) { 3210 if (this._weekdaysParseExact) { 3211 if (!hasOwnProp(this, '_weekdaysRegex')) { 3212 computeWeekdaysParse.call(this); 3213 } 3214 if (isStrict) { 3215 return this._weekdaysStrictRegex; 3216 } else { 3217 return this._weekdaysRegex; 3218 } 3219 } else { 3220 if (!hasOwnProp(this, '_weekdaysRegex')) { 3221 this._weekdaysRegex = defaultWeekdaysRegex; 3222 } 3223 return this._weekdaysStrictRegex && isStrict ? 3224 this._weekdaysStrictRegex : this._weekdaysRegex; 3225 } 3226} 3227 3228var defaultWeekdaysShortRegex = matchWord; 3229function weekdaysShortRegex (isStrict) { 3230 if (this._weekdaysParseExact) { 3231 if (!hasOwnProp(this, '_weekdaysRegex')) { 3232 computeWeekdaysParse.call(this); 3233 } 3234 if (isStrict) { 3235 return this._weekdaysShortStrictRegex; 3236 } else { 3237 return this._weekdaysShortRegex; 3238 } 3239 } else { 3240 if (!hasOwnProp(this, '_weekdaysShortRegex')) { 3241 this._weekdaysShortRegex = defaultWeekdaysShortRegex; 3242 } 3243 return this._weekdaysShortStrictRegex && isStrict ? 3244 this._weekdaysShortStrictRegex : this._weekdaysShortRegex; 3245 } 3246} 3247 3248var defaultWeekdaysMinRegex = matchWord; 3249function weekdaysMinRegex (isStrict) { 3250 if (this._weekdaysParseExact) { 3251 if (!hasOwnProp(this, '_weekdaysRegex')) { 3252 computeWeekdaysParse.call(this); 3253 } 3254 if (isStrict) { 3255 return this._weekdaysMinStrictRegex; 3256 } else { 3257 return this._weekdaysMinRegex; 3258 } 3259 } else { 3260 if (!hasOwnProp(this, '_weekdaysMinRegex')) { 3261 this._weekdaysMinRegex = defaultWeekdaysMinRegex; 3262 } 3263 return this._weekdaysMinStrictRegex && isStrict ? 3264 this._weekdaysMinStrictRegex : this._weekdaysMinRegex; 3265 } 3266} 3267 3268 3269function computeWeekdaysParse () { 3270 function cmpLenRev(a, b) { 3271 return b.length - a.length; 3272 } 3273 3274 var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], 3275 i, mom, minp, shortp, longp; 3276 for (i = 0; i < 7; i++) { 3277 // make the regex if we don't have it already 3278 mom = createUTC([2000, 1]).day(i); 3279 minp = this.weekdaysMin(mom, ''); 3280 shortp = this.weekdaysShort(mom, ''); 3281 longp = this.weekdays(mom, ''); 3282 minPieces.push(minp); 3283 shortPieces.push(shortp); 3284 longPieces.push(longp); 3285 mixedPieces.push(minp); 3286 mixedPieces.push(shortp); 3287 mixedPieces.push(longp); 3288 } 3289 // Sorting makes sure if one weekday (or abbr) is a prefix of another it 3290 // will match the longer piece. 3291 minPieces.sort(cmpLenRev); 3292 shortPieces.sort(cmpLenRev); 3293 longPieces.sort(cmpLenRev); 3294 mixedPieces.sort(cmpLenRev); 3295 for (i = 0; i < 7; i++) { 3296 shortPieces[i] = regexEscape(shortPieces[i]); 3297 longPieces[i] = regexEscape(longPieces[i]); 3298 mixedPieces[i] = regexEscape(mixedPieces[i]); 3299 } 3300 3301 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); 3302 this._weekdaysShortRegex = this._weekdaysRegex; 3303 this._weekdaysMinRegex = this._weekdaysRegex; 3304 3305 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); 3306 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); 3307 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); 3308} 3309 3310// FORMATTING 3311 3312function hFormat() { 3313 return this.hours() % 12 || 12; 3314} 3315 3316function kFormat() { 3317 return this.hours() || 24; 3318} 3319 3320addFormatToken('H', ['HH', 2], 0, 'hour'); 3321addFormatToken('h', ['hh', 2], 0, hFormat); 3322addFormatToken('k', ['kk', 2], 0, kFormat); 3323 3324addFormatToken('hmm', 0, 0, function () { 3325 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); 3326}); 3327 3328addFormatToken('hmmss', 0, 0, function () { 3329 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + 3330 zeroFill(this.seconds(), 2); 3331}); 3332 3333addFormatToken('Hmm', 0, 0, function () { 3334 return '' + this.hours() + zeroFill(this.minutes(), 2); 3335}); 3336 3337addFormatToken('Hmmss', 0, 0, function () { 3338 return '' + this.hours() + zeroFill(this.minutes(), 2) + 3339 zeroFill(this.seconds(), 2); 3340}); 3341 3342function meridiem (token, lowercase) { 3343 addFormatToken(token, 0, 0, function () { 3344 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); 3345 }); 3346} 3347 3348meridiem('a', true); 3349meridiem('A', false); 3350 3351// ALIASES 3352 3353addUnitAlias('hour', 'h'); 3354 3355// PRIORITY 3356addUnitPriority('hour', 13); 3357 3358// PARSING 3359 3360function matchMeridiem (isStrict, locale) { 3361 return locale._meridiemParse; 3362} 3363 3364addRegexToken('a', matchMeridiem); 3365addRegexToken('A', matchMeridiem); 3366addRegexToken('H', match1to2); 3367addRegexToken('h', match1to2); 3368addRegexToken('k', match1to2); 3369addRegexToken('HH', match1to2, match2); 3370addRegexToken('hh', match1to2, match2); 3371addRegexToken('kk', match1to2, match2); 3372
vendor: 18,399 bytes, lines 3373-3945
3373addRegexToken('hmm', match3to4); 3374addRegexToken('hmmss', match5to6); 3375addRegexToken('Hmm', match3to4); 3376addRegexToken('Hmmss', match5to6); 3377 3378addParseToken(['H', 'HH'], HOUR); 3379addParseToken(['k', 'kk'], function (input, array, config) { 3380 var kInput = toInt(input); 3381 array[HOUR] = kInput === 24 ? 0 : kInput; 3382}); 3383addParseToken(['a', 'A'], function (input, array, config) { 3384 config._isPm = config._locale.isPM(input); 3385 config._meridiem = input; 3386}); 3387addParseToken(['h', 'hh'], function (input, array, config) { 3388 array[HOUR] = toInt(input); 3389 getParsingFlags(config).bigHour = true; 3390}); 3391addParseToken('hmm', function (input, array, config) { 3392 var pos = input.length - 2; 3393 array[HOUR] = toInt(input.substr(0, pos)); 3394 array[MINUTE] = toInt(input.substr(pos)); 3395 getParsingFlags(config).bigHour = true; 3396}); 3397addParseToken('hmmss', function (input, array, config) { 3398 var pos1 = input.length - 4; 3399 var pos2 = input.length - 2; 3400 array[HOUR] = toInt(input.substr(0, pos1)); 3401 array[MINUTE] = toInt(input.substr(pos1, 2)); 3402 array[SECOND] = toInt(input.substr(pos2)); 3403 getParsingFlags(config).bigHour = true; 3404}); 3405addParseToken('Hmm', function (input, array, config) { 3406 var pos = input.length - 2; 3407 array[HOUR] = toInt(input.substr(0, pos)); 3408 array[MINUTE] = toInt(input.substr(pos)); 3409}); 3410addParseToken('Hmmss', function (input, array, config) { 3411 var pos1 = input.length - 4; 3412 var pos2 = input.length - 2; 3413 array[HOUR] = toInt(input.substr(0, pos1)); 3414 array[MINUTE] = toInt(input.substr(pos1, 2)); 3415 array[SECOND] = toInt(input.substr(pos2)); 3416}); 3417 3418// LOCALES 3419 3420function localeIsPM (input) { 3421 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays 3422 // Using charAt should be more compatible. 3423 return ((input + '').toLowerCase().charAt(0) === 'p'); 3424} 3425 3426var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; 3427function localeMeridiem (hours, minutes, isLower) { 3428 if (hours > 11) { 3429 return isLower ? 'pm' : 'PM'; 3430 } else { 3431 return isLower ? 'am' : 'AM'; 3432 } 3433} 3434 3435 3436// MOMENTS 3437 3438// Setting the hour should keep the time, because the user explicitly 3439// specified which hour he wants. So trying to maintain the same hour (in 3440// a new timezone) makes sense. Adding/subtracting hours does not follow 3441// this rule. 3442var getSetHour = makeGetSet('Hours', true); 3443 3444// months 3445// week 3446// weekdays 3447// meridiem 3448var baseConfig = { 3449 calendar: defaultCalendar, 3450 longDateFormat: defaultLongDateFormat, 3451 invalidDate: defaultInvalidDate, 3452 ordinal: defaultOrdinal, 3453 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, 3454 relativeTime: defaultRelativeTime, 3455 3456 months: defaultLocaleMonths, 3457 monthsShort: defaultLocaleMonthsShort, 3458 3459 week: defaultLocaleWeek, 3460 3461 weekdays: defaultLocaleWeekdays, 3462 weekdaysMin: defaultLocaleWeekdaysMin, 3463 weekdaysShort: defaultLocaleWeekdaysShort, 3464 3465 meridiemParse: defaultLocaleMeridiemParse 3466}; 3467 3468// internal storage for locale config files 3469var locales = {}; 3470var localeFamilies = {}; 3471var globalLocale; 3472 3473function normalizeLocale(key) { 3474 return key ? key.toLowerCase().replace('_', '-') : key; 3475} 3476 3477// pick the locale from the array 3478// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each 3479// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root 3480function chooseLocale(names) { 3481 var i = 0, j, next, locale, split; 3482 3483 while (i < names.length) { 3484 split = normalizeLocale(names[i]).split('-'); 3485 j = split.length; 3486 next = normalizeLocale(names[i + 1]); 3487 next = next ? next.split('-') : null; 3488 while (j > 0) { 3489 locale = loadLocale(split.slice(0, j).join('-')); 3490 if (locale) { 3491 return locale; 3492 } 3493 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { 3494 //the next array item is better than a shallower substring of this one 3495 break; 3496 } 3497 j--; 3498 } 3499 i++; 3500 } 3501 return null; 3502} 3503 3504function loadLocale(name) { 3505 var oldLocale = null; 3506 // TODO: Find a better way to register and load all the locales in Node 3507 if (!locales[name] && (typeof module !== 'undefined') && 3508 module && module.exports) { 3509 try { 3510 oldLocale = globalLocale._abbr; 3511 var aliasedRequire = require; 3512 aliasedRequire('./locale/' + name); 3513 getSetGlobalLocale(oldLocale); 3514 } catch (e) {} 3515 } 3516 return locales[name]; 3517} 3518 3519// This function will load locale and then set the global locale. If 3520// no arguments are passed in, it will simply return the current global 3521// locale key. 3522function getSetGlobalLocale (key, values) { 3523 var data; 3524 if (key) { 3525 if (isUndefined(values)) { 3526 data = getLocale(key); 3527 } 3528 else { 3529 data = defineLocale(key, values); 3530 } 3531 3532 if (data) { 3533 // moment.duration._locale = moment._locale = data; 3534 globalLocale = data; 3535 } 3536 } 3537 3538 return globalLocale._abbr; 3539} 3540 3541function defineLocale (name, config) { 3542 if (config !== null) { 3543 var parentConfig = baseConfig; 3544 config.abbr = name; 3545 if (locales[name] != null) { 3546 deprecateSimple('defineLocaleOverride', 3547 'use moment.updateLocale(localeName, config) to change ' + 3548 'an existing locale. moment.defineLocale(localeName, ' + 3549 'config) should only be used for creating a new locale ' + 3550 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); 3551 parentConfig = locales[name]._config; 3552 } else if (config.parentLocale != null) { 3553 if (locales[config.parentLocale] != null) { 3554 parentConfig = locales[config.parentLocale]._config; 3555 } else { 3556 if (!localeFamilies[config.parentLocale]) { 3557 localeFamilies[config.parentLocale] = []; 3558 } 3559 localeFamilies[config.parentLocale].push({ 3560 name: name, 3561 config: config 3562 }); 3563 return null; 3564 } 3565 } 3566 locales[name] = new Locale(mergeConfigs(parentConfig, config)); 3567 3568 if (localeFamilies[name]) { 3569 localeFamilies[name].forEach(function (x) { 3570 defineLocale(x.name, x.config); 3571 }); 3572 } 3573 3574 // backwards compat for now: also set the locale 3575 // make sure we set the locale AFTER all child locales have been 3576 // created, so we won't end up with the child locale set. 3577 getSetGlobalLocale(name); 3578 3579 3580 return locales[name]; 3581 } else { 3582 // useful for testing 3583 delete locales[name]; 3584 return null; 3585 } 3586} 3587 3588function updateLocale(name, config) { 3589 if (config != null) { 3590 var locale, tmpLocale, parentConfig = baseConfig; 3591 // MERGE 3592 tmpLocale = loadLocale(name); 3593 if (tmpLocale != null) { 3594 parentConfig = tmpLocale._config; 3595 } 3596 config = mergeConfigs(parentConfig, config); 3597 locale = new Locale(config); 3598 locale.parentLocale = locales[name]; 3599 locales[name] = locale; 3600 3601 // backwards compat for now: also set the locale 3602 getSetGlobalLocale(name); 3603 } else { 3604 // pass null for config to unupdate, useful for tests 3605 if (locales[name] != null) { 3606 if (locales[name].parentLocale != null) { 3607 locales[name] = locales[name].parentLocale; 3608 } else if (locales[name] != null) { 3609 delete locales[name]; 3610 } 3611 } 3612 } 3613 return locales[name]; 3614} 3615 3616// returns locale data 3617function getLocale (key) { 3618 var locale; 3619 3620 if (key && key._locale && key._locale._abbr) { 3621 key = key._locale._abbr; 3622 } 3623 3624 if (!key) { 3625 return globalLocale; 3626 } 3627 3628 if (!isArray(key)) { 3629 //short-circuit everything else 3630 locale = loadLocale(key); 3631 if (locale) { 3632 return locale; 3633 } 3634 key = [key]; 3635 } 3636 3637 return chooseLocale(key); 3638} 3639 3640function listLocales() { 3641 return keys(locales); 3642} 3643 3644function checkOverflow (m) { 3645 var overflow; 3646 var a = m._a; 3647 3648 if (a && getParsingFlags(m).overflow === -2) { 3649 overflow = 3650 a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : 3651 a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : 3652 a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : 3653 a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : 3654 a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : 3655 a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : 3656 -1; 3657 3658 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { 3659 overflow = DATE; 3660 } 3661 if (getParsingFlags(m)._overflowWeeks && overflow === -1) { 3662 overflow = WEEK; 3663 } 3664 if (getParsingFlags(m)._overflowWeekday && overflow === -1) { 3665 overflow = WEEKDAY; 3666 } 3667 3668 getParsingFlags(m).overflow = overflow; 3669 } 3670 3671 return m; 3672} 3673 3674// Pick the first defined of two or three arguments. 3675function defaults(a, b, c) { 3676 if (a != null) { 3677 return a; 3678 } 3679 if (b != null) { 3680 return b; 3681 } 3682 return c; 3683} 3684 3685function currentDateArray(config) { 3686 // hooks is actually the exported moment object 3687 var nowValue = new Date(hooks.now()); 3688 if (config._useUTC) { 3689 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; 3690 } 3691 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; 3692} 3693 3694// convert an array to a date. 3695// the array should mirror the parameters below 3696// note: all values past the year are optional and will default to the lowest possible value. 3697// [year, month, day , hour, minute, second, millisecond] 3698function configFromArray (config) { 3699 var i, date, input = [], currentDate, expectedWeekday, yearToUse; 3700 3701 if (config._d) { 3702 return; 3703 } 3704 3705 currentDate = currentDateArray(config); 3706 3707 //compute day of the year from weeks and weekdays 3708 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { 3709 dayOfYearFromWeekInfo(config); 3710 } 3711 3712 //if the day of the year is set, figure out what it is 3713 if (config._dayOfYear != null) { 3714 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); 3715 3716 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { 3717 getParsingFlags(config)._overflowDayOfYear = true; 3718 } 3719 3720 date = createUTCDate(yearToUse, 0, config._dayOfYear); 3721 config._a[MONTH] = date.getUTCMonth(); 3722 config._a[DATE] = date.getUTCDate(); 3723 } 3724 3725 // Default to current date. 3726 // * if no year, month, day of month are given, default to today 3727 // * if day of month is given, default month and year 3728 // * if month is given, default only year 3729 // * if year is given, don't default anything 3730 for (i = 0; i < 3 && config._a[i] == null; ++i) { 3731 config._a[i] = input[i] = currentDate[i]; 3732 } 3733 3734 // Zero out whatever was not defaulted, including time 3735 for (; i < 7; i++) { 3736 config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; 3737 } 3738 3739 // Check for 24:00:00.000 3740 if (config._a[HOUR] === 24 && 3741 config._a[MINUTE] === 0 && 3742 config._a[SECOND] === 0 && 3743 config._a[MILLISECOND] === 0) { 3744 config._nextDay = true; 3745 config._a[HOUR] = 0; 3746 } 3747 3748 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); 3749 expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); 3750 3751 // Apply timezone offset from input. The actual utcOffset can be changed 3752 // with parseZone. 3753 if (config._tzm != null) { 3754 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 3755 } 3756 3757 if (config._nextDay) { 3758 config._a[HOUR] = 24; 3759 } 3760 3761 // check for mismatching day of week 3762 if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { 3763 getParsingFlags(config).weekdayMismatch = true; 3764 } 3765} 3766 3767function dayOfYearFromWeekInfo(config) { 3768 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; 3769 3770 w = config._w; 3771 if (w.GG != null || w.W != null || w.E != null) { 3772 dow = 1; 3773 doy = 4; 3774 3775 // TODO: We need to take the current isoWeekYear, but that depends on 3776 // how we interpret now (local, utc, fixed offset). So create 3777 // a now version of current config (take local/utc/offset flags, and 3778 // create now). 3779 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); 3780 week = defaults(w.W, 1); 3781 weekday = defaults(w.E, 1); 3782 if (weekday < 1 || weekday > 7) { 3783 weekdayOverflow = true; 3784 } 3785 } else { 3786 dow = config._locale._week.dow; 3787 doy = config._locale._week.doy; 3788 3789 var curWeek = weekOfYear(createLocal(), dow, doy); 3790 3791 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); 3792 3793 // Default to current week. 3794 week = defaults(w.w, curWeek.week); 3795 3796 if (w.d != null) { 3797 // weekday -- low day numbers are considered next week 3798 weekday = w.d; 3799 if (weekday < 0 || weekday > 6) { 3800 weekdayOverflow = true; 3801 } 3802 } else if (w.e != null) { 3803 // local weekday -- counting starts from begining of week 3804 weekday = w.e + dow; 3805 if (w.e < 0 || w.e > 6) { 3806 weekdayOverflow = true; 3807 } 3808 } else { 3809 // default to begining of week 3810 weekday = dow; 3811 } 3812 } 3813 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { 3814 getParsingFlags(config)._overflowWeeks = true; 3815 } else if (weekdayOverflow != null) { 3816 getParsingFlags(config)._overflowWeekday = true; 3817 } else { 3818 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); 3819 config._a[YEAR] = temp.year; 3820 config._dayOfYear = temp.dayOfYear; 3821 } 3822} 3823 3824// iso 8601 regex 3825// 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) 3826var 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)?)?$/; 3827var 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)?)?$/; 3828 3829var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; 3830 3831var isoDates = [ 3832 ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], 3833 ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], 3834 ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], 3835 ['GGGG-[W]WW', /\d{4}-W\d\d/, false], 3836 ['YYYY-DDD', /\d{4}-\d{3}/], 3837 ['YYYY-MM', /\d{4}-\d\d/, false], 3838 ['YYYYYYMMDD', /[+-]\d{10}/], 3839 ['YYYYMMDD', /\d{8}/], 3840 // YYYYMM is NOT allowed by the standard 3841 ['GGGG[W]WWE', /\d{4}W\d{3}/], 3842 ['GGGG[W]WW', /\d{4}W\d{2}/, false], 3843 ['YYYYDDD', /\d{7}/] 3844]; 3845 3846// iso time formats and regexes 3847var isoTimes = [ 3848 ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], 3849 ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], 3850 ['HH:mm:ss', /\d\d:\d\d:\d\d/], 3851 ['HH:mm', /\d\d:\d\d/], 3852 ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], 3853 ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], 3854 ['HHmmss', /\d\d\d\d\d\d/], 3855 ['HHmm', /\d\d\d\d/], 3856 ['HH', /\d\d/] 3857]; 3858 3859var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; 3860 3861// date from iso format 3862function configFromISO(config) { 3863 var i, l, 3864 string = config._i, 3865 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), 3866 allowTime, dateFormat, timeFormat, tzFormat; 3867 3868 if (match) { 3869 getParsingFlags(config).iso = true; 3870 3871 for (i = 0, l = isoDates.length; i < l; i++) { 3872 if (isoDates[i][1].exec(match[1])) { 3873 dateFormat = isoDates[i][0]; 3874 allowTime = isoDates[i][2] !== false; 3875 break; 3876 } 3877 } 3878 if (dateFormat == null) { 3879 config._isValid = false; 3880 return; 3881 } 3882 if (match[3]) { 3883 for (i = 0, l = isoTimes.length; i < l; i++) { 3884 if (isoTimes[i][1].exec(match[3])) { 3885 // match[2] should be 'T' or space 3886 timeFormat = (match[2] || ' ') + isoTimes[i][0]; 3887 break; 3888 } 3889 } 3890 if (timeFormat == null) { 3891 config._isValid = false; 3892 return; 3893 } 3894 } 3895 if (!allowTime && timeFormat != null) { 3896 config._isValid = false; 3897 return; 3898 } 3899 if (match[4]) { 3900 if (tzRegex.exec(match[4])) { 3901 tzFormat = 'Z'; 3902 } else { 3903 config._isValid = false; 3904 return; 3905 } 3906 } 3907 config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); 3908 configFromStringAndFormat(config); 3909 } else { 3910 config._isValid = false; 3911 } 3912} 3913 3914// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 3915var 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}))$/; 3916 3917function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { 3918 var result = [ 3919 untruncateYear(yearStr), 3920 defaultLocaleMonthsShort.indexOf(monthStr), 3921 parseInt(dayStr, 10), 3922 parseInt(hourStr, 10), 3923 parseInt(minuteStr, 10) 3924 ]; 3925 3926 if (secondStr) { 3927 result.push(parseInt(secondStr, 10)); 3928 } 3929 3930 return result; 3931} 3932 3933function untruncateYear(yearStr) { 3934 var year = parseInt(yearStr, 10); 3935 if (year <= 49) { 3936 return 2000 + year; 3937 } else if (year <= 999) { 3938 return 1900 + year; 3939 } 3940 return year; 3941} 3942 3943function preprocessRFC2822(s) { 3944 // Remove comments and folding whitespace and replace multiple-spaces with a single space 3945 return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim(
vendor: 6,917 bytes, lines 3945-4174
3945); 3946} 3947 3948function checkWeekday(weekdayStr, parsedInput, config) { 3949 if (weekdayStr) { 3950 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. 3951 var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), 3952 weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); 3953 if (weekdayProvided !== weekdayActual) { 3954 getParsingFlags(config).weekdayMismatch = true; 3955 config._isValid = false; 3956 return false; 3957 } 3958 } 3959 return true; 3960} 3961 3962var obsOffsets = { 3963 UT: 0, 3964 GMT: 0, 3965 EDT: -4 * 60, 3966 EST: -5 * 60, 3967 CDT: -5 * 60, 3968 CST: -6 * 60, 3969 MDT: -6 * 60, 3970 MST: -7 * 60, 3971 PDT: -7 * 60, 3972 PST: -8 * 60 3973}; 3974 3975function calculateOffset(obsOffset, militaryOffset, numOffset) { 3976 if (obsOffset) { 3977 return obsOffsets[obsOffset]; 3978 } else if (militaryOffset) { 3979 // the only allowed military tz is Z 3980 return 0; 3981 } else { 3982 var hm = parseInt(numOffset, 10); 3983 var m = hm % 100, h = (hm - m) / 100; 3984 return h * 60 + m; 3985 } 3986} 3987 3988// date and time from ref 2822 format 3989function configFromRFC2822(config) { 3990 var match = rfc2822.exec(preprocessRFC2822(config._i)); 3991 if (match) { 3992 var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); 3993 if (!checkWeekday(match[1], parsedArray, config)) { 3994 return; 3995 } 3996 3997 config._a = parsedArray; 3998 config._tzm = calculateOffset(match[8], match[9], match[10]); 3999 4000 config._d = createUTCDate.apply(null, config._a); 4001 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); 4002 4003 getParsingFlags(config).rfc2822 = true; 4004 } else { 4005 config._isValid = false; 4006 } 4007} 4008 4009// date from iso format or fallback 4010function configFromString(config) { 4011 var matched = aspNetJsonRegex.exec(config._i); 4012 4013 if (matched !== null) { 4014 config._d = new Date(+matched[1]); 4015 return; 4016 } 4017 4018 configFromISO(config); 4019 if (config._isValid === false) { 4020 delete config._isValid; 4021 } else { 4022 return; 4023 } 4024 4025 configFromRFC2822(config); 4026 if (config._isValid === false) { 4027 delete config._isValid; 4028 } else { 4029 return; 4030 } 4031 4032 // Final attempt, use Input Fallback 4033 hooks.createFromInputFallback(config); 4034} 4035 4036hooks.createFromInputFallback = deprecate( 4037 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 4038 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 4039 'discouraged and will be removed in an upcoming major release. Please refer to ' + 4040 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', 4041 function (config) { 4042 config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); 4043 } 4044); 4045 4046// constant that refers to the ISO standard 4047hooks.ISO_8601 = function () {}; 4048 4049// constant that refers to the RFC 2822 form 4050hooks.RFC_2822 = function () {}; 4051 4052// date from string and format string 4053function configFromStringAndFormat(config) { 4054 // TODO: Move this to another part of the creation flow to prevent circular deps 4055 if (config._f === hooks.ISO_8601) { 4056 configFromISO(config); 4057 return; 4058 } 4059 if (config._f === hooks.RFC_2822) { 4060 configFromRFC2822(config); 4061 return; 4062 } 4063 config._a = []; 4064 getParsingFlags(config).empty = true; 4065 4066 // This array is used to make a Date, either with `new Date` or `Date.UTC` 4067 var string = '' + config._i, 4068 i, parsedInput, tokens, token, skipped, 4069 stringLength = string.length, 4070 totalParsedInputLength = 0; 4071 4072 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; 4073 4074 for (i = 0; i < tokens.length; i++) { 4075 token = tokens[i]; 4076 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; 4077 // console.log('token', token, 'parsedInput', parsedInput, 4078 // 'regex', getParseRegexForToken(token, config)); 4079 if (parsedInput) { 4080 skipped = string.substr(0, string.indexOf(parsedInput)); 4081 if (skipped.length > 0) { 4082 getParsingFlags(config).unusedInput.push(skipped); 4083 } 4084 string = string.slice(string.indexOf(parsedInput) + parsedInput.length); 4085 totalParsedInputLength += parsedInput.length; 4086 } 4087 // don't parse if it's not a known token 4088 if (formatTokenFunctions[token]) { 4089 if (parsedInput) { 4090 getParsingFlags(config).empty = false; 4091 } 4092 else { 4093 getParsingFlags(config).unusedTokens.push(token); 4094 } 4095 addTimeToArrayFromToken(token, parsedInput, config); 4096 } 4097 else if (config._strict && !parsedInput) { 4098 getParsingFlags(config).unusedTokens.push(token); 4099 } 4100 } 4101 4102 // add remaining unparsed input length to the string 4103 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; 4104 if (string.length > 0) { 4105 getParsingFlags(config).unusedInput.push(string); 4106 } 4107 4108 // clear _12h flag if hour is <= 12 4109 if (config._a[HOUR] <= 12 && 4110 getParsingFlags(config).bigHour === true && 4111 config._a[HOUR] > 0) { 4112 getParsingFlags(config).bigHour = undefined; 4113 } 4114 4115 getParsingFlags(config).parsedDateParts = config._a.slice(0); 4116 getParsingFlags(config).meridiem = config._meridiem; 4117 // handle meridiem 4118 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); 4119 4120 configFromArray(config); 4121 checkOverflow(config); 4122} 4123 4124 4125function meridiemFixWrap (locale, hour, meridiem) { 4126 var isPm; 4127 4128 if (meridiem == null) { 4129 // nothing to do 4130 return hour; 4131 } 4132 if (locale.meridiemHour != null) { 4133 return locale.meridiemHour(hour, meridiem); 4134 } else if (locale.isPM != null) { 4135 // Fallback 4136 isPm = locale.isPM(meridiem); 4137 if (isPm && hour < 12) { 4138 hour += 12; 4139 } 4140 if (!isPm && hour === 12) { 4141 hour = 0; 4142 } 4143 return hour; 4144 } else { 4145 // this is not supposed to happen 4146 return hour; 4147 } 4148} 4149 4150// date from string and array of format strings 4151function configFromStringAndArray(config) { 4152 var tempConfig, 4153 bestMoment, 4154 4155 scoreToBeat, 4156 i, 4157 currentScore; 4158 4159 if (config._f.length === 0) { 4160 getParsingFlags(config).invalidFormat = true; 4161 config._d = new Date(NaN); 4162 return; 4163 } 4164 4165 for (i = 0; i < config._f.length; i++) { 4166 currentScore = 0; 4167 tempConfig = copyConfig({}, config); 4168 if (config._useUTC != null) { 4169 tempConfig._useUTC = config._useUTC; 4170 } 4171 tempConfig._f = config._f[i]; 4172 configFromStringAndFormat(tempConfig); 4173 4174 if (!isValid
vendor: 9,914 bytes, lines 4174-4530
4174(tempConfig)) { 4175 continue; 4176 } 4177 4178 // if there is any input that was not parsed add a penalty for that format 4179 currentScore += getParsingFlags(tempConfig).charsLeftOver; 4180 4181 //or tokens 4182 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; 4183 4184 getParsingFlags(tempConfig).score = currentScore; 4185 4186 if (scoreToBeat == null || currentScore < scoreToBeat) { 4187 scoreToBeat = currentScore; 4188 bestMoment = tempConfig; 4189 } 4190 } 4191 4192 extend(config, bestMoment || tempConfig); 4193} 4194 4195function configFromObject(config) { 4196 if (config._d) { 4197 return; 4198 } 4199 4200 var i = normalizeObjectUnits(config._i); 4201 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { 4202 return obj && parseInt(obj, 10); 4203 }); 4204 4205 configFromArray(config); 4206} 4207 4208function createFromConfig (config) { 4209 var res = new Moment(checkOverflow(prepareConfig(config))); 4210 if (res._nextDay) { 4211 // Adding is smart enough around DST 4212 res.add(1, 'd'); 4213 res._nextDay = undefined; 4214 } 4215 4216 return res; 4217} 4218 4219function prepareConfig (config) { 4220 var input = config._i, 4221 format = config._f; 4222 4223 config._locale = config._locale || getLocale(config._l); 4224 4225 if (input === null || (format === undefined && input === '')) { 4226 return createInvalid({nullInput: true}); 4227 } 4228 4229 if (typeof input === 'string') { 4230 config._i = input = config._locale.preparse(input); 4231 } 4232 4233 if (isMoment(input)) { 4234 return new Moment(checkOverflow(input)); 4235 } else if (isDate(input)) { 4236 config._d = input; 4237 } else if (isArray(format)) { 4238 configFromStringAndArray(config); 4239 } else if (format) { 4240 configFromStringAndFormat(config); 4241 } else { 4242 configFromInput(config); 4243 } 4244 4245 if (!isValid(config)) { 4246 config._d = null; 4247 } 4248 4249 return config; 4250} 4251 4252function configFromInput(config) { 4253 var input = config._i; 4254 if (isUndefined(input)) { 4255 config._d = new Date(hooks.now()); 4256 } else if (isDate(input)) { 4257 config._d = new Date(input.valueOf()); 4258 } else if (typeof input === 'string') { 4259 configFromString(config); 4260 } else if (isArray(input)) { 4261 config._a = map(input.slice(0), function (obj) { 4262 return parseInt(obj, 10); 4263 }); 4264 configFromArray(config); 4265 } else if (isObject(input)) { 4266 configFromObject(config); 4267 } else if (isNumber(input)) { 4268 // from milliseconds 4269 config._d = new Date(input); 4270 } else { 4271 hooks.createFromInputFallback(config); 4272 } 4273} 4274 4275function createLocalOrUTC (input, format, locale, strict, isUTC) { 4276 var c = {}; 4277 4278 if (locale === true || locale === false) { 4279 strict = locale; 4280 locale = undefined; 4281 } 4282 4283 if ((isObject(input) && isObjectEmpty(input)) || 4284 (isArray(input) && input.length === 0)) { 4285 input = undefined; 4286 } 4287 // object construction must be done this way. 4288 // https://github.com/moment/moment/issues/1423 4289 c._isAMomentObject = true; 4290 c._useUTC = c._isUTC = isUTC; 4291 c._l = locale; 4292 c._i = input; 4293 c._f = format; 4294 c._strict = strict; 4295 4296 return createFromConfig(c); 4297} 4298 4299function createLocal (input, format, locale, strict) { 4300 return createLocalOrUTC(input, format, locale, strict, false); 4301} 4302 4303var prototypeMin = deprecate( 4304 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', 4305 function () { 4306 var other = createLocal.apply(null, arguments); 4307 if (this.isValid() && other.isValid()) { 4308 return other < this ? this : other; 4309 } else { 4310 return createInvalid(); 4311 } 4312 } 4313); 4314 4315var prototypeMax = deprecate( 4316 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', 4317 function () { 4318 var other = createLocal.apply(null, arguments); 4319 if (this.isValid() && other.isValid()) { 4320 return other > this ? this : other; 4321 } else { 4322 return createInvalid(); 4323 } 4324 } 4325); 4326 4327// Pick a moment m from moments so that m[fn](other) is true for all 4328// other. This relies on the function fn to be transitive. 4329// 4330// moments should either be an array of moment objects or an array, whose 4331// first element is an array of moment objects. 4332function pickBy(fn, moments) { 4333 var res, i; 4334 if (moments.length === 1 && isArray(moments[0])) { 4335 moments = moments[0]; 4336 } 4337 if (!moments.length) { 4338 return createLocal(); 4339 } 4340 res = moments[0]; 4341 for (i = 1; i < moments.length; ++i) { 4342 if (!moments[i].isValid() || moments[i][fn](res)) { 4343 res = moments[i]; 4344 } 4345 } 4346 return res; 4347} 4348 4349// TODO: Use [].sort instead? 4350function min () { 4351 var args = [].slice.call(arguments, 0); 4352 4353 return pickBy('isBefore', args); 4354} 4355 4356function max () { 4357 var args = [].slice.call(arguments, 0); 4358 4359 return pickBy('isAfter', args); 4360} 4361 4362var now = function () { 4363 return Date.now ? Date.now() : +(new Date()); 4364}; 4365 4366var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; 4367 4368function isDurationValid(m) { 4369 for (var key in m) { 4370 if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { 4371 return false; 4372 } 4373 } 4374 4375 var unitHasDecimal = false; 4376 for (var i = 0; i < ordering.length; ++i) { 4377 if (m[ordering[i]]) { 4378 if (unitHasDecimal) { 4379 return false; // only allow non-integers for smallest unit 4380 } 4381 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { 4382 unitHasDecimal = true; 4383 } 4384 } 4385 } 4386 4387 return true; 4388} 4389 4390function isValid$1() { 4391 return this._isValid; 4392} 4393 4394function createInvalid$1() { 4395 return createDuration(NaN); 4396} 4397 4398function Duration (duration) { 4399 var normalizedInput = normalizeObjectUnits(duration), 4400 years = normalizedInput.year || 0, 4401 quarters = normalizedInput.quarter || 0, 4402 months = normalizedInput.month || 0, 4403 weeks = normalizedInput.week || 0, 4404 days = normalizedInput.day || 0, 4405 hours = normalizedInput.hour || 0, 4406 minutes = normalizedInput.minute || 0, 4407 seconds = normalizedInput.second || 0, 4408 milliseconds = normalizedInput.millisecond || 0; 4409 4410 this._isValid = isDurationValid(normalizedInput); 4411 4412 // representation for dateAddRemove 4413 this._milliseconds = +milliseconds + 4414 seconds * 1e3 + // 1000 4415 minutes * 6e4 + // 1000 * 60 4416 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 4417 // Because of dateAddRemove treats 24 hours as different from a 4418 // day when working around DST, we need to store them separately 4419 this._days = +days + 4420 weeks * 7; 4421 // It is impossible to translate months into days without knowing 4422 // which months you are are talking about, so we have to store 4423 // it separately. 4424 this._months = +months + 4425 quarters * 3 + 4426 years * 12; 4427 4428 this._data = {}; 4429 4430 this._locale = getLocale(); 4431 4432 this._bubble(); 4433} 4434 4435function isDuration (obj) { 4436 return obj instanceof Duration; 4437} 4438 4439function absRound (number) { 4440 if (number < 0) { 4441 return Math.round(-1 * number) * -1; 4442 } else { 4443 return Math.round(number); 4444 } 4445} 4446 4447// FORMATTING 4448 4449function offset (token, separator) { 4450 addFormatToken(token, 0, 0, function () { 4451 var offset = this.utcOffset(); 4452 var sign = '+'; 4453 if (offset < 0) { 4454 offset = -offset; 4455 sign = '-'; 4456 } 4457 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); 4458 }); 4459} 4460 4461offset('Z', ':'); 4462offset('ZZ', ''); 4463 4464// PARSING 4465 4466addRegexToken('Z', matchShortOffset); 4467addRegexToken('ZZ', matchShortOffset); 4468addParseToken(['Z', 'ZZ'], function (input, array, config) { 4469 config._useUTC = true; 4470 config._tzm = offsetFromString(matchShortOffset, input); 4471}); 4472 4473// HELPERS 4474 4475// timezone chunker 4476// '+10:00' > ['10', '00'] 4477// '-1530' > ['-15', '30'] 4478var chunkOffset = /([\+\-]|\d\d)/gi; 4479 4480function offsetFromString(matcher, string) { 4481 var matches = (string || '').match(matcher); 4482 4483 if (matches === null) { 4484 return null; 4485 } 4486 4487 var chunk = matches[matches.length - 1] || []; 4488 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; 4489 var minutes = +(parts[1] * 60) + toInt(parts[2]); 4490 4491 return minutes === 0 ? 4492 0 : 4493 parts[0] === '+' ? minutes : -minutes; 4494} 4495 4496// Return a moment from input, that is local/utc/zone equivalent to model. 4497function cloneWithOffset(input, model) { 4498 var res, diff; 4499 if (model._isUTC) { 4500 res = model.clone(); 4501 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); 4502 // Use low-level api, because this fn is low-level api. 4503 res._d.setTime(res._d.valueOf() + diff); 4504 hooks.updateOffset(res, false); 4505 return res; 4506 } else { 4507 return createLocal(input).local(); 4508 } 4509} 4510 4511function getDateOffset (m) { 4512 // On Firefox.24 Date#getTimezoneOffset returns a floating point. 4513 // https://github.com/moment/moment/pull/1871 4514 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; 4515} 4516 4517// HOOKS 4518 4519// This function will be called whenever a moment is mutated. 4520// It is intended to keep the offset in sync with the timezone. 4521hooks.updateOffset = function () {}; 4522 4523// MOMENTS 4524 4525// keepLocalTime = true means only change the timezone, without 4526// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> 4527// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset 4528// +0200, so we adjust the time as needed, to be valid. 4529// 4530// Keeping the time actually adds/subtracts (one hour)
vendor: 19,497 bytes, lines 4531-5182
4531// from the actual represented time. That is why we call updateOffset 4532// a second time. In case it wants us to change the offset again 4533// _changeInProgress == true case, then we have to adjust, because 4534// there is no such time in the given timezone. 4535function getSetOffset (input, keepLocalTime, keepMinutes) { 4536 var offset = this._offset || 0, 4537 localAdjust; 4538 if (!this.isValid()) { 4539 return input != null ? this : NaN; 4540 } 4541 if (input != null) { 4542 if (typeof input === 'string') { 4543 input = offsetFromString(matchShortOffset, input); 4544 if (input === null) { 4545 return this; 4546 } 4547 } else if (Math.abs(input) < 16 && !keepMinutes) { 4548 input = input * 60; 4549 } 4550 if (!this._isUTC && keepLocalTime) { 4551 localAdjust = getDateOffset(this); 4552 } 4553 this._offset = input; 4554 this._isUTC = true; 4555 if (localAdjust != null) { 4556 this.add(localAdjust, 'm'); 4557 } 4558 if (offset !== input) { 4559 if (!keepLocalTime || this._changeInProgress) { 4560 addSubtract(this, createDuration(input - offset, 'm'), 1, false); 4561 } else if (!this._changeInProgress) { 4562 this._changeInProgress = true; 4563 hooks.updateOffset(this, true); 4564 this._changeInProgress = null; 4565 } 4566 } 4567 return this; 4568 } else { 4569 return this._isUTC ? offset : getDateOffset(this); 4570 } 4571} 4572 4573function getSetZone (input, keepLocalTime) { 4574 if (input != null) { 4575 if (typeof input !== 'string') { 4576 input = -input; 4577 } 4578 4579 this.utcOffset(input, keepLocalTime); 4580 4581 return this; 4582 } else { 4583 return -this.utcOffset(); 4584 } 4585} 4586 4587function setOffsetToUTC (keepLocalTime) { 4588 return this.utcOffset(0, keepLocalTime); 4589} 4590 4591function setOffsetToLocal (keepLocalTime) { 4592 if (this._isUTC) { 4593 this.utcOffset(0, keepLocalTime); 4594 this._isUTC = false; 4595 4596 if (keepLocalTime) { 4597 this.subtract(getDateOffset(this), 'm'); 4598 } 4599 } 4600 return this; 4601} 4602 4603function setOffsetToParsedOffset () { 4604 if (this._tzm != null) { 4605 this.utcOffset(this._tzm, false, true); 4606 } else if (typeof this._i === 'string') { 4607 var tZone = offsetFromString(matchOffset, this._i); 4608 if (tZone != null) { 4609 this.utcOffset(tZone); 4610 } 4611 else { 4612 this.utcOffset(0, true); 4613 } 4614 } 4615 return this; 4616} 4617 4618function hasAlignedHourOffset (input) { 4619 if (!this.isValid()) { 4620 return false; 4621 } 4622 input = input ? createLocal(input).utcOffset() : 0; 4623 4624 return (this.utcOffset() - input) % 60 === 0; 4625} 4626 4627function isDaylightSavingTime () { 4628 return ( 4629 this.utcOffset() > this.clone().month(0).utcOffset() || 4630 this.utcOffset() > this.clone().month(5).utcOffset() 4631 ); 4632} 4633 4634function isDaylightSavingTimeShifted () { 4635 if (!isUndefined(this._isDSTShifted)) { 4636 return this._isDSTShifted; 4637 } 4638 4639 var c = {}; 4640 4641 copyConfig(c, this); 4642 c = prepareConfig(c); 4643 4644 if (c._a) { 4645 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); 4646 this._isDSTShifted = this.isValid() && 4647 compareArrays(c._a, other.toArray()) > 0; 4648 } else { 4649 this._isDSTShifted = false; 4650 } 4651 4652 return this._isDSTShifted; 4653} 4654 4655function isLocal () { 4656 return this.isValid() ? !this._isUTC : false; 4657} 4658 4659function isUtcOffset () { 4660 return this.isValid() ? this._isUTC : false; 4661} 4662 4663function isUtc () { 4664 return this.isValid() ? this._isUTC && this._offset === 0 : false; 4665} 4666 4667// ASP.NET json date format regex 4668var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; 4669 4670// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html 4671// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere 4672// and further modified to allow for strings containing both week and day 4673var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; 4674 4675function createDuration (input, key) { 4676 var duration = input, 4677 // matching against regexp is expensive, do it on demand 4678 match = null, 4679 sign, 4680 ret, 4681 diffRes; 4682 4683 if (isDuration(input)) { 4684 duration = { 4685 ms : input._milliseconds, 4686 d : input._days, 4687 M : input._months 4688 }; 4689 } else if (isNumber(input)) { 4690 duration = {}; 4691 if (key) { 4692 duration[key] = input; 4693 } else { 4694 duration.milliseconds = input; 4695 } 4696 } else if (!!(match = aspNetRegex.exec(input))) { 4697 sign = (match[1] === '-') ? -1 : 1; 4698 duration = { 4699 y : 0, 4700 d : toInt(match[DATE]) * sign, 4701 h : toInt(match[HOUR]) * sign, 4702 m : toInt(match[MINUTE]) * sign, 4703 s : toInt(match[SECOND]) * sign, 4704 ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match 4705 }; 4706 } else if (!!(match = isoRegex.exec(input))) { 4707 sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1; 4708 duration = { 4709 y : parseIso(match[2], sign), 4710 M : parseIso(match[3], sign), 4711 w : parseIso(match[4], sign), 4712 d : parseIso(match[5], sign), 4713 h : parseIso(match[6], sign), 4714 m : parseIso(match[7], sign), 4715 s : parseIso(match[8], sign) 4716 }; 4717 } else if (duration == null) {// checks for null or undefined 4718 duration = {}; 4719 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { 4720 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); 4721 4722 duration = {}; 4723 duration.ms = diffRes.milliseconds; 4724 duration.M = diffRes.months; 4725 } 4726 4727 ret = new Duration(duration); 4728 4729 if (isDuration(input) && hasOwnProp(input, '_locale')) { 4730 ret._locale = input._locale; 4731 } 4732 4733 return ret; 4734} 4735 4736createDuration.fn = Duration.prototype; 4737createDuration.invalid = createInvalid$1; 4738 4739function parseIso (inp, sign) { 4740 // We'd normally use ~~inp for this, but unfortunately it also 4741 // converts floats to ints. 4742 // inp may be undefined, so careful calling replace on it. 4743 var res = inp && parseFloat(inp.replace(',', '.')); 4744 // apply sign while we're at it 4745 return (isNaN(res) ? 0 : res) * sign; 4746} 4747 4748function positiveMomentsDifference(base, other) { 4749 var res = {milliseconds: 0, months: 0}; 4750 4751 res.months = other.month() - base.month() + 4752 (other.year() - base.year()) * 12; 4753 if (base.clone().add(res.months, 'M').isAfter(other)) { 4754 --res.months; 4755 } 4756 4757 res.milliseconds = +other - +(base.clone().add(res.months, 'M')); 4758 4759 return res; 4760} 4761 4762function momentsDifference(base, other) { 4763 var res; 4764 if (!(base.isValid() && other.isValid())) { 4765 return {milliseconds: 0, months: 0}; 4766 } 4767 4768 other = cloneWithOffset(other, base); 4769 if (base.isBefore(other)) { 4770 res = positiveMomentsDifference(base, other); 4771 } else { 4772 res = positiveMomentsDifference(other, base); 4773 res.milliseconds = -res.milliseconds; 4774 res.months = -res.months; 4775 } 4776 4777 return res; 4778} 4779 4780// TODO: remove 'name' arg after deprecation is removed 4781function createAdder(direction, name) { 4782 return function (val, period) { 4783 var dur, tmp; 4784 //invert the arguments, but complain about it 4785 if (period !== null && !isNaN(+period)) { 4786 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 4787 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); 4788 tmp = val; val = period; period = tmp; 4789 } 4790 4791 val = typeof val === 'string' ? +val : val; 4792 dur = createDuration(val, period); 4793 addSubtract(this, dur, direction); 4794 return this; 4795 }; 4796} 4797 4798function addSubtract (mom, duration, isAdding, updateOffset) { 4799 var milliseconds = duration._milliseconds, 4800 days = absRound(duration._days), 4801 months = absRound(duration._months); 4802 4803 if (!mom.isValid()) { 4804 // No op 4805 return; 4806 } 4807 4808 updateOffset = updateOffset == null ? true : updateOffset; 4809 4810 if (months) { 4811 setMonth(mom, get(mom, 'Month') + months * isAdding); 4812 } 4813 if (days) { 4814 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); 4815 } 4816 if (milliseconds) { 4817 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); 4818 } 4819 if (updateOffset) { 4820 hooks.updateOffset(mom, days || months); 4821 } 4822} 4823 4824var add = createAdder(1, 'add'); 4825var subtract = createAdder(-1, 'subtract'); 4826 4827function getCalendarFormat(myMoment, now) { 4828 var diff = myMoment.diff(now, 'days', true); 4829 return diff < -6 ? 'sameElse' : 4830 diff < -1 ? 'lastWeek' : 4831 diff < 0 ? 'lastDay' : 4832 diff < 1 ? 'sameDay' : 4833 diff < 2 ? 'nextDay' : 4834 diff < 7 ? 'nextWeek' : 'sameElse'; 4835} 4836 4837function calendar$1 (time, formats) { 4838 // We want to compare the start of today, vs this. 4839 // Getting start-of-today depends on whether we're local/utc/offset or not. 4840 var now = time || createLocal(), 4841 sod = cloneWithOffset(now, this).startOf('day'), 4842 format = hooks.calendarFormat(this, sod) || 'sameElse'; 4843 4844 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); 4845 4846 return this.format(output || this.localeData().calendar(format, this, createLocal(now))); 4847} 4848 4849function clone () { 4850 return new Moment(this); 4851} 4852 4853function isAfter (input, units) { 4854 var localInput = isMoment(input) ? input : createLocal(input); 4855 if (!(this.isValid() && localInput.isValid())) { 4856 return false; 4857 } 4858 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 4859 if (units === 'millisecond') { 4860 return this.valueOf() > localInput.valueOf(); 4861 } else { 4862 return localInput.valueOf() < this.clone().startOf(units).valueOf(); 4863 } 4864} 4865 4866function isBefore (input, units) { 4867 var localInput = isMoment(input) ? input : createLocal(input); 4868 if (!(this.isValid() && localInput.isValid())) { 4869 return false; 4870 } 4871 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); 4872 if (units === 'millisecond') { 4873 return this.valueOf() < localInput.valueOf(); 4874 } else { 4875 return this.clone().endOf(units).valueOf() < localInput.valueOf(); 4876 } 4877} 4878 4879function isBetween (from, to, units, inclusivity) { 4880 inclusivity = inclusivity || '()'; 4881 return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && 4882 (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); 4883} 4884 4885function isSame (input, units) { 4886 var localInput = isMoment(input) ? input : createLocal(input), 4887 inputMs; 4888 if (!(this.isValid() && localInput.isValid())) { 4889 return false; 4890 } 4891 units = normalizeUnits(units || 'millisecond'); 4892 if (units === 'millisecond') { 4893 return this.valueOf() === localInput.valueOf(); 4894 } else { 4895 inputMs = localInput.valueOf(); 4896 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); 4897 } 4898} 4899 4900function isSameOrAfter (input, units) { 4901 return this.isSame(input, units) || this.isAfter(input,units); 4902} 4903 4904function isSameOrBefore (input, units) { 4905 return this.isSame(input, units) || this.isBefore(input,units); 4906} 4907 4908function diff (input, units, asFloat) { 4909 var that, 4910 zoneDelta, 4911 delta, output; 4912 4913 if (!this.isValid()) { 4914 return NaN; 4915 } 4916 4917 that = cloneWithOffset(input, this); 4918 4919 if (!that.isValid()) { 4920 return NaN; 4921 } 4922 4923 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; 4924 4925 units = normalizeUnits(units); 4926 4927 switch (units) { 4928 case 'year': output = monthDiff(this, that) / 12; break; 4929 case 'month': output = monthDiff(this, that); break; 4930 case 'quarter': output = monthDiff(this, that) / 3; break; 4931 case 'second': output = (this - that) / 1e3; break; // 1000 4932 case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 4933 case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 4934 case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst 4935 case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst 4936 default: output = this - that; 4937 } 4938 4939 return asFloat ? output : absFloor(output); 4940} 4941 4942function monthDiff (a, b) { 4943 // difference in months 4944 var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), 4945 // b is in (anchor - 1 month, anchor + 1 month) 4946 anchor = a.clone().add(wholeMonthDiff, 'months'), 4947 anchor2, adjust; 4948 4949 if (b - anchor < 0) { 4950 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); 4951 // linear across the month 4952 adjust = (b - anchor) / (anchor - anchor2); 4953 } else { 4954 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); 4955 // linear across the month 4956 adjust = (b - anchor) / (anchor2 - anchor); 4957 } 4958 4959 //check for negative zero, return zero if negative zero 4960 return -(wholeMonthDiff + adjust) || 0; 4961} 4962 4963hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; 4964hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; 4965 4966function toString () { 4967 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); 4968} 4969 4970function toISOString(keepOffset) { 4971 if (!this.isValid()) { 4972 return null; 4973 } 4974 var utc = keepOffset !== true; 4975 var m = utc ? this.clone().utc() : this; 4976 if (m.year() < 0 || m.year() > 9999) { 4977 return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); 4978 } 4979 if (isFunction(Date.prototype.toISOString)) { 4980 // native implementation is ~50x faster, use it when we can 4981 if (utc) { 4982 return this.toDate().toISOString(); 4983 } else { 4984 return new Date(this._d.valueOf()).toISOString().replace('Z', formatMoment(m, 'Z')); 4985 } 4986 } 4987 return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); 4988} 4989 4990/** 4991 * Return a human readable representation of a moment that can 4992 * also be evaluated to get a new moment which is the same 4993 * 4994 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects 4995 */ 4996function inspect () { 4997 if (!this.isValid()) { 4998 return 'moment.invalid(/* ' + this._i + ' */)'; 4999 } 5000 var func = 'moment'; 5001 var zone = ''; 5002 if (!this.isLocal()) { 5003 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; 5004 zone = 'Z'; 5005 } 5006 var prefix = '[' + func + '("]'; 5007 var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; 5008 var datetime = '-MM-DD[T]HH:mm:ss.SSS'; 5009 var suffix = zone + '[")]'; 5010 5011 return this.format(prefix + year + datetime + suffix); 5012} 5013 5014function format (inputString) { 5015 if (!inputString) { 5016 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; 5017 } 5018 var output = formatMoment(this, inputString); 5019 return this.localeData().postformat(output); 5020} 5021 5022function from (time, withoutSuffix) { 5023 if (this.isValid() && 5024 ((isMoment(time) && time.isValid()) || 5025 createLocal(time).isValid())) { 5026 return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); 5027 } else { 5028 return this.localeData().invalidDate(); 5029 } 5030} 5031 5032function fromNow (withoutSuffix) { 5033 return this.from(createLocal(), withoutSuffix); 5034} 5035 5036function to (time, withoutSuffix) { 5037 if (this.isValid() && 5038 ((isMoment(time) && time.isValid()) || 5039 createLocal(time).isValid())) { 5040 return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); 5041 } else { 5042 return this.localeData().invalidDate(); 5043 } 5044} 5045 5046function toNow (withoutSuffix) { 5047 return this.to(createLocal(), withoutSuffix); 5048} 5049 5050// If passed a locale key, it will set the locale for this 5051// instance. Otherwise, it will return the locale configuration 5052// variables for this instance. 5053function locale (key) { 5054 var newLocaleData; 5055 5056 if (key === undefined) { 5057 return this._locale._abbr; 5058 } else { 5059 newLocaleData = getLocale(key); 5060 if (newLocaleData != null) { 5061 this._locale = newLocaleData; 5062 } 5063 return this; 5064 } 5065} 5066 5067var lang = deprecate( 5068 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', 5069 function (key) { 5070 if (key === undefined) { 5071 return this.localeData(); 5072 } else { 5073 return this.locale(key); 5074 } 5075 } 5076); 5077 5078function localeData () { 5079 return this._locale; 5080} 5081 5082function startOf (units) { 5083 units = normalizeUnits(units); 5084 // the following switch intentionally omits break keywords 5085 // to utilize falling through the cases. 5086 switch (units) { 5087 case 'year': 5088 this.month(0); 5089 /* falls through */ 5090 case 'quarter': 5091 case 'month': 5092 this.date(1); 5093 /* falls through */ 5094 case 'week': 5095 case 'isoWeek': 5096 case 'day': 5097 case 'date': 5098 this.hours(0); 5099 /* falls through */ 5100 case 'hour': 5101 this.minutes(0); 5102 /* falls through */ 5103 case 'minute': 5104 this.seconds(0); 5105 /* falls through */ 5106 case 'second': 5107 this.milliseconds(0); 5108 } 5109 5110 // weeks are a special case 5111 if (units === 'week') { 5112 this.weekday(0); 5113 } 5114 if (units === 'isoWeek') { 5115 this.isoWeekday(1); 5116 } 5117 5118 // quarters are also special 5119 if (units === 'quarter') { 5120 this.month(Math.floor(this.month() / 3) * 3); 5121 } 5122 5123 return this; 5124} 5125 5126function endOf (units) { 5127 units = normalizeUnits(units); 5128 if (units === undefined || units === 'millisecond') { 5129 return this; 5130 } 5131 5132 // 'date' is an alias for 'day', so it should be considered as such. 5133 if (units === 'date') { 5134 units = 'day'; 5135 } 5136 5137 return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); 5138} 5139 5140function valueOf () { 5141 return this._d.valueOf() - ((this._offset || 0) * 60000); 5142} 5143 5144function unix () { 5145 return Math.floor(this.valueOf() / 1000); 5146} 5147 5148function toDate () { 5149 return new Date(this.valueOf()); 5150} 5151 5152function toArray () { 5153 var m = this; 5154 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; 5155} 5156 5157function toObject () { 5158 var m = this; 5159 return { 5160 years: m.year(), 5161 months: m.month(), 5162 date: m.date(), 5163 hours: m.hours(), 5164 minutes: m.minutes(), 5165 seconds: m.seconds(), 5166 milliseconds: m.milliseconds() 5167 }; 5168} 5169 5170function toJSON () { 5171 // new Date(NaN).toJSON() === null 5172 return this.isValid() ? this.toISOString() : null; 5173} 5174 5175function isValid$2 () { 5176 return isValid(this); 5177} 5178 5179function parsingFlags () { 5180 return extend({}, getParsingFlags(this)); 5181} 5182
vendor: 11,961 bytes, lines 5183-5654
5183function invalidAt () { 5184 return getParsingFlags(this).overflow; 5185} 5186 5187function creationData() { 5188 return { 5189 input: this._i, 5190 format: this._f, 5191 locale: this._locale, 5192 isUTC: this._isUTC, 5193 strict: this._strict 5194 }; 5195} 5196 5197// FORMATTING 5198 5199addFormatToken(0, ['gg', 2], 0, function () { 5200 return this.weekYear() % 100; 5201}); 5202 5203addFormatToken(0, ['GG', 2], 0, function () { 5204 return this.isoWeekYear() % 100; 5205}); 5206 5207function addWeekYearFormatToken (token, getter) { 5208 addFormatToken(0, [token, token.length], 0, getter); 5209} 5210 5211addWeekYearFormatToken('gggg', 'weekYear'); 5212addWeekYearFormatToken('ggggg', 'weekYear'); 5213addWeekYearFormatToken('GGGG', 'isoWeekYear'); 5214addWeekYearFormatToken('GGGGG', 'isoWeekYear'); 5215 5216// ALIASES 5217 5218addUnitAlias('weekYear', 'gg'); 5219addUnitAlias('isoWeekYear', 'GG'); 5220 5221// PRIORITY 5222 5223addUnitPriority('weekYear', 1); 5224addUnitPriority('isoWeekYear', 1); 5225 5226 5227// PARSING 5228 5229addRegexToken('G', matchSigned); 5230addRegexToken('g', matchSigned); 5231addRegexToken('GG', match1to2, match2); 5232addRegexToken('gg', match1to2, match2); 5233addRegexToken('GGGG', match1to4, match4); 5234addRegexToken('gggg', match1to4, match4); 5235addRegexToken('GGGGG', match1to6, match6); 5236addRegexToken('ggggg', match1to6, match6); 5237 5238addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { 5239 week[token.substr(0, 2)] = toInt(input); 5240}); 5241 5242addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { 5243 week[token] = hooks.parseTwoDigitYear(input); 5244}); 5245 5246// MOMENTS 5247 5248function getSetWeekYear (input) { 5249 return getSetWeekYearHelper.call(this, 5250 input, 5251 this.week(), 5252 this.weekday(), 5253 this.localeData()._week.dow, 5254 this.localeData()._week.doy); 5255} 5256 5257function getSetISOWeekYear (input) { 5258 return getSetWeekYearHelper.call(this, 5259 input, this.isoWeek(), this.isoWeekday(), 1, 4); 5260} 5261 5262function getISOWeeksInYear () { 5263 return weeksInYear(this.year(), 1, 4); 5264} 5265 5266function getWeeksInYear () { 5267 var weekInfo = this.localeData()._week; 5268 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); 5269} 5270 5271function getSetWeekYearHelper(input, week, weekday, dow, doy) { 5272 var weeksTarget; 5273 if (input == null) { 5274 return weekOfYear(this, dow, doy).year; 5275 } else { 5276 weeksTarget = weeksInYear(input, dow, doy); 5277 if (week > weeksTarget) { 5278 week = weeksTarget; 5279 } 5280 return setWeekAll.call(this, input, week, weekday, dow, doy); 5281 } 5282} 5283 5284function setWeekAll(weekYear, week, weekday, dow, doy) { 5285 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), 5286 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); 5287 5288 this.year(date.getUTCFullYear()); 5289 this.month(date.getUTCMonth()); 5290 this.date(date.getUTCDate()); 5291 return this; 5292} 5293 5294// FORMATTING 5295 5296addFormatToken('Q', 0, 'Qo', 'quarter'); 5297 5298// ALIASES 5299 5300addUnitAlias('quarter', 'Q'); 5301 5302// PRIORITY 5303 5304addUnitPriority('quarter', 7); 5305 5306// PARSING 5307 5308addRegexToken('Q', match1); 5309addParseToken('Q', function (input, array) { 5310 array[MONTH] = (toInt(input) - 1) * 3; 5311}); 5312 5313// MOMENTS 5314 5315function getSetQuarter (input) { 5316 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); 5317} 5318 5319// FORMATTING 5320 5321addFormatToken('D', ['DD', 2], 'Do', 'date'); 5322 5323// ALIASES 5324 5325addUnitAlias('date', 'D'); 5326 5327// PRIOROITY 5328addUnitPriority('date', 9); 5329 5330// PARSING 5331 5332addRegexToken('D', match1to2); 5333addRegexToken('DD', match1to2, match2); 5334addRegexToken('Do', function (isStrict, locale) { 5335 // TODO: Remove "ordinalParse" fallback in next major release. 5336 return isStrict ? 5337 (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : 5338 locale._dayOfMonthOrdinalParseLenient; 5339}); 5340 5341addParseToken(['D', 'DD'], DATE); 5342addParseToken('Do', function (input, array) { 5343 array[DATE] = toInt(input.match(match1to2)[0]); 5344}); 5345 5346// MOMENTS 5347 5348var getSetDayOfMonth = makeGetSet('Date', true); 5349 5350// FORMATTING 5351 5352addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); 5353 5354// ALIASES 5355 5356addUnitAlias('dayOfYear', 'DDD'); 5357 5358// PRIORITY 5359addUnitPriority('dayOfYear', 4); 5360 5361// PARSING 5362 5363addRegexToken('DDD', match1to3); 5364addRegexToken('DDDD', match3); 5365addParseToken(['DDD', 'DDDD'], function (input, array, config) { 5366 config._dayOfYear = toInt(input); 5367}); 5368 5369// HELPERS 5370 5371// MOMENTS 5372 5373function getSetDayOfYear (input) { 5374 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; 5375 return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); 5376} 5377 5378// FORMATTING 5379 5380addFormatToken('m', ['mm', 2], 0, 'minute'); 5381 5382// ALIASES 5383 5384addUnitAlias('minute', 'm'); 5385 5386// PRIORITY 5387 5388addUnitPriority('minute', 14); 5389 5390// PARSING 5391 5392addRegexToken('m', match1to2); 5393addRegexToken('mm', match1to2, match2); 5394addParseToken(['m', 'mm'], MINUTE); 5395 5396// MOMENTS 5397 5398var getSetMinute = makeGetSet('Minutes', false); 5399 5400// FORMATTING 5401 5402addFormatToken('s', ['ss', 2], 0, 'second'); 5403 5404// ALIASES 5405 5406addUnitAlias('second', 's'); 5407 5408// PRIORITY 5409 5410addUnitPriority('second', 15); 5411 5412// PARSING 5413 5414addRegexToken('s', match1to2); 5415addRegexToken('ss', match1to2, match2); 5416addParseToken(['s', 'ss'], SECOND); 5417 5418// MOMENTS 5419 5420var getSetSecond = makeGetSet('Seconds', false); 5421 5422// FORMATTING 5423 5424addFormatToken('S', 0, 0, function () { 5425 return ~~(this.millisecond() / 100); 5426}); 5427 5428addFormatToken(0, ['SS', 2], 0, function () { 5429 return ~~(this.millisecond() / 10); 5430}); 5431 5432addFormatToken(0, ['SSS', 3], 0, 'millisecond'); 5433addFormatToken(0, ['SSSS', 4], 0, function () { 5434 return this.millisecond() * 10; 5435}); 5436addFormatToken(0, ['SSSSS', 5], 0, function () { 5437 return this.millisecond() * 100; 5438}); 5439addFormatToken(0, ['SSSSSS', 6], 0, function () { 5440 return this.millisecond() * 1000; 5441}); 5442addFormatToken(0, ['SSSSSSS', 7], 0, function () { 5443 return this.millisecond() * 10000; 5444}); 5445addFormatToken(0, ['SSSSSSSS', 8], 0, function () { 5446 return this.millisecond() * 100000; 5447}); 5448addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { 5449 return this.millisecond() * 1000000; 5450}); 5451 5452 5453// ALIASES 5454 5455addUnitAlias('millisecond', 'ms'); 5456 5457// PRIORITY 5458 5459addUnitPriority('millisecond', 16); 5460 5461// PARSING 5462 5463addRegexToken('S', match1to3, match1); 5464addRegexToken('SS', match1to3, match2); 5465addRegexToken('SSS', match1to3, match3); 5466 5467var token; 5468for (token = 'SSSS'; token.length <= 9; token += 'S') { 5469 addRegexToken(token, matchUnsigned); 5470} 5471 5472function parseMs(input, array) { 5473 array[MILLISECOND] = toInt(('0.' + input) * 1000); 5474} 5475 5476for (token = 'S'; token.length <= 9; token += 'S') { 5477 addParseToken(token, parseMs); 5478} 5479// MOMENTS 5480 5481var getSetMillisecond = makeGetSet('Milliseconds', false); 5482 5483// FORMATTING 5484 5485addFormatToken('z', 0, 0, 'zoneAbbr'); 5486addFormatToken('zz', 0, 0, 'zoneName'); 5487 5488// MOMENTS 5489 5490function getZoneAbbr () { 5491 return this._isUTC ? 'UTC' : ''; 5492} 5493 5494function getZoneName () { 5495 return this._isUTC ? 'Coordinated Universal Time' : ''; 5496} 5497 5498var proto = Moment.prototype; 5499 5500proto.add = add; 5501proto.calendar = calendar$1; 5502proto.clone = clone; 5503proto.diff = diff; 5504proto.endOf = endOf; 5505proto.format = format; 5506proto.from = from; 5507proto.fromNow = fromNow; 5508proto.to = to; 5509proto.toNow = toNow; 5510proto.get = stringGet; 5511proto.invalidAt = invalidAt; 5512proto.isAfter = isAfter; 5513proto.isBefore = isBefore; 5514proto.isBetween = isBetween; 5515proto.isSame = isSame; 5516proto.isSameOrAfter = isSameOrAfter; 5517proto.isSameOrBefore = isSameOrBefore; 5518proto.isValid = isValid$2; 5519proto.lang = lang; 5520proto.locale = locale; 5521proto.localeData = localeData; 5522proto.max = prototypeMax; 5523proto.min = prototypeMin; 5524proto.parsingFlags = parsingFlags; 5525proto.set = stringSet; 5526proto.startOf = startOf; 5527proto.subtract = subtract; 5528proto.toArray = toArray; 5529proto.toObject = toObject; 5530proto.toDate = toDate; 5531proto.toISOString = toISOString; 5532proto.inspect = inspect; 5533proto.toJSON = toJSON; 5534proto.toString = toString; 5535proto.unix = unix; 5536proto.valueOf = valueOf; 5537proto.creationData = creationData; 5538 5539// Year 5540proto.year = getSetYear; 5541proto.isLeapYear = getIsLeapYear; 5542 5543// Week Year 5544proto.weekYear = getSetWeekYear; 5545proto.isoWeekYear = getSetISOWeekYear; 5546 5547// Quarter 5548proto.quarter = proto.quarters = getSetQuarter; 5549 5550// Month 5551proto.month = getSetMonth; 5552proto.daysInMonth = getDaysInMonth; 5553 5554// Week 5555proto.week = proto.weeks = getSetWeek; 5556proto.isoWeek = proto.isoWeeks = getSetISOWeek; 5557proto.weeksInYear = getWeeksInYear; 5558proto.isoWeeksInYear = getISOWeeksInYear; 5559 5560// Day 5561proto.date = getSetDayOfMonth; 5562proto.day = proto.days = getSetDayOfWeek; 5563proto.weekday = getSetLocaleDayOfWeek; 5564proto.isoWeekday = getSetISODayOfWeek; 5565proto.dayOfYear = getSetDayOfYear; 5566 5567// Hour 5568proto.hour = proto.hours = getSetHour; 5569 5570// Minute 5571proto.minute = proto.minutes = getSetMinute; 5572 5573// Second 5574proto.second = proto.seconds = getSetSecond; 5575 5576// Millisecond 5577proto.millisecond = proto.milliseconds = getSetMillisecond; 5578 5579// Offset 5580proto.utcOffset = getSetOffset; 5581proto.utc = setOffsetToUTC; 5582proto.local = setOffsetToLocal; 5583proto.parseZone = setOffsetToParsedOffset; 5584proto.hasAlignedHourOffset = hasAlignedHourOffset; 5585proto.isDST = isDaylightSavingTime; 5586proto.isLocal = isLocal; 5587proto.isUtcOffset = isUtcOffset; 5588proto.isUtc = isUtc; 5589proto.isUTC = isUtc; 5590 5591// Timezone 5592proto.zoneAbbr = getZoneAbbr; 5593proto.zoneName = getZoneName; 5594 5595// Deprecations 5596proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); 5597proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); 5598proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); 5599proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); 5600proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); 5601 5602function createUnix (input) { 5603 return createLocal(input * 1000); 5604} 5605 5606function createInZone () { 5607 return createLocal.apply(null, arguments).parseZone(); 5608} 5609 5610function preParsePostFormat (string) { 5611 return string; 5612} 5613 5614var proto$1 = Locale.prototype; 5615 5616proto$1.calendar = calendar; 5617proto$1.longDateFormat = longDateFormat; 5618proto$1.invalidDate = invalidDate; 5619proto$1.ordinal = ordinal; 5620proto$1.preparse = preParsePostFormat; 5621proto$1.postformat = preParsePostFormat; 5622proto$1.relativeTime = relativeTime; 5623proto$1.pastFuture = pastFuture; 5624proto$1.set = set; 5625 5626// Month 5627proto$1.months = localeMonths; 5628proto$1.monthsShort = localeMonthsShort; 5629proto$1.monthsParse = localeMonthsParse; 5630proto$1.monthsRegex = monthsRegex; 5631proto$1.monthsShortRegex = monthsShortRegex; 5632 5633// Week 5634proto$1.week = localeWeek; 5635proto$1.firstDayOfYear = localeFirstDayOfYear; 5636proto$1.firstDayOfWeek = localeFirstDayOfWeek; 5637 5638// Day of Week 5639proto$1.weekdays = localeWeekdays; 5640proto$1.weekdaysMin = localeWeekdaysMin; 5641proto$1.weekdaysShort = localeWeekdaysShort; 5642proto$1.weekdaysParse = localeWeekdaysParse; 5643 5644proto$1.weekdaysRegex = weekdaysRegex; 5645proto$1.weekdaysShortRegex = weekdaysShortRegex; 5646proto$1.weekdaysMinRegex = weekdaysMinRegex; 5647 5648// Hours 5649proto$1.isPM = localeIsPM; 5650proto$1.meridiem = localeMeridiem; 5651 5652function get$1 (format, index, field, setter) { 5653 var locale = getLocale(); 5654 var utc = createUTC().set(setter, index);
vendor: 7,931 bytes, lines 5655-5947
5655 return locale[field](utc, format); 5656} 5657 5658function listMonthsImpl (format, index, field) { 5659 if (isNumber(format)) { 5660 index = format; 5661 format = undefined; 5662 } 5663 5664 format = format || ''; 5665 5666 if (index != null) { 5667 return get$1(format, index, field, 'month'); 5668 } 5669 5670 var i; 5671 var out = []; 5672 for (i = 0; i < 12; i++) { 5673 out[i] = get$1(format, i, field, 'month'); 5674 } 5675 return out; 5676} 5677 5678// () 5679// (5) 5680// (fmt, 5) 5681// (fmt) 5682// (true) 5683// (true, 5) 5684// (true, fmt, 5) 5685// (true, fmt) 5686function listWeekdaysImpl (localeSorted, format, index, field) { 5687 if (typeof localeSorted === 'boolean') { 5688 if (isNumber(format)) { 5689 index = format; 5690 format = undefined; 5691 } 5692 5693 format = format || ''; 5694 } else { 5695 format = localeSorted; 5696 index = format; 5697 localeSorted = false; 5698 5699 if (isNumber(format)) { 5700 index = format; 5701 format = undefined; 5702 } 5703 5704 format = format || ''; 5705 } 5706 5707 var locale = getLocale(), 5708 shift = localeSorted ? locale._week.dow : 0; 5709 5710 if (index != null) { 5711 return get$1(format, (index + shift) % 7, field, 'day'); 5712 } 5713 5714 var i; 5715 var out = []; 5716 for (i = 0; i < 7; i++) { 5717 out[i] = get$1(format, (i + shift) % 7, field, 'day'); 5718 } 5719 return out; 5720} 5721 5722function listMonths (format, index) { 5723 return listMonthsImpl(format, index, 'months'); 5724} 5725 5726function listMonthsShort (format, index) { 5727 return listMonthsImpl(format, index, 'monthsShort'); 5728} 5729 5730function listWeekdays (localeSorted, format, index) { 5731 return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); 5732} 5733 5734function listWeekdaysShort (localeSorted, format, index) { 5735 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); 5736} 5737 5738function listWeekdaysMin (localeSorted, format, index) { 5739 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); 5740} 5741 5742getSetGlobalLocale('en', { 5743 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, 5744 ordinal : function (number) { 5745 var b = number % 10, 5746 output = (toInt(number % 100 / 10) === 1) ? 'th' : 5747 (b === 1) ? 'st' : 5748 (b === 2) ? 'nd' : 5749 (b === 3) ? 'rd' : 'th'; 5750 return number + output; 5751 } 5752}); 5753 5754// Side effect imports 5755hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); 5756hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); 5757 5758var mathAbs = Math.abs; 5759 5760function abs () { 5761 var data = this._data; 5762 5763 this._milliseconds = mathAbs(this._milliseconds); 5764 this._days = mathAbs(this._days); 5765 this._months = mathAbs(this._months); 5766 5767 data.milliseconds = mathAbs(data.milliseconds); 5768 data.seconds = mathAbs(data.seconds); 5769 data.minutes = mathAbs(data.minutes); 5770 data.hours = mathAbs(data.hours); 5771 data.months = mathAbs(data.months); 5772 data.years = mathAbs(data.years); 5773 5774 return this; 5775} 5776 5777function addSubtract$1 (duration, input, value, direction) { 5778 var other = createDuration(input, value); 5779 5780 duration._milliseconds += direction * other._milliseconds; 5781 duration._days += direction * other._days; 5782 duration._months += direction * other._months; 5783 5784 return duration._bubble(); 5785} 5786 5787// supports only 2.0-style add(1, 's') or add(duration) 5788function add$1 (input, value) { 5789 return addSubtract$1(this, input, value, 1); 5790} 5791 5792// supports only 2.0-style subtract(1, 's') or subtract(duration) 5793function subtract$1 (input, value) { 5794 return addSubtract$1(this, input, value, -1); 5795} 5796 5797function absCeil (number) { 5798 if (number < 0) { 5799 return Math.floor(number); 5800 } else { 5801 return Math.ceil(number); 5802 } 5803} 5804 5805function bubble () { 5806 var milliseconds = this._milliseconds; 5807 var days = this._days; 5808 var months = this._months; 5809 var data = this._data; 5810 var seconds, minutes, hours, years, monthsFromDays; 5811 5812 // if we have a mix of positive and negative values, bubble down first 5813 // check: https://github.com/moment/moment/issues/2166 5814 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || 5815 (milliseconds <= 0 && days <= 0 && months <= 0))) { 5816 milliseconds += absCeil(monthsToDays(months) + days) * 864e5; 5817 days = 0; 5818 months = 0; 5819 } 5820 5821 // The following code bubbles up values, see the tests for 5822 // examples of what that means. 5823 data.milliseconds = milliseconds % 1000; 5824 5825 seconds = absFloor(milliseconds / 1000); 5826 data.seconds = seconds % 60; 5827 5828 minutes = absFloor(seconds / 60); 5829 data.minutes = minutes % 60; 5830 5831 hours = absFloor(minutes / 60); 5832 data.hours = hours % 24; 5833 5834 days += absFloor(hours / 24); 5835 5836 // convert days to months 5837 monthsFromDays = absFloor(daysToMonths(days)); 5838 months += monthsFromDays; 5839 days -= absCeil(monthsToDays(monthsFromDays)); 5840 5841 // 12 months -> 1 year 5842 years = absFloor(months / 12); 5843 months %= 12; 5844 5845 data.days = days; 5846 data.months = months; 5847 data.years = years; 5848 5849 return this; 5850} 5851 5852function daysToMonths (days) { 5853 // 400 years have 146097 days (taking into account leap year rules) 5854 // 400 years have 12 months === 4800 5855 return days * 4800 / 146097; 5856} 5857 5858function monthsToDays (months) { 5859 // the reverse of daysToMonths 5860 return months * 146097 / 4800; 5861} 5862 5863function as (units) { 5864 if (!this.isValid()) { 5865 return NaN; 5866 } 5867 var days; 5868 var months; 5869 var milliseconds = this._milliseconds; 5870 5871 units = normalizeUnits(units); 5872 5873 if (units === 'month' || units === 'year') { 5874 days = this._days + milliseconds / 864e5; 5875 months = this._months + daysToMonths(days); 5876 return units === 'month' ? months : months / 12; 5877 } else { 5878 // handle milliseconds separately because of floating point math errors (issue #1867) 5879 days = this._days + Math.round(monthsToDays(this._months)); 5880 switch (units) { 5881 case 'week' : return days / 7 + milliseconds / 6048e5; 5882 case 'day' : return days + milliseconds / 864e5; 5883 case 'hour' : return days * 24 + milliseconds / 36e5; 5884 case 'minute' : return days * 1440 + milliseconds / 6e4; 5885 case 'second' : return days * 86400 + milliseconds / 1000; 5886 // Math.floor prevents floating point math errors here 5887 case 'millisecond': return Math.floor(days * 864e5) + milliseconds; 5888 default: throw new Error('Unknown unit ' + units); 5889 } 5890 } 5891} 5892 5893// TODO: Use this.as('ms')? 5894function valueOf$1 () { 5895 if (!this.isValid()) { 5896 return NaN; 5897 } 5898 return ( 5899 this._milliseconds + 5900 this._days * 864e5 + 5901 (this._months % 12) * 2592e6 + 5902 toInt(this._months / 12) * 31536e6 5903 ); 5904} 5905 5906function makeAs (alias) { 5907 return function () { 5908 return this.as(alias); 5909 }; 5910} 5911 5912var asMilliseconds = makeAs('ms'); 5913var asSeconds = makeAs('s'); 5914var asMinutes = makeAs('m'); 5915var asHours = makeAs('h'); 5916var asDays = makeAs('d'); 5917var asWeeks = makeAs('w'); 5918var asMonths = makeAs('M'); 5919var asYears = makeAs('y'); 5920 5921function clone$1 () { 5922 return createDuration(this); 5923} 5924 5925function get$2 (units) { 5926 units = normalizeUnits(units); 5927 return this.isValid() ? this[units + 's']() : NaN; 5928} 5929 5930function makeGetter(name) { 5931 return function () { 5932 return this.isValid() ? this._data[name] : NaN; 5933 }; 5934} 5935 5936var milliseconds = makeGetter('milliseconds'); 5937var seconds = makeGetter('seconds'); 5938var minutes = makeGetter('minutes'); 5939var hours = makeGetter('hours'); 5940var days = makeGetter('days'); 5941var months = makeGetter('months'); 5942var years = makeGetter('years'); 5943 5944function weeks () { 5945 return absFloor(this.days() / 7); 5946} 5947
vendor: 6,285 bytes, lines 5948-6144
5948var round = Math.round; 5949var thresholds = { 5950 ss: 44, // a few seconds to seconds 5951 s : 45, // seconds to minute 5952 m : 45, // minutes to hour 5953 h : 22, // hours to day 5954 d : 26, // days to month 5955 M : 11 // months to year 5956}; 5957 5958// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize 5959function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { 5960 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); 5961} 5962 5963function relativeTime$1 (posNegDuration, withoutSuffix, locale) { 5964 var duration = createDuration(posNegDuration).abs(); 5965 var seconds = round(duration.as('s')); 5966 var minutes = round(duration.as('m')); 5967 var hours = round(duration.as('h')); 5968 var days = round(duration.as('d')); 5969 var months = round(duration.as('M')); 5970 var years = round(duration.as('y')); 5971 5972 var a = seconds <= thresholds.ss && ['s', seconds] || 5973 seconds < thresholds.s && ['ss', seconds] || 5974 minutes <= 1 && ['m'] || 5975 minutes < thresholds.m && ['mm', minutes] || 5976 hours <= 1 && ['h'] || 5977 hours < thresholds.h && ['hh', hours] || 5978 days <= 1 && ['d'] || 5979 days < thresholds.d && ['dd', days] || 5980 months <= 1 && ['M'] || 5981 months < thresholds.M && ['MM', months] || 5982 years <= 1 && ['y'] || ['yy', years]; 5983 5984 a[2] = withoutSuffix; 5985 a[3] = +posNegDuration > 0; 5986 a[4] = locale; 5987 return substituteTimeAgo.apply(null, a); 5988} 5989 5990// This function allows you to set the rounding function for relative time strings 5991function getSetRelativeTimeRounding (roundingFunction) { 5992 if (roundingFunction === undefined) { 5993 return round; 5994 } 5995 if (typeof(roundingFunction) === 'function') { 5996 round = roundingFunction; 5997 return true; 5998 } 5999 return false; 6000} 6001 6002// This function allows you to set a threshold for relative time strings 6003function getSetRelativeTimeThreshold (threshold, limit) { 6004 if (thresholds[threshold] === undefined) { 6005 return false; 6006 } 6007 if (limit === undefined) { 6008 return thresholds[threshold]; 6009 } 6010 thresholds[threshold] = limit; 6011 if (threshold === 's') { 6012 thresholds.ss = limit - 1; 6013 } 6014 return true; 6015} 6016 6017function humanize (withSuffix) { 6018 if (!this.isValid()) { 6019 return this.localeData().invalidDate(); 6020 } 6021 6022 var locale = this.localeData(); 6023 var output = relativeTime$1(this, !withSuffix, locale); 6024 6025 if (withSuffix) { 6026 output = locale.pastFuture(+this, output); 6027 } 6028 6029 return locale.postformat(output); 6030} 6031 6032var abs$1 = Math.abs; 6033 6034function sign(x) { 6035 return ((x > 0) - (x < 0)) || +x; 6036} 6037 6038function toISOString$1() { 6039 // for ISO strings we do not use the normal bubbling rules: 6040 // * milliseconds bubble up until they become hours 6041 // * days do not bubble at all 6042 // * months bubble up until they become years 6043 // This is because there is no context-free conversion between hours and days 6044 // (think of clock changes) 6045 // and also not between days and months (28-31 days per month) 6046 if (!this.isValid()) { 6047 return this.localeData().invalidDate(); 6048 } 6049 6050 var seconds = abs$1(this._milliseconds) / 1000; 6051 var days = abs$1(this._days); 6052 var months = abs$1(this._months); 6053 var minutes, hours, years; 6054 6055 // 3600 seconds -> 60 minutes -> 1 hour 6056 minutes = absFloor(seconds / 60); 6057 hours = absFloor(minutes / 60); 6058 seconds %= 60; 6059 minutes %= 60; 6060 6061 // 12 months -> 1 year 6062 years = absFloor(months / 12); 6063 months %= 12; 6064 6065 6066 // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js 6067 var Y = years; 6068 var M = months; 6069 var D = days; 6070 var h = hours; 6071 var m = minutes; 6072 var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; 6073 var total = this.asSeconds(); 6074 6075 if (!total) { 6076 // this is the same as C#'s (Noda) and python (isodate)... 6077 // but not other JS (goog.date) 6078 return 'P0D'; 6079 } 6080 6081 var totalSign = total < 0 ? '-' : ''; 6082 var ymSign = sign(this._months) !== sign(total) ? '-' : ''; 6083 var daysSign = sign(this._days) !== sign(total) ? '-' : ''; 6084 var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; 6085 6086 return totalSign + 'P' + 6087 (Y ? ymSign + Y + 'Y' : '') + 6088 (M ? ymSign + M + 'M' : '') + 6089 (D ? daysSign + D + 'D' : '') + 6090 ((h || m || s) ? 'T' : '') + 6091 (h ? hmsSign + h + 'H' : '') + 6092 (m ? hmsSign + m + 'M' : '') + 6093 (s ? hmsSign + s + 'S' : ''); 6094} 6095 6096var proto$2 = Duration.prototype; 6097 6098proto$2.isValid = isValid$1; 6099proto$2.abs = abs; 6100proto$2.add = add$1; 6101proto$2.subtract = subtract$1; 6102proto$2.as = as; 6103proto$2.asMilliseconds = asMilliseconds; 6104proto$2.asSeconds = asSeconds; 6105proto$2.asMinutes = asMinutes; 6106proto$2.asHours = asHours; 6107proto$2.asDays = asDays; 6108proto$2.asWeeks = asWeeks; 6109proto$2.asMonths = asMonths; 6110proto$2.asYears = asYears; 6111proto$2.valueOf = valueOf$1; 6112proto$2._bubble = bubble; 6113proto$2.clone = clone$1; 6114proto$2.get = get$2; 6115proto$2.milliseconds = milliseconds; 6116proto$2.seconds = seconds; 6117proto$2.minutes = minutes; 6118proto$2.hours = hours; 6119proto$2.days = days; 6120proto$2.weeks = weeks; 6121proto$2.months = months; 6122proto$2.years = years; 6123proto$2.humanize = humanize; 6124proto$2.toISOString = toISOString$1; 6125proto$2.toString = toISOString$1; 6126proto$2.toJSON = toISOString$1; 6127proto$2.locale = locale; 6128proto$2.localeData = localeData; 6129 6130// Deprecations 6131proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); 6132proto$2.lang = lang; 6133 6134// Side effect imports 6135 6136// FORMATTING 6137 6138addFormatToken('X', 0, 0, 'unix'); 6139addFormatToken('x', 0, 0, 'valueOf'); 6140 6141// PARSING 6142 6143addRegexToken('x', matchSigned); 6144addRegexToken('X', matchTimestamp);
6145addParseToken('X', function (input, array, config) { 6146 config._d = new Date(parseFloat(input, 10) * 1000); 6147}); 6148addParseToken('x', function (input, array, config) { 6149 config._d = new Date(toInt(input)); 6150}); 6151 6152// Side effect imports 6153 6154 6155hooks.version = '2.20.1'; 6156 6157setHookCallback(createLocal); 6158 6159hooks.fn = proto; 6160hooks.min = min; 6161hooks.max = max; 6162hooks.now = now; 6163hooks.utc = createUTC; 6164hooks.unix = createUnix; 6165hooks.months = listMonths; 6166hooks.isDate = isDate; 6167hooks.locale = getSetGlobalLocale; 6168hooks.invalid = createInvalid; 6169hooks.duration = createDuration; 6170hooks.isMoment = isMoment; 6171hooks.weekdays = listWeekdays; 6172hooks.parseZone = createInZone; 6173hooks.localeData = getLocale; 6174hooks.isDuration = isDuration; 6175hooks.monthsShort = listMonthsShort; 6176hooks.weekdaysMin = listWeekdaysMin; 6177hooks.defineLocale = defineLocale; 6178hooks.updateLocale = updateLocale; 6179hooks.locales = listLocales; 6180hooks.weekdaysShort = listWeekdaysShort; 6181hooks.normalizeUnits = normalizeUnits; 6182hooks.relativeTimeRounding = getSetRelativeTimeRounding; 6183hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; 6184hooks.calendarFormat = getCalendarFormat; 6185hooks.prototype = proto; 6186 6187// currently HTML5 input type only supports 24-hour formats 6188hooks.HTML5_FMT = { 6189 DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // <input type="datetime-local" /> 6190 DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // <input type="datetime-local" step="1" /> 6191 DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // <input type="datetime-local" step="0.001" /> 6192 DATE: 'YYYY-MM-DD', // <input type="date" /> 6193 TIME: 'HH:mm', // <input type="time" /> 6194 TIME_SECONDS: 'HH:mm:ss', // <input type="time" step="1" /> 6195 TIME_MS: 'HH:mm:ss.SSS', // <input type="time" step="0.001" /> 6196 WEEK: 'YYYY-[W]WW', // <input type="week" /> 6197 MONTH: 'YYYY-MM' // <input type="month" /> 6198}; 6199 6200return hooks; 6201 6202}))); 6203 6204},{}],7:[function(require,module,exports){ 6205/** 6206 * @namespace Chart 6207 */ 6208var Chart = require(29)(); 6209 6210Chart.helpers = require(45); 6211 6212// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! 6213require(27)(Chart); 6214 6215Chart.defaults = require(25); 6216Chart.Element = require(26); 6217Chart.elements = require(40); 6218Chart.Interaction = require(28); 6219Chart.layouts = require(30); 6220Chart.platform = require(48); 6221Chart.plugins = require(31); 6222Chart.Ticks = require(34); 6223 6224require(22)(Chart); 6225require(23)(Chart); 6226require(24)(Chart); 6227require(33)(Chart); 6228require(32)(Chart); 6229require(35)(Chart); 6230 6231require(55)(Chart); 6232require(53)(Chart); 6233require(54)(Chart); 6234require(56)(Chart); 6235require(57)(Chart); 6236require(58)(Chart); 6237 6238// Controllers must be loaded after elements 6239// See Chart.core.datasetController.dataElementType 6240require(15)(Chart); 6241require(16)(Chart); 6242require(17)(Chart); 6243require(18)(Chart); 6244require(19)(Chart); 6245require(20)(Chart); 6246require(21)(Chart); 6247 6248require(8)(Chart); 6249require(9)(Chart); 6250require(10)(Chart); 6251require(11)(Chart); 6252require(12)(Chart); 6253require(13)(Chart); 6254require(14)(Chart); 6255 6256// Loading built-it plugins 6257var plugins = require(49); 6258for (var k in plugins) { 6259 if (plugins.hasOwnProperty(k)) { 6260 Chart.plugins.register(plugins[k]); 6261 } 6262} 6263 6264Chart.platform.initialize(); 6265 6266module.exports = Chart; 6267if (typeof window !== 'undefined') { 6268 window.Chart = Chart; 6269} 6270 6271// DEPRECATIONS 6272 6273/** 6274 * Provided for backward compatibility, not available anymore 6275 * @namespace Chart.Legend 6276 * @deprecated since version 2.1.5 6277 * @todo remove at version 3 6278 * @private 6279 */ 6280Chart.Legend = plugins.legend._element; 6281 6282/** 6283 * Provided for backward compatibility, not available anymore 6284 * @namespace Chart.Title 6285 * @deprecated since version 2.1.5 6286 * @todo remove at version 3 6287 * @private 6288 */ 6289Chart.Title = plugins.title._element; 6290 6291/** 6292 * Provided for backward compatibility, use Chart.plugins instead
6293 * @namespace Chart.pluginService 6294 * @deprecated since version 2.1.5 6295 * @todo remove at version 3 6296 * @private 6297 */ 6298Chart.pluginService = Chart.plugins; 6299 6300/** 6301 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no 6302 * effect, instead simply create/register plugins via plain JavaScript objects. 6303 * @interface Chart.PluginBase 6304 * @deprecated since version 2.5.0 6305 * @todo remove at version 3 6306 * @private 6307 */ 6308Chart.PluginBase = Chart.Element.extend({}); 6309 6310/** 6311 * Provided for backward compatibility, use Chart.helpers.canvas instead. 6312 * @namespace Chart.canvasHelpers 6313 * @deprecated since version 2.6.0 6314 * @todo remove at version 3 6315 * @private 6316 */ 6317Chart.canvasHelpers = Chart.helpers.canvas; 6318 6319/** 6320 * Provided for backward compatibility, use Chart.layouts instead. 6321 * @namespace Chart.layoutService 6322 * @deprecated since version 2.8.0 6323 * @todo remove at version 3 6324 * @private 6325 */ 6326Chart.layoutService = Chart.layouts; 6327 6328},{"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){ 6329'use strict'; 6330 6331module.exports = function(Chart) { 6332 6333 Chart.Bar = function(context, config) { 6334 config.type = 'bar'; 6335 6336 return new Chart(context, config); 6337 }; 6338 6339}; 6340 6341},{}],9:[function(require,module,exports){ 6342'use strict'; 6343 6344module.exports = function(Chart) { 6345 6346 Chart.Bubble = function(context, config) { 6347 config.type = 'bubble'; 6348 return new Chart(context, config); 6349 }; 6350 6351}; 6352 6353},{}],10:[function(require,module,exports){ 6354'use strict'; 6355 6356module.exports = function(Chart) { 6357 6358 Chart.Doughnut = function(context, config) { 6359 config.type = 'doughnut'; 6360 6361 return new Chart(context, config); 6362 }; 6363 6364}; 6365 6366},{}],11:[function(require,module,exports){ 6367'use strict'; 6368 6369module.exports = function(Chart) { 6370 6371 Chart.Line = function(context, config) { 6372 config.type = 'line'; 6373 6374 return new Chart(context, config); 6375 }; 6376 6377}; 6378 6379},{}],12:[function(require,module,exports){ 6380'use strict'; 6381 6382module.exports = function(Chart) { 6383 6384 Chart.PolarArea = function(context, config) { 6385 config.type = 'polarArea'; 6386 6387 return new Chart(context, config); 6388 }; 6389 6390}; 6391 6392},{}],13:[function(require,module,exports){ 6393'use strict'; 6394 6395module.exports = function(Chart) { 6396 6397 Chart.Radar = function(context, config) { 6398 config.type = 'radar'; 6399 6400 return new Chart(context, config); 6401 }; 6402 6403}; 6404 6405},{}],14:[function(require,module,exports){ 6406'use strict'; 6407 6408module.exports = function(Chart) { 6409 Chart.Scatter = function(context, config) { 6410 config.type = 'scatter'; 6411 return new Chart(context, config); 6412 }; 6413}; 6414 6415},{}],15:[function(require,module,exports){ 6416'use strict'; 6417 6418var defaults = require(25); 6419var elements = require(40); 6420var helpers = require(45); 6421 6422defaults._set('bar', { 6423 hover: { 6424 mode: 'label' 6425 }, 6426 6427 scales: { 6428 xAxes: [{ 6429 type: 'category', 6430 6431 // Specific to Bar Controller 6432 categoryPercentage: 0.8, 6433 barPercentage: 0.9, 6434 6435 // offset settings 6436 offset: true, 6437 6438 // grid line settings 6439 gridLines: { 6440 offsetGridLines: true 6441 } 6442 }], 6443 6444 yAxes: [{ 6445 type: 'linear' 6446 }] 6447 } 6448}); 6449 6450defaults._set('horizontalBar', { 6451 hover: { 6452 mode: 'index', 6453 axis: 'y' 6454 }, 6455 6456 scales: { 6457 xAxes: [{ 6458 type: 'linear', 6459 position: 'bottom' 6460 }], 6461 6462 yAxes: [{ 6463 position: 'left', 6464 type: 'category', 6465 6466 // Specific to Horizontal Bar Controller 6467 categoryPercentage: 0.8, 6468 barPercentage: 0.9, 6469 6470 // offset settings 6471 offset: true, 6472 6473 // grid line settings 6474 gridLines: { 6475 offsetGridLines: true 6476 } 6477 }] 6478 }, 6479 6480 elements: { 6481 rectangle: { 6482 borderSkipped: 'left' 6483 } 6484 }, 6485 6486 tooltips: { 6487 callbacks: { 6488 title: function(item, data) { 6489 // Pick first xLabel for now 6490 var title = ''; 6491 6492 if (item.length > 0) { 6493 if (item[0].yLabel) { 6494 title = item[0].yLabel; 6495 } else if (data.labels.length > 0 && item[0].index < data.labels.length) { 6496 title = data.labels[item[0].index]; 6497 } 6498 } 6499 6500 return title; 6501 }, 6502 6503 label: function(item, data) { 6504 var datasetLabel = data.datasets[item.datasetIndex].label || ''; 6505 return datasetLabel + ': ' + item.xLabel; 6506 } 6507 }, 6508 mode: 'index', 6509 axis: 'y' 6510 } 6511}); 6512 6513/** 6514 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap. 6515 * @private 6516 */ 6517function computeMinSampleSize(scale, pixels) { 6518 var min = scale.isHorizontal() ? scale.width : scale.height; 6519 var ticks = scale.getTicks(); 6520 var prev, curr, i, ilen; 6521 6522 for (i = 1, ilen = pixels.length; i < ilen; ++i) { 6523 min = Math.min(min, pixels[i] - pixels[i - 1]); 6524 } 6525 6526 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 6527 curr = scale.getPixelForTick(i); 6528 min = i > 0 ? Math.min(min, curr - prev) : min; 6529 prev = curr; 6530 } 6531 6532 return min; 6533} 6534 6535/** 6536 * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null, 6537 * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This 6538 * mode currently always generates bars equally sized (until we introduce scriptable options?). 6539 * @private 6540 */ 6541function computeFitCategoryTraits(index, ruler, options) { 6542 var thickness = options.barThickness; 6543 var count = ruler.stackCount; 6544 var curr = ruler.pixels[index]; 6545 var size, ratio; 6546 6547 if (helpers.isNullOrUndef(thickness)) { 6548 size = ruler.min * options.categoryPercentage; 6549 ratio = options.barPercentage; 6550 } else { 6551 // When bar thickness is enforced, category and bar percentages are ignored. 6552 // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%') 6553 // and deprecate barPercentage since this value is ignored when thickness is absolute. 6554 size = thickness * count; 6555 ratio = 1; 6556 } 6557 6558 return { 6559 chunk: size / count, 6560 ratio: ratio, 6561 start: curr - (size / 2) 6562 }; 6563} 6564 6565/** 6566 * Computes an "optimal" category that globally arranges bars side by side (no gap when 6567 * percentage options are 1), based on the previous and following categories. This mode 6568 * generates bars with different widths when data are not evenly spaced. 6569 * @private 6570 */ 6571function computeFlexCategoryTraits(index, ruler, options) { 6572 var pixels = ruler.pixels; 6573 var curr = pixels[index]; 6574 var prev = index > 0 ? pixels[index - 1] : null; 6575 var next = index < pixels.length - 1 ? pixels[index + 1] : null; 6576 var percent = options.categoryPercentage; 6577 var start, size; 6578 6579 if (prev === null) { 6580 // first data: its size is double based on the next point or, 6581 // if it's also the last data, we use the scale end extremity. 6582 prev = curr - (next === null ? ruler.end - curr : next - curr); 6583 } 6584 6585 if (next === null) { 6586 // last data: its size is also double based on the previous point. 6587 next = curr + curr - prev; 6588 } 6589 6590 start = curr - ((curr - prev) / 2) * percent; 6591 size = ((next - prev) / 2) * percent; 6592 6593 return { 6594 chunk: size / ruler.stackCount, 6595 ratio: options.barPercentage, 6596 start: start 6597 }; 6598} 6599 6600module.exports = function(Chart) { 6601 6602 Chart.controllers.bar = Chart.DatasetController.extend({ 6603 6604 dataElementType: elements.Rectangle, 6605 6606 initialize: function() { 6607 var me = this; 6608 var meta; 6609 6610 Chart.DatasetController.prototype.initialize.apply(me, arguments); 6611 6612 meta = me.getMeta(); 6613 meta.stack = me.getDataset().stack; 6614 meta.bar = true; 6615 }, 6616 6617 update: function(reset) { 6618 var me = this; 6619 var rects = me.getMeta().data; 6620 var i, ilen; 6621 6622 me._ruler = me.getRuler(); 6623 6624 for (i = 0, ilen = rects.length; i < ilen; ++i) { 6625 me.updateElement(rects[i], i, reset); 6626 } 6627 }, 6628 6629 updateElement: function(rectangle, index, reset) { 6630 var me = this; 6631 var chart = me.chart; 6632 var meta = me.getMeta(); 6633 var dataset = me.getDataset(); 6634 var custom = rectangle.custom || {}; 6635 var rectangleOptions = chart.options.elements.rectangle; 6636 6637 rectangle._xScale = me.getScaleForId(meta.xAxisID); 6638 rectangle._yScale = me.getScaleForId(meta.yAxisID); 6639 rectangle._datasetIndex = me.index; 6640 rectangle._index = index; 6641 6642 rectangle._model = { 6643 datasetLabel: dataset.label, 6644 label: chart.data.labels[index], 6645 borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, 6646 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), 6647 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), 6648 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) 6649 }; 6650 6651 me.updateElementGeometry(rectangle, index, reset); 6652 6653 rectangle.pivot(); 6654 }, 6655 6656 /** 6657 * @private 6658 */ 6659 updateElementGeometry: function(rectangle, index, reset) { 6660 var me = this; 6661 var model = rectangle._model; 6662 var vscale = me.getValueScale(); 6663 var base = vscale.getBasePixel(); 6664 var horizontal = vscale.isHorizontal(); 6665 var ruler = me._ruler || me.getRuler(); 6666 var vpixels = me.calculateBarValuePixels(me.index, index); 6667 var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); 6668 6669 model.horizontal = horizontal; 6670 model.base = reset ? base : vpixels.base; 6671 model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; 6672 model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; 6673 model.height = horizontal ? ipixels.size : undefined; 6674 model.width = horizontal ? undefined : ipixels.size; 6675 }, 6676 6677 /** 6678 * @private 6679 */ 6680 getValueScaleId: function() { 6681 return this.getMeta().yAxisID; 6682 }, 6683 6684 /** 6685 * @private 6686 */ 6687 getIndexScaleId: function() { 6688 return this.getMeta().xAxisID; 6689 }, 6690 6691 /** 6692 * @private 6693 */ 6694 getValueScale: function() {
6695 return this.getScaleForId(this.getValueScaleId()); 6696 }, 6697 6698 /** 6699 * @private 6700 */ 6701 getIndexScale: function() { 6702 return this.getScaleForId(this.getIndexScaleId()); 6703 }, 6704 6705 /** 6706 * Returns the stacks based on groups and bar visibility. 6707 * @param {Number} [last] - The dataset index 6708 * @returns {Array} The stack list 6709 * @private 6710 */ 6711 _getStacks: function(last) { 6712 var me = this; 6713 var chart = me.chart; 6714 var scale = me.getIndexScale(); 6715 var stacked = scale.options.stacked; 6716 var ilen = last === undefined ? chart.data.datasets.length : last + 1; 6717 var stacks = []; 6718 var i, meta; 6719 6720 for (i = 0; i < ilen; ++i) { 6721 meta = chart.getDatasetMeta(i); 6722 if (meta.bar && chart.isDatasetVisible(i) && 6723 (stacked === false || 6724 (stacked === true && stacks.indexOf(meta.stack) === -1) || 6725 (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { 6726 stacks.push(meta.stack); 6727 } 6728 } 6729 6730 return stacks; 6731 }, 6732 6733 /** 6734 * Returns the effective number of stacks based on groups and bar visibility. 6735 * @private 6736 */ 6737 getStackCount: function() { 6738 return this._getStacks().length; 6739 }, 6740 6741 /** 6742 * Returns the stack index for the given dataset based on groups and bar visibility. 6743 * @param {Number} [datasetIndex] - The dataset index 6744 * @param {String} [name] - The stack name to find 6745 * @returns {Number} The stack index 6746 * @private 6747 */ 6748 getStackIndex: function(datasetIndex, name) { 6749 var stacks = this._getStacks(datasetIndex); 6750 var index = (name !== undefined) 6751 ? stacks.indexOf(name) 6752 : -1; // indexOf returns -1 if element is not present 6753 6754 return (index === -1) 6755 ? stacks.length - 1 6756 : index; 6757 }, 6758 6759 /** 6760 * @private 6761 */ 6762 getRuler: function() { 6763 var me = this; 6764 var scale = me.getIndexScale(); 6765 var stackCount = me.getStackCount(); 6766 var datasetIndex = me.index; 6767 var isHorizontal = scale.isHorizontal(); 6768 var start = isHorizontal ? scale.left : scale.top; 6769 var end = start + (isHorizontal ? scale.width : scale.height); 6770 var pixels = []; 6771 var i, ilen, min; 6772 6773 for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { 6774 pixels.push(scale.getPixelForValue(null, i, datasetIndex)); 6775 } 6776 6777 min = helpers.isNullOrUndef(scale.options.barThickness) 6778 ? computeMinSampleSize(scale, pixels) 6779 : -1; 6780 6781 return { 6782 min: min, 6783 pixels: pixels, 6784 start: start, 6785 end: end, 6786 stackCount: stackCount, 6787 scale: scale 6788 }; 6789 }, 6790 6791 /** 6792 * Note: pixel values are not clamped to the scale area. 6793 * @private 6794 */ 6795 calculateBarValuePixels: function(datasetIndex, index) { 6796 var me = this; 6797 var chart = me.chart; 6798 var meta = me.getMeta(); 6799 var scale = me.getValueScale(); 6800 var datasets = chart.data.datasets; 6801 var value = scale.getRightValue(datasets[datasetIndex].data[index]); 6802 var stacked = scale.options.stacked; 6803 var stack = meta.stack; 6804 var start = 0; 6805 var i, imeta, ivalue, base, head, size; 6806 6807 if (stacked || (stacked === undefined && stack !== undefined)) { 6808 for (i = 0; i < datasetIndex; ++i) { 6809 imeta = chart.getDatasetMeta(i); 6810 6811 if (imeta.bar && 6812 imeta.stack === stack && 6813 imeta.controller.getValueScaleId() === scale.id && 6814 chart.isDatasetVisible(i)) { 6815 6816 ivalue = scale.getRightValue(datasets[i].data[index]); 6817 if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { 6818 start += ivalue; 6819 } 6820 } 6821 } 6822 } 6823 6824 base = scale.getPixelForValue(start); 6825 head = scale.getPixelForValue(start + value); 6826 size = (head - base) / 2; 6827 6828 return { 6829 size: size, 6830 base: base, 6831 head: head, 6832 center: head + size / 2 6833 }; 6834 }, 6835 6836 /** 6837 * @private 6838 */ 6839 calculateBarIndexPixels: function(datasetIndex, index, ruler) { 6840 var me = this; 6841 var options = ruler.scale.options; 6842 var range = options.barThickness === 'flex' 6843 ? computeFlexCategoryTraits(index, ruler, options) 6844 : computeFitCategoryTraits(index, ruler, options); 6845 6846 var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack); 6847 var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); 6848 var size = Math.min( 6849 helpers.valueOrDefault(options.maxBarThickness, Infinity), 6850 range.chunk * range.ratio); 6851 6852 return { 6853 base: center - size / 2, 6854 head: center + size / 2, 6855 center: center, 6856 size: size 6857 }; 6858 }, 6859 6860 draw: function() { 6861 var me = this; 6862 var chart = me.chart; 6863 var scale = me.getValueScale(); 6864 var rects = me.getMeta().data; 6865 var dataset = me.getDataset(); 6866 var ilen = rects.length; 6867 var i = 0; 6868 6869 helpers.canvas.clipArea(chart.ctx, chart.chartArea); 6870 6871 for (; i < ilen; ++i) { 6872 if (!isNaN(scale.getRightValue(dataset.data[i]))) { 6873 rects[i].draw(); 6874 } 6875 } 6876 6877 helpers.canvas.unclipArea(chart.ctx); 6878 }, 6879 6880 setHoverStyle: function(rectangle) { 6881 var dataset = this.chart.data.datasets[rectangle._datasetIndex]; 6882 var index = rectangle._index; 6883 var custom = rectangle.custom || {}; 6884 var model = rectangle._model; 6885
6886 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 6887 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 6888 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); 6889 }, 6890 6891 removeHoverStyle: function(rectangle) { 6892 var dataset = this.chart.data.datasets[rectangle._datasetIndex]; 6893 var index = rectangle._index; 6894 var custom = rectangle.custom || {}; 6895 var model = rectangle._model; 6896 var rectangleElementOptions = this.chart.options.elements.rectangle; 6897 6898 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); 6899 model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); 6900 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); 6901 } 6902 }); 6903 6904 Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ 6905 /** 6906 * @private 6907 */ 6908 getValueScaleId: function() { 6909 return this.getMeta().xAxisID; 6910 }, 6911 6912 /** 6913 * @private 6914 */ 6915 getIndexScaleId: function() { 6916 return this.getMeta().yAxisID; 6917 } 6918 }); 6919}; 6920 6921},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ 6922'use strict'; 6923 6924var defaults = require(25); 6925var elements = require(40); 6926var helpers = require(45); 6927 6928defaults._set('bubble', { 6929 hover: { 6930 mode: 'single' 6931 }, 6932 6933 scales: { 6934 xAxes: [{ 6935 type: 'linear', // bubble should probably use a linear scale by default 6936 position: 'bottom', 6937 id: 'x-axis-0' // need an ID so datasets can reference the scale 6938 }], 6939 yAxes: [{ 6940 type: 'linear', 6941 position: 'left', 6942 id: 'y-axis-0' 6943 }] 6944 }, 6945 6946 tooltips: { 6947 callbacks: { 6948 title: function() { 6949 // Title doesn't make sense for scatter since we format the data as a point 6950 return ''; 6951 }, 6952 label: function(item, data) { 6953 var datasetLabel = data.datasets[item.datasetIndex].label || ''; 6954 var dataPoint = data.datasets[item.datasetIndex].data[item.index]; 6955 return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; 6956 } 6957 } 6958 } 6959}); 6960 6961 6962module.exports = function(Chart) { 6963 6964 Chart.controllers.bubble = Chart.DatasetController.extend({ 6965 /** 6966 * @protected 6967 */ 6968 dataElementType: elements.Point, 6969 6970 /** 6971 * @protected 6972 */ 6973 update: function(reset) { 6974 var me = this; 6975 var meta = me.getMeta(); 6976 var points = meta.data; 6977 6978 // Update Points 6979 helpers.each(points, function(point, index) { 6980 me.updateElement(point, index, reset); 6981 }); 6982 }, 6983 6984 /** 6985 * @protected 6986 */ 6987 updateElement: function(point, index, reset) { 6988 var me = this; 6989 var meta = me.getMeta(); 6990 var custom = point.custom || {}; 6991 var xScale = me.getScaleForId(meta.xAxisID); 6992 var yScale = me.getScaleForId(meta.yAxisID); 6993 var options = me._resolveElementOptions(point, index); 6994 var data = me.getDataset().data[index]; 6995 var dsIndex = me.index; 6996 6997 var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); 6998 var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); 6999 7000 point._xScale = xScale; 7001 point._yScale = yScale; 7002 point._options = options; 7003 point._datasetIndex = dsIndex; 7004 point._index = index; 7005 point._model = { 7006 backgroundColor: options.backgroundColor, 7007 borderColor: options.borderColor, 7008 borderWidth: options.borderWidth, 7009 hitRadius: options.hitRadius, 7010 pointStyle: options.pointStyle, 7011 radius: reset ? 0 : options.radius, 7012 skip: custom.skip || isNaN(x) || isNaN(y), 7013 x: x, 7014 y: y, 7015 }; 7016 7017 point.pivot(); 7018 }, 7019 7020 /** 7021 * @protected 7022 */ 7023 setHoverStyle: function(point) { 7024 var model = point._model; 7025 var options = point._options; 7026 7027 model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroun
7027dColor, helpers.getHoverColor(options.backgroundColor)); 7028 model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); 7029 model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); 7030 model.radius = options.radius + options.hoverRadius; 7031 }, 7032 7033 /** 7034 * @protected 7035 */ 7036 removeHoverStyle: function(point) { 7037 var model = point._model; 7038 var options = point._options; 7039 7040 model.backgroundColor = options.backgroundColor; 7041 model.borderColor = options.borderColor; 7042 model.borderWidth = options.borderWidth; 7043 model.radius = options.radius; 7044 }, 7045 7046 /** 7047 * @private 7048 */ 7049 _resolveElementOptions: function(point, index) { 7050 var me = this; 7051 var chart = me.chart; 7052 var datasets = chart.data.datasets; 7053 var dataset = datasets[me.index]; 7054 var custom = point.custom || {}; 7055 var options = chart.options.elements.point; 7056 var resolve = helpers.options.resolve; 7057 var data = dataset.data[index]; 7058 var values = {}; 7059 var i, ilen, key; 7060 7061 // Scriptable options 7062 var context = { 7063 chart: chart, 7064 dataIndex: index, 7065 dataset: dataset, 7066 datasetIndex: me.index 7067 }; 7068 7069 var keys = [ 7070 'backgroundColor', 7071 'borderColor', 7072 'borderWidth', 7073 'hoverBackgroundColor', 7074 'hoverBorderColor', 7075 'hoverBorderWidth', 7076 'hoverRadius', 7077 'hitRadius', 7078 'pointStyle' 7079 ]; 7080 7081 for (i = 0, ilen = keys.length; i < ilen; ++i) { 7082 key = keys[i]; 7083 values[key] = resolve([ 7084 custom[key], 7085 dataset[key], 7086 options[key] 7087 ], context, index); 7088 } 7089 7090 // Custom radius resolution 7091 values.radius = resolve([ 7092 custom.radius, 7093 data ? data.r : undefined, 7094 dataset.radius, 7095 options.radius 7096 ], context, index); 7097 7098 return values; 7099 } 7100 }); 7101}; 7102 7103},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ 7104'use strict'; 7105 7106var defaults = require(25); 7107var elements = require(40); 7108var helpers = require(45); 7109 7110defaults._set('doughnut', { 7111 animation: { 7112 // Boolean - Whether we animate the rotation of the Doughnut 7113 animateRotate: true, 7114 // Boolean - Whether we animate scaling the Doughnut from the centre 7115 animateScale: false 7116 }, 7117 hover: { 7118 mode: 'single' 7119 }, 7120 legendCallback: function(chart) { 7121 var text = []; 7122 text.push('<ul class="' + chart.id + '-legend">'); 7123 7124 var data = chart.data; 7125 var datasets = data.datasets; 7126 var labels = data.labels; 7127 7128 if (datasets.length) { 7129 for (var i = 0; i < datasets[0].data.length; ++i) { 7130 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); 7131 if (labels[i]) { 7132 text.push(labels[i]); 7133 } 7134 text.push('</li>'); 7135 } 7136 } 7137 7138 text.push('</ul>'); 7139 return text.join(''); 7140 }, 7141 legend: { 7142 labels: { 7143 generateLabels: function(chart) { 7144 var data = chart.data; 7145 if (data.labels.length && data.datasets.length) { 7146 return data.labels.map(function(label, i) { 7147 var meta = chart.getDatasetMeta(0); 7148 var ds = data.datasets[0]; 7149 var arc = meta.data[i]; 7150 var custom = arc && arc.custom || {}; 7151 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7152 var arcOpts = chart.options.elements.arc; 7153 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); 7154 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); 7155 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); 7156 7157 return { 7158 text: label, 7159 fillStyle: fill, 7160 strokeStyle: stroke, 7161 lineWidth: bw, 7162 hidden: isNaN(ds.data[i]) || meta.data[i].hidden, 7163 7164 // Extra data used for toggling the correct item 7165 index: i 7166 }; 7167 }); 7168 } 7169 return []; 7170 } 7171 }, 7172 7173 onClick: function(e, legendItem) { 7174 var index = legendItem.index; 7175 var chart = this.chart; 7176 var i, ilen, meta; 7177 7178 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 7179 meta = chart.getDatasetMeta(i); 7180 // toggle visibility of index if exists 7181 if (meta.data[index]) { 7182 meta.data[index].hidden = !meta.data[index].hidden; 7183 } 7184 } 7185
7186 chart.update(); 7187 } 7188 }, 7189 7190 // The percentage of the chart that we cut out of the middle. 7191 cutoutPercentage: 50, 7192 7193 // The rotation of the chart, where the first data arc begins. 7194 rotation: Math.PI * -0.5, 7195 7196 // The total circumference of the chart. 7197 circumference: Math.PI * 2.0, 7198 7199 // Need to override these to give a nice default 7200 tooltips: { 7201 callbacks: { 7202 title: function() { 7203 return ''; 7204 }, 7205 label: function(tooltipItem, data) { 7206 var dataLabel = data.labels[tooltipItem.index]; 7207 var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; 7208 7209 if (helpers.isArray(dataLabel)) { 7210 // show value on first line of multiline label 7211 // need to clone because we are changing the value 7212 dataLabel = dataLabel.slice(); 7213 dataLabel[0] += value; 7214 } else { 7215 dataLabel += value; 7216 } 7217 7218 return dataLabel; 7219 } 7220 } 7221 } 7222}); 7223 7224defaults._set('pie', helpers.clone(defaults.doughnut)); 7225defaults._set('pie', { 7226 cutoutPercentage: 0 7227}); 7228 7229module.exports = function(Chart) { 7230 7231 Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ 7232 7233 dataElementType: elements.Arc, 7234 7235 linkScales: helpers.noop, 7236 7237 // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly 7238 getRingIndex: function(datasetIndex) { 7239 var ringIndex = 0; 7240 7241 for (var j = 0; j < datasetIndex; ++j) { 7242 if (this.chart.isDatasetVisible(j)) { 7243 ++ringIndex; 7244 } 7245 } 7246 7247 return ringIndex; 7248 }, 7249 7250 update: function(reset) { 7251 var me = this; 7252 var chart = me.chart; 7253 var chartArea = chart.chartArea; 7254 var opts = chart.options; 7255 var arcOpts = opts.elements.arc; 7256 var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; 7257 var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; 7258 var minSize = Math.min(availableWidth, availableHeight); 7259 var offset = {x: 0, y: 0}; 7260 var meta = me.getMeta(); 7261 var cutoutPercentage = opts.cutoutPercentage; 7262 var circumference = opts.circumference; 7263 7264 // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc 7265 if (circumference < Math.PI * 2.0) { 7266 var startAngle = opts.rotation % (Math.PI * 2.0); 7267 startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); 7268 var endAngle = startAngle + circumference; 7269 var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; 7270 var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; 7271 var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); 7272 var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); 7273 var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); 7274 var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); 7275 var cutout = cutoutPercentage / 100.0; 7276 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))}; 7277 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))}; 7278 var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; 7279 minSize = Math.min(availableWidth / size.width, availableHeight / size.height); 7280 offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; 7281 } 7282 7283 chart.borderWidth = me.getMaxBorderWidth(meta.data); 7284 chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); 7285 chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); 7286 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); 7287 chart.offsetX = offset.x * chart.outerRadius; 7288 chart.offsetY = offset.y * chart.outerRadius; 7289 7290 meta.total = me.calculateTotal(); 7291
7292 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); 7293 me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); 7294 7295 helpers.each(meta.data, function(arc, index) { 7296 me.updateElement(arc, index, reset); 7297 }); 7298 }, 7299 7300 updateElement: function(arc, index, reset) { 7301 var me = this; 7302 var chart = me.chart; 7303 var chartArea = chart.chartArea; 7304 var opts = chart.options; 7305 var animationOpts = opts.animation; 7306 var centerX = (chartArea.left + chartArea.right) / 2; 7307 var centerY = (chartArea.top + chartArea.bottom) / 2; 7308 var startAngle = opts.rotation; // non reset case handled later 7309 var endAngle = opts.rotation; // non reset case handled later 7310 var dataset = me.getDataset(); 7311 var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); 7312 var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; 7313 var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; 7314 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7315 7316 helpers.extend(arc, { 7317 // Utility 7318 _datasetIndex: me.index, 7319 _index: index, 7320 7321 // Desired view properties 7322 _model: { 7323 x: centerX + chart.offsetX, 7324 y: centerY + chart.offsetY, 7325 startAngle: startAngle, 7326 endAngle: endAngle, 7327 circumference: circumference, 7328 outerRadius: outerRadius, 7329 innerRadius: innerRadius, 7330 label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) 7331 } 7332 }); 7333 7334 var model = arc._model; 7335 // Resets the visual styles 7336 this.removeHoverStyle(arc); 7337 7338 // Set correct angles if not resetting 7339 if (!reset || !animationOpts.animateRotate) { 7340 if (index === 0) { 7341 model.startAngle = opts.rotation; 7342 } else { 7343 model.startAngle = me.getMeta().data[index - 1]._model.endAngle; 7344 } 7345 7346 model.endAngle = model.startAngle + model.circumference; 7347 } 7348 7349 arc.pivot(); 7350 }, 7351 7352 removeHoverStyle: function(arc) { 7353 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); 7354 }, 7355 7356 calculateTotal: function() { 7357 var dataset = this.getDataset(); 7358 var meta = this.getMeta(); 7359 var total = 0; 7360 var value; 7361 7362 helpers.each(meta.data, function(element, index) { 7363 value = dataset.data[index]; 7364 if (!isNaN(value) && !element.hidden) { 7365 total += Math.abs(value); 7366 } 7367 }); 7368 7369 /* if (total === 0) { 7370 total = NaN; 7371 }*/ 7372 7373 return total; 7374 }, 7375 7376 calculateCircumference: function(value) { 7377 var total = this.getMeta().total; 7378 if (total > 0 && !isNaN(value)) { 7379 return (Math.PI * 2.0) * (Math.abs(value) / total); 7380 } 7381 return 0; 7382 }, 7383 7384 // gets the max border or hover width to properly scale pie charts 7385 getMaxBorderWidth: function(arcs) { 7386 var max = 0; 7387 var index = this.index; 7388 var length = arcs.length; 7389 var borderWidth; 7390 var hoverWidth; 7391 7392 for (var i = 0; i < length; i++) { 7393 borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; 7394 hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; 7395 7396 max = borderWidth > max ? borderWidth : max; 7397 max = hoverWidth > max ? hoverWidth : max; 7398 } 7399 return max; 7400 } 7401 }); 7402}; 7403 7404},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ 7405'use strict'; 7406 7407var defaults = require(25); 7408var elements = require(40); 7409var helpers = require(45); 7410 7411defaults._set('line', { 7412 showLines: true, 7413 spanGaps: false, 7414 7415 hover: { 7416 mode: 'label' 7417 }, 7418 7419 scales: { 7420 xAxes: [{ 7421 type: 'category', 7422 id: 'x-axis-0' 7423 }], 7424 yAxes: [{ 7425 type: 'linear', 7426 id: 'y-axis-0' 7427 }] 7428 } 7429}); 7430 7431module.exports = function(Chart) { 7432 7433 function lineEnabled(dataset, options) { 7434 return helpers.valueOrDefault(dataset.showLine, options.showLines); 7435 } 7436 7437 Chart.controllers.line = Chart.DatasetController.extend({ 7438 7439 datasetElementType: elements.Line, 7440 7441 dataElementType: elements.Point, 7442 7443 update: function(reset) { 7444 var me = this; 7445 var meta = me.getMeta(); 7446 var line = meta.dataset; 7447 var points = meta.data || []; 7448 var options = me.chart.options; 7449 var lineElementOptions = options.elements.line; 7450 var scale = me.getScaleForId(meta.yAxisID); 7451 var i, ilen, custom; 7452 var dataset = me.getDataset(); 7453 var showLine = lineEnabled(dataset, options); 7454 7455 // Update Line 7456 if (showLine) { 7457 custom = line.custom || {}; 7458 7459 // Compatibility: If the properties are defined with only the old name, use those values 7460 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { 7461 dataset.lineTension = dataset.tension; 7462 } 7463 7464 // Utility 7465 line._scale = scale; 7466 line._datasetIndex = me.index; 7467 // Data 7468 line._children = points; 7469 // Model 7470 line._model = { 7471 // Appearance 7472 // The default behavior of lines is to break at null values, according 7473 // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 7474 // This option gives lines the ability to span gaps 7475 spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, 7476 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), 7477 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), 7478 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), 7479 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), 7480 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), 7481 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), 7482 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), 7483 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
7484 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), 7485 steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), 7486 cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), 7487 }; 7488 7489 line.pivot(); 7490 } 7491 7492 // Update Points 7493 for (i = 0, ilen = points.length; i < ilen; ++i) { 7494 me.updateElement(points[i], i, reset); 7495 } 7496 7497 if (showLine && line._model.tension !== 0) { 7498 me.updateBezierControlPoints(); 7499 } 7500 7501 // Now pivot the point for animation 7502 for (i = 0, ilen = points.length; i < ilen; ++i) { 7503 points[i].pivot(); 7504 } 7505 }, 7506 7507 getPointBackgroundColor: function(point, index) { 7508 var backgroundColor = this.chart.options.elements.point.backgroundColor; 7509 var dataset = this.getDataset(); 7510 var custom = point.custom || {}; 7511 7512 if (custom.backgroundColor) { 7513 backgroundColor = custom.backgroundColor; 7514 }
7514 else if (dataset.pointBackgroundColor) { 7515 backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); 7516 } else if (dataset.backgroundColor) { 7517 backgroundColor = dataset.backgroundColor; 7518 } 7519 7520 return backgroundColor; 7521 }, 7522 7523 getPointBorderColor: function(point, index) { 7524 var borderColor = this.chart.options.elements.point.borderColor; 7525 var dataset = this.getDataset(); 7526 var custom = point.custom || {}; 7527 7528 if (custom.borderColor) { 7529 borderColor = custom.borderColor; 7530 } else if (dataset.pointBorderColor) { 7531 borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); 7532 } else if (dataset.borderColor) { 7533 borderColor = dataset.borderColor; 7534 } 7535 7536 return borderColor; 7537 }, 7538 7539 getPointBorderWidth: function(point, index) { 7540 var borderWidth = this.chart.options.elements.point.borderWidth; 7541 var dataset = this.getDataset(); 7542 var custom = point.custom || {}; 7543 7544 if (!isNaN(custom.borderWidth)) { 7545 borderWidth = custom.borderWidth; 7546 } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { 7547 borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); 7548 } else if (!isNaN(dataset.borderWidth)) { 7549 borderWidth = dataset.borderWidth; 7550 } 7551 7552 return borderWidth; 7553 }, 7554 7555 updateElement: function(point, index, reset) { 7556 var me = this; 7557 var meta = me.getMeta(); 7558 var custom = point.custom || {}; 7559 var dataset = me.getDataset(); 7560 var datasetIndex = me.index; 7561 var value = dataset.data[index]; 7562 var yScale = me.getScaleForId(meta.yAxisID); 7563 var xScale = me.getScaleForId(meta.xAxisID); 7564 var pointOptions = me.chart.options.elements.point; 7565 var x, y; 7566 7567 // Compatibility: If the properties are defined with only the old name, use those values 7568 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 7569 dataset.pointRadius = dataset.radius; 7570 } 7571 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { 7572 dataset.pointHitRadius = dataset.hitRadius; 7573 } 7574 7575 x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); 7576 y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); 7577 7578 // Utility 7579 point._xScale = xScale; 7580 point._yScale = yScale; 7581 point._datasetIndex = datasetIndex; 7582 point._index = index; 7583 7584 // Desired view properties 7585 point._model = { 7586 x: x, 7587 y: y, 7588 skip: custom.skip || isNaN(x) || isNaN(y), 7589 // Appearance 7590 radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), 7591 pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
7592 backgroundColor: me.getPointBackgroundColor(point, index), 7593 borderColor: me.getPointBorderColor(point, index), 7594 borderWidth: me.getPointBorderWidth(point, index), 7595 tension: meta.dataset._model ? meta.dataset._model.tension : 0, 7596 steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, 7597 // Tooltip 7598 hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) 7599 }; 7600 }, 7601 7602 calculatePointY: function(value, index, datasetIndex) { 7603 var me = this; 7604 var chart = me.chart; 7605 var meta = me.getMeta(); 7606 var yScale = me.getScaleForId(meta.yAxisID); 7607 var sumPos = 0; 7608 var sumNeg = 0; 7609 var i, ds, dsMeta; 7610 7611 if (yScale.options.stacked) { 7612 for (i = 0; i < datasetIndex; i++) { 7613 ds = chart.data.datasets[i]; 7614 dsMeta = chart.getDatasetMeta(i); 7615 if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { 7616 var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); 7617 if (stackedRightValue < 0) { 7618 sumNeg += stackedRightValue || 0; 7619 } else { 7620 sumPos += stackedRightValue || 0; 7621 } 7622 } 7623 } 7624 7625 var rightValue = Number(yScale.getRightValue(value)); 7626 if (rightValue < 0) { 7627 return yScale.getPixelForValue(sumNeg + rightValue); 7628 } 7629 return yScale.getPixelForValue(sumPos + rightValue); 7630 } 7631 7632 return yScale.getPixelForValue(value); 7633 }, 7634 7635 updateBezierControlPoints: function() { 7636 var me = this; 7637 var meta = me.getMeta(); 7638 var area = me.chart.chartArea; 7639 var points = (meta.data || []); 7640 var i, ilen, point, model, controlPoints; 7641 7642 // Only consider points that are drawn in case the spanGaps option is used 7643 if (meta.dataset._model.spanGaps) { 7644 points = points.filter(function(pt) { 7645 return !pt._model.skip; 7646 }); 7647 } 7648 7649 function capControlPoint(pt, min, max) { 7650 return Math.max(Math.min(pt, max), min); 7651 } 7652 7653 if (meta.dataset._model.cubicInterpolationMode === 'monotone') { 7654 helpers.splineCurveMonotone(points); 7655 } else { 7656 for (i = 0, ilen = points.length; i < ilen; ++i) { 7657 point = points[i]; 7658 model = point._model; 7659 controlPoints = helpers.splineCurve( 7660 helpers.previousItem(points, i)._model, 7661 model, 7662 helpers.nextItem(points, i)._model, 7663 meta.dataset._model.tension 7664 ); 7665 model.controlPointPreviousX = controlPoints.previous.x; 7666 model.controlPointPreviousY = controlPoints.previous.y; 7667 model.controlPointNextX = controlPoints.next.x; 7668 model.controlPointNextY = controlPoints.next.y; 7669 } 7670 } 7671 7672 if (me.chart.options.elements.line.capBezierPoints) { 7673 for (i = 0, ilen = points.length; i < ilen; ++i) { 7674 model = points[i]._model; 7675 model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); 7676 model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); 7677 model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); 7678 model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); 7679 } 7680 } 7681 }, 7682 7683 draw: function() { 7684 var me = this; 7685 var chart = me.chart; 7686 var meta = me.getMeta(); 7687 var points = meta.data || []; 7688 var area = chart.chartArea; 7689 var ilen = points.length; 7690 var i = 0; 7691 7692 helpers.canvas.clipArea(chart.ctx, area); 7693 7694 if (lineEnabled(me.getDataset(), chart.options)) { 7695 meta.dataset.draw(); 7696 } 7697 7698 helpers.canvas.unclipArea(chart.ctx); 7699 7700 // Draw the points 7701 for (; i < ilen; ++i) { 7702 points[i].draw(area); 7703 } 7704 }, 7705 7706 setHoverStyle: function(point) { 7707 // Point 7708 var dataset = this.chart.data.datasets[point._datasetIndex]; 7709 var index = point._index; 7710 var custom = point.custom || {}; 7711 var model = point._model; 7712 7713 model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); 7714 model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 7715 model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 7716 model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); 7717 }, 7718 7719 removeHoverStyle: function(point) { 7720 var me = this; 7721 var dataset = me.chart.data.datasets[point._datasetIndex]; 7722 var index = point._index; 7723 var custom = point.custom || {}; 7724 var model = point._model; 7725 7726 // Compatibility: If the properties are defined with only the old name, use those values 7727 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 7728 dataset.pointRadius = dataset.radius; 7729 } 7730 7731 model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
7732 model.backgroundColor = me.getPointBackgroundColor(point, index); 7733 model.borderColor = me.getPointBorderColor(point, index); 7734 model.borderWidth = me.getPointBorderWidth(point, index); 7735 } 7736 }); 7737}; 7738 7739},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ 7740'use strict'; 7741 7742var defaults = require(25); 7743var elements = require(40); 7744var helpers = require(45); 7745 7746defaults._set('polarArea', { 7747 scale: { 7748 type: 'radialLinear', 7749 angleLines: { 7750 display: false 7751 }, 7752 gridLines: { 7753 circular: true 7754 }, 7755 pointLabels: { 7756 display: false 7757 }, 7758 ticks: { 7759 beginAtZero: true 7760 } 7761 }, 7762 7763 // Boolean - Whether to animate the rotation of the chart 7764 animation: { 7765 animateRotate: true, 7766 animateScale: true 7767 }, 7768 7769 startAngle: -0.5 * Math.PI, 7770 legendCallback: function(chart) { 7771 var text = []; 7772 text.push('<ul class="' + chart.id + '-legend">'); 7773 7774 var data = chart.data; 7775 var datasets = data.datasets; 7776 var labels = data.labels; 7777 7778 if (datasets.length) { 7779 for (var i = 0; i < datasets[0].data.length; ++i) { 7780 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>'); 7781 if (labels[i]) { 7782 text.push(labels[i]); 7783 } 7784 text.push('</li>'); 7785 } 7786 } 7787 7788 text.push('</ul>'); 7789 return text.join(''); 7790 }, 7791 legend: { 7792 labels: { 7793 generateLabels: function(chart) { 7794 var data = chart.data; 7795 if (data.labels.length && data.datasets.length) { 7796 return data.labels.map(function(label, i) { 7797 var meta = chart.getDatasetMeta(0); 7798 var ds = data.datasets[0]; 7799 var arc = meta.data[i]; 7800 var custom = arc.custom || {}; 7801 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 7802 var arcOpts = chart.options.elements.arc; 7803 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); 7804 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); 7805 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); 7806 7807 return { 7808 text: label, 7809 fillStyle: fill, 7810 strokeStyle: stroke, 7811 lineWidth: bw, 7812 hidden: isNaN(ds.data[i]) || meta.data[i].hidden, 7813 7814 // Extra data used for toggling the correct item 7815 index: i 7816 }; 7817 }); 7818 } 7819 return []; 7820 } 7821 }, 7822 7823 onClick: function(e, legendItem) { 7824 var index = legendItem.index; 7825 var chart = this.chart; 7826 var i, ilen, meta; 7827 7828 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 7829 meta = chart.getDatasetMeta(i); 7830 meta.data[index].hidden = !meta.data[index].hidden; 7831 } 7832 7833 chart.update(); 7834 } 7835 }, 7836 7837 // Need to override these to give a nice default 7838 tooltips: { 7839 callbacks: { 7840 title: function() { 7841 return ''; 7842 }, 7843 label: function(item, data) { 7844 return data.labels[item.index] + ': ' + item.yLabel; 7845 } 7846 } 7847 } 7848}); 7849 7850module.exports = function(Chart) { 7851 7852 Chart.controllers.polarArea = Chart.DatasetController.extend({ 7853 7854 dataElementType: elements.Arc, 7855 7856 linkScales: helpers.noop, 7857 7858 update: function(reset) { 7859 var me = this; 7860 var chart = me.chart; 7861 var chartArea = chart.chartArea; 7862 var meta = me.getMeta(); 7863 var opts = chart.options; 7864 var arcOpts = opts.elements.arc; 7865 var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); 7866 chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); 7867 chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); 7868 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); 7869 7870 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
7871 me.innerRadius = me.outerRadius - chart.radiusLength; 7872 7873 meta.count = me.countVisibleElements(); 7874 7875 helpers.each(meta.data, function(arc, index) { 7876 me.updateElement(arc, index, reset); 7877 }); 7878 }, 7879 7880 updateElement: function(arc, index, reset) { 7881 var me = this; 7882 var chart = me.chart; 7883 var dataset = me.getDataset(); 7884 var opts = chart.options; 7885 var animationOpts = opts.animation; 7886 var scale = chart.scale; 7887 var labels = chart.data.labels; 7888 7889 var circumference = me.calculateCircumference(dataset.data[index]); 7890 var centerX = scale.xCenter; 7891 var centerY = scale.yCenter; 7892 7893 // If there is NaN data before us, we need to calculate the starting angle correctly. 7894 // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data 7895 var visibleCount = 0; 7896 var meta = me.getMeta(); 7897 for (var i = 0; i < index; ++i) { 7898 if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { 7899 ++visibleCount; 7900 } 7901 } 7902 7903 // var negHalfPI = -0.5 * Math.PI; 7904 var datasetStartAngle = opts.startAngle; 7905 var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); 7906 var startAngle = datasetStartAngle + (circumference * visibleCount); 7907 var endAngle = startAngle + (arc.hidden ? 0 : circumference); 7908 7909 var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); 7910 7911 helpers.extend(arc, { 7912 // Utility 7913 _datasetIndex: me.index, 7914 _index: index, 7915 _scale: scale, 7916 7917 // Desired view properties 7918 _model: { 7919 x: centerX, 7920 y: centerY, 7921 innerRadius: 0, 7922 outerRadius: reset ? resetRadius : distance, 7923 startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, 7924 endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, 7925 label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) 7926 } 7927 }); 7928 7929 // Apply border and fill style 7930 me.removeHoverStyle(arc); 7931 7932 arc.pivot(); 7933 }, 7934 7935 removeHoverStyle: function(arc) { 7936 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); 7937 }, 7938 7939 countVisibleElements: function() { 7940 var dataset = this.getDataset(); 7941 var meta = this.getMeta(); 7942 var count = 0; 7943 7944 helpers.each(meta.data, function(element, index) { 7945 if (!isNaN(dataset.data[index]) && !element.hidden) { 7946 count++; 7947 } 7948 }); 7949 7950 return count; 7951 }, 7952 7953 calculateCircumference: function(value) { 7954 var count = this.getMeta().count; 7955 if (count > 0 && !isNaN(value)) { 7956 return (2 * Math.PI) / count; 7957 } 7958 return 0; 7959 } 7960 }); 7961}; 7962 7963},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ 7964'use strict'; 7965 7966var defaults = require(25); 7967var elements = require(40); 7968var helpers = require(45); 7969 7970defaults._set('radar', { 7971 scale: { 7972 type: 'radialLinear' 7973 }, 7974 elements: { 7975 line: { 7976 tension: 0 // no bezier in radar 7977 } 7978 } 7979}); 7980 7981module.exports = function(Chart) { 7982 7983 Chart.controllers.radar = Chart.DatasetController.extend({ 7984 7985 datasetElementType: elements.Line, 7986 7987 dataElementType: elements.Point, 7988 7989 linkScales: helpers.noop, 7990 7991 update: function(reset) { 7992 var me = this; 7993 var meta = me.getMeta(); 7994 var line = meta.dataset; 7995 var points = meta.data; 7996 var custom = line.custom || {}; 7997 var dataset = me.getDataset(); 7998 var lineElementOptions = me.chart.options.elements.line; 7999 var scale = me.chart.scale; 8000 8001 // Compatibility: If the properties are defined with only the old name, use those values 8002 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { 8003 dataset.lineTension = dataset.tension; 8004 } 8005 8006 helpers.extend(meta.dataset, { 8007 // Utility 8008 _datasetIndex: me.index, 8009 _scale: scale, 8010 // Data 8011 _children: points, 8012 _loop: true, 8013 // Model 8014 _model: { 8015 // Appearance 8016 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), 8017 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), 8018 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), 8019 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
8020 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), 8021 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), 8022 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), 8023 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), 8024 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), 8025 } 8026 }); 8027 8028 meta.dataset.pivot(); 8029 8030 // Update Points 8031 helpers.each(points, function(point, index) { 8032 me.updateElement(point, index, reset); 8033 }, me); 8034 8035 // Update bezier control points 8036 me.updateBezierControlPoints(); 8037 }, 8038 updateElement: function(point, index, reset) { 8039 var me = this; 8040 var custom = point.custom || {}; 8041 var dataset = me.getDataset(); 8042 var scale = me.chart.scale; 8043 var pointElementOptions = me.chart.options.elements.point; 8044 var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); 8045 8046 // Compatibility: If the properties are defined with only the old name, use those values 8047 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { 8048 dataset.pointRadius = dataset.radius; 8049 } 8050 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { 8051 dataset.pointHitRadius = dataset.hitRadius; 8052 } 8053 8054 helpers.extend(point, { 8055 // Utility 8056 _datasetIndex: me.index, 8057 _index: index, 8058 _scale: scale, 8059 8060 // Desired view properties 8061 _model: { 8062 x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales 8063 y: reset ? scale.yCenter : pointPosition.y, 8064 8065 // Appearance 8066 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), 8067 radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), 8068 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
8068dColor, index, pointElementOptions.backgroundColor), 8069 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), 8070 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), 8071 pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), 8072 8073 // Tooltip 8074 hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) 8075 } 8076 }); 8077 8078 point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); 8079 }, 8080 updateBezierControlPoints: function() { 8081 var chartArea = this.chart.chartArea; 8082 var meta = this.getMeta(); 8083 8084 helpers.each(meta.data, function(point, index) { 8085 var model = point._model; 8086 var controlPoints = helpers.splineCurve( 8087 helpers.previousItem(meta.data, index, true)._model, 8088 model, 8089 helpers.nextItem(meta.data, index, true)._model, 8090 model.tension 8091 ); 8092 8093 // Prevent the bezier going outside of the bounds of the graph 8094 model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); 8095 model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); 8096 8097 model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); 8098 model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); 8099 8100 // Now pivot the point for animation 8101 point.pivot(); 8102 }); 8103 }, 8104 8105 setHoverStyle: function(point) { 8106 // Point 8107 var dataset = this.chart.data.datasets[point._datasetIndex]; 8108 var custom = point.custom || {}; 8109 var index = point._index; 8110 var model = point._model; 8111 8112 model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); 8113 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); 8114 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); 8115 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); 8116 }, 8117 8118 removeHoverStyle: function(point) { 8119 var dataset = this.chart.data.datasets[point._datasetIndex]; 8120 var custom = point.custom || {}; 8121 var index = point._index; 8122 var model = point._model; 8123 var pointElementOptions = this.chart.options.elements.point; 8124 8125 model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); 8126 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
8126dColor, index, pointElementOptions.backgroundColor); 8127 model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); 8128 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); 8129 } 8130 }); 8131}; 8132 8133},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ 8134'use strict'; 8135 8136var defaults = require(25); 8137 8138defaults._set('scatter', { 8139 hover: { 8140 mode: 'single' 8141 }, 8142 8143 scales: { 8144 xAxes: [{ 8145 id: 'x-axis-1', // need an ID so datasets can reference the scale 8146 type: 'linear', // scatter should not use a category axis 8147 position: 'bottom' 8148 }], 8149 yAxes: [{ 8150 id: 'y-axis-1', 8151 type: 'linear', 8152 position: 'left' 8153 }] 8154 }, 8155 8156 showLines: false, 8157 8158 tooltips: { 8159 callbacks: { 8160 title: function() { 8161 return ''; // doesn't make sense for scatter since data are formatted as a point 8162 }, 8163 label: function(item) { 8164 return '(' + item.xLabel + ', ' + item.yLabel + ')'; 8165 } 8166 } 8167 } 8168}); 8169 8170module.exports = function(Chart) { 8171 8172 // Scatter charts use line controllers 8173 Chart.controllers.scatter = Chart.controllers.line; 8174 8175}; 8176 8177},{"25":25}],22:[function(require,module,exports){ 8178/* global window: false */ 8179'use strict'; 8180 8181var defaults = require(25); 8182var Element = require(26); 8183var helpers = require(45); 8184 8185defaults._set('global', { 8186 animation: { 8187 duration: 1000, 8188 easing: 'easeOutQuart', 8189 onProgress: helpers.noop, 8190 onComplete: helpers.noop 8191 } 8192}); 8193 8194module.exports = function(Chart) { 8195 8196 Chart.Animation = Element.extend({ 8197 chart: null, // the animation associated chart instance 8198 currentStep: 0, // the current animation step 8199 numSteps: 60, // default number of steps 8200 easing: '', // the easing to use for this animation 8201 render: null, // render function used by the animation service 8202 8203 onAnimationProgress: null, // user specified callback to fire on each step of the animation 8204 onAnimationComplete: null, // user specified callback to fire when the animation finishes 8205 }); 8206 8207 Chart.animationService = { 8208 frameDuration: 17, 8209 animations: [], 8210 dropFrames: 0, 8211 request: null, 8212 8213 /** 8214 * @param {Chart} chart - The chart to animate. 8215 * @param {Chart.Animation} animation - The animation that we will animate. 8216 * @param {Number} duration - The animation duration in ms. 8217 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions 8218 */ 8219 addAnimation: function(chart, animation, duration, lazy) { 8220 var animations = this.animations; 8221 var i, ilen; 8222 8223 animation.chart = chart; 8224 8225 if (!lazy) { 8226 chart.animating = true; 8227 } 8228 8229 for (i = 0, ilen = animations.length; i < ilen; ++i) { 8230 if (animations[i].chart === chart) { 8231 animations[i] = animation; 8232 return; 8233 } 8234 } 8235 8236 animations.push(animation); 8237 8238 // If there are no animations queued, manually kickstart a digest, for lack of a better word 8239 if (animations.length === 1) { 8240 this.requestAnimationFrame(); 8241 } 8242 }, 8243 8244 cancelAnimation: function(chart) { 8245 var index = helpers.findIndex(this.animations, function(animation) { 8246 return animation.chart === chart; 8247 }); 8248 8249 if (index !== -1) { 8250 this.animations.splice(index, 1); 8251 chart.animating = false; 8252 } 8253 }, 8254 8255 requestAnimationFrame: function() { 8256 var me = this; 8257 if (me.request === null) { 8258 // Skip animation frame requests until the active one is executed. 8259 // This can happen when processing mouse events, e.g. 'mousemove' 8260 // and 'mouseout' events will trigger multiple renders. 8261 me.request = helpers.requestAnimFrame.call(window, function() { 8262 me.request = null; 8263 me.startDigest(); 8264 }); 8265 } 8266 }, 8267 8268 /** 8269 * @private 8270 */ 8271 startDigest: function() { 8272 var me = this; 8273 var startTime = Date.now(); 8274 var framesToDrop = 0; 8275 8276 if (me.dropFrames > 1) { 8277 framesToDrop = Math.floor(me.dropFrames); 8278 me.dropFrames = me.dropFrames % 1; 8279 } 8280 8281 me.advance(1 + framesToDrop); 8282 8283 var endTime = Date.now(); 8284 8285 me.dropFrames += (endTime - startTime) / me.frameDuration; 8286 8287 // Do we have more stuff to animate? 8288 if (me.animations.length > 0) { 8289 me.requestAnimationFrame(); 8290 } 8291 }, 8292 8293 /** 8294 * @private 8295 */ 8296 advance: function(count) { 8297 var animations = this.animations; 8298 var animation, chart; 8299 var i = 0; 8300 8301 while (i < animations.length) { 8302 animation = animations[i]; 8303 chart = animation.chart; 8304 8305 animation.currentStep = (animation.currentStep || 0) + count; 8306 animation.currentStep = Math.min(animation.currentStep, animation.numSteps); 8307 8308 helpers.callback(animation.render, [chart, animation], chart); 8309 helpers.callback(animation.onAnimationProgress, [animation], chart); 8310 8311 if (animation.currentStep >= animation.numSteps) { 8312 helpers.callback(animation.onAnimationComplete, [animation], chart); 8313 chart.animating = false;
8314 animations.splice(i, 1); 8315 } else { 8316 ++i; 8317 } 8318 } 8319 } 8320 }; 8321 8322 /** 8323 * Provided for backward compatibility, use Chart.Animation instead 8324 * @prop Chart.Animation#animationObject 8325 * @deprecated since version 2.6.0 8326 * @todo remove at version 3 8327 */ 8328 Object.defineProperty(Chart.Animation.prototype, 'animationObject', { 8329 get: function() { 8330 return this; 8331 } 8332 }); 8333 8334 /** 8335 * Provided for backward compatibility, use Chart.Animation#chart instead 8336 * @prop Chart.Animation#chartInstance 8337 * @deprecated since version 2.6.0 8338 * @todo remove at version 3 8339 */ 8340 Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { 8341 get: function() { 8342 return this.chart; 8343 }, 8344 set: function(value) { 8345 this.chart = value; 8346 } 8347 }); 8348 8349}; 8350 8351},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ 8352'use strict'; 8353 8354var defaults = require(25); 8355var helpers = require(45); 8356var Interaction = require(28); 8357var layouts = require(30); 8358var platform = require(48); 8359var plugins = require(31); 8360 8361module.exports = function(Chart) { 8362 8363 // Create a dictionary of chart types, to allow for extension of existing types 8364 Chart.types = {}; 8365 8366 // Store a reference to each instance - allowing us to globally resize chart instances on window resize. 8367 // Destroy method on the chart will remove the instance of the chart from this reference. 8368 Chart.instances = {}; 8369 8370 // Controllers available for dataset visualization eg. bar, line, slice, etc. 8371 Chart.controllers = {}; 8372 8373 /** 8374 * Initializes the given config with global and chart default values. 8375 */ 8376 function initConfig(config) { 8377 config = config || {}; 8378 8379 // Do NOT use configMerge() for the data object because this method merges arrays 8380 // and so would change references to labels and datasets, preventing data updates. 8381 var data = config.data = config.data || {}; 8382 data.datasets = data.datasets || []; 8383 data.labels = data.labels || []; 8384 8385 config.options = helpers.configMerge( 8386 defaults.global, 8387 defaults[config.type], 8388 config.options || {}); 8389 8390 return config; 8391 } 8392 8393 /** 8394 * Updates the config of the chart 8395 * @param chart {Chart} chart to update the options for 8396 */ 8397 function updateConfig(chart) { 8398 var newOptions = chart.options; 8399 8400 helpers.each(chart.scales, function(scale) { 8401 layouts.removeBox(chart, scale); 8402 }); 8403 8404 newOptions = helpers.configMerge( 8405 Chart.defaults.global, 8406 Chart.defaults[chart.config.type], 8407 newOptions); 8408 8409 chart.options = chart.config.options = newOptions; 8410 chart.ensureScalesHaveIDs(); 8411 chart.buildOrUpdateScales(); 8412 // Tooltip 8413 chart.tooltip._options = newOptions.tooltips; 8414 chart.tooltip.initialize(); 8415 } 8416 8417 function positionIsHorizontal(position) { 8418 return position === 'top' || position === 'bottom'; 8419 } 8420 8421 helpers.extend(Chart.prototype, /** @lends Chart */ { 8422 /** 8423 * @private 8424 */ 8425 construct: function(item, config) { 8426 var me = this; 8427 8428 config = initConfig(config); 8429 8430 var context = platform.acquireContext(item, config); 8431 var canvas = context && context.canvas; 8432 var height = canvas && canvas.height; 8433 var width = canvas && canvas.width; 8434 8435 me.id = helpers.uid(); 8436 me.ctx = context; 8437 me.canvas = canvas; 8438 me.config = config; 8439 me.width = width; 8440 me.height = height; 8441 me.aspectRatio = height ? width / height : null; 8442 me.options = config.options; 8443 me._bufferedRender = false;
vendor: 10,788 bytes, lines 8444-8842
8444 8445 /** 8446 * Provided for backward compatibility, Chart and Chart.Controller have been merged, 8447 * the "instance" still need to be defined since it might be called from plugins. 8448 * @prop Chart#chart 8449 * @deprecated since version 2.6.0 8450 * @todo remove at version 3 8451 * @private 8452 */ 8453 me.chart = me; 8454 me.controller = me; // chart.chart.controller #inception 8455 8456 // Add the chart instance to the global namespace 8457 Chart.instances[me.id] = me; 8458 8459 // Define alias to the config data: `chart.data === chart.config.data` 8460 Object.defineProperty(me, 'data', { 8461 get: function() { 8462 return me.config.data; 8463 }, 8464 set: function(value) { 8465 me.config.data = value; 8466 } 8467 }); 8468 8469 if (!context || !canvas) { 8470 // The given item is not a compatible context2d element, let's return before finalizing 8471 // the chart initialization but after setting basic chart / controller properties that 8472 // can help to figure out that the chart is not valid (e.g chart.canvas !== null); 8473 // https://github.com/chartjs/Chart.js/issues/2807 8474 console.error("Failed to create chart: can't acquire context from the given item"); 8475 return; 8476 } 8477 8478 me.initialize(); 8479 me.update(); 8480 }, 8481 8482 /** 8483 * @private 8484 */ 8485 initialize: function() { 8486 var me = this; 8487 8488 // Before init plugin notification 8489 plugins.notify(me, 'beforeInit'); 8490 8491 helpers.retinaScale(me, me.options.devicePixelRatio); 8492 8493 me.bindEvents(); 8494 8495 if (me.options.responsive) { 8496 // Initial resize before chart draws (must be silent to preserve initial animations). 8497 me.resize(true); 8498 } 8499 8500 // Make sure scales have IDs and are built before we build any controllers. 8501 me.ensureScalesHaveIDs(); 8502 me.buildOrUpdateScales(); 8503 me.initToolTip(); 8504 8505 // After init plugin notification 8506 plugins.notify(me, 'afterInit'); 8507 8508 return me; 8509 }, 8510 8511 clear: function() { 8512 helpers.canvas.clear(this); 8513 return this; 8514 }, 8515 8516 stop: function() { 8517 // Stops any current animation loop occurring 8518 Chart.animationService.cancelAnimation(this); 8519 return this; 8520 }, 8521 8522 resize: function(silent) { 8523 var me = this; 8524 var options = me.options; 8525 var canvas = me.canvas; 8526 var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; 8527 8528 // the canvas render width and height will be casted to integers so make sure that 8529 // the canvas display style uses the same integer values to avoid blurring effect. 8530 8531 // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased 8532 var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); 8533 var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); 8534 8535 if (me.width === newWidth && me.height === newHeight) { 8536 return; 8537 } 8538 8539 canvas.width = me.width = newWidth; 8540 canvas.height = me.height = newHeight; 8541 canvas.style.width = newWidth + 'px'; 8542 canvas.style.height = newHeight + 'px'; 8543 8544 helpers.retinaScale(me, options.devicePixelRatio); 8545 8546 if (!silent) { 8547 // Notify any plugins about the resize 8548 var newSize = {width: newWidth, height: newHeight}; 8549 plugins.notify(me, 'resize', [newSize]); 8550 8551 // Notify of resize 8552 if (me.options.onResize) { 8553 me.options.onResize(me, newSize); 8554 } 8555 8556 me.stop(); 8557 me.update(me.options.responsiveAnimationDuration); 8558 } 8559 }, 8560 8561 ensureScalesHaveIDs: function() { 8562 var options = this.options; 8563 var scalesOptions = options.scales || {}; 8564 var scaleOptions = options.scale; 8565 8566 helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { 8567 xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); 8568 }); 8569 8570 helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { 8571 yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); 8572 }); 8573 8574 if (scaleOptions) { 8575 scaleOptions.id = scaleOptions.id || 'scale'; 8576 } 8577 }, 8578 8579 /** 8580 * Builds a map of scale ID to scale object for future lookup. 8581 */ 8582 buildOrUpdateScales: function() { 8583 var me = this; 8584 var options = me.options; 8585 var scales = me.scales || {}; 8586 var items = []; 8587 var updated = Object.keys(scales).reduce(function(obj, id) { 8588 obj[id] = false; 8589 return obj; 8590 }, {}); 8591 8592 if (options.scales) { 8593 items = items.concat( 8594 (options.scales.xAxes || []).map(function(xAxisOptions) { 8595 return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; 8596 }), 8597 (options.scales.yAxes || []).map(function(yAxisOptions) { 8598 return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; 8599 }) 8600 ); 8601 } 8602 8603 if (options.scale) { 8604 items.push({ 8605 options: options.scale, 8606 dtype: 'radialLinear', 8607 isDefault: true, 8608 dposition: 'chartArea' 8609 }); 8610 } 8611 8612 helpers.each(items, function(item) { 8613 var scaleOptions = item.options; 8614 var id = scaleOptions.id; 8615 var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); 8616 8617 if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { 8618 scaleOptions.position = item.dposition; 8619 } 8620 8621 updated[id] = true; 8622 var scale = null; 8623 if (id in scales && scales[id].type === scaleType) { 8624 scale = scales[id]; 8625 scale.options = scaleOptions; 8626 scale.ctx = me.ctx; 8627 scale.chart = me; 8628 } else { 8629 var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); 8630 if (!scaleClass) { 8631 return; 8632 } 8633 scale = new scaleClass({ 8634 id: id, 8635 type: scaleType, 8636 options: scaleOptions, 8637 ctx: me.ctx, 8638 chart: me 8639 }); 8640 scales[scale.id] = scale; 8641 } 8642 8643 scale.mergeTicksOptions(); 8644 8645 // TODO(SB): I think we should be able to remove this custom case (options.scale) 8646 // and consider it as a regular scale part of the "scales"" map only! This would 8647 // make the logic easier and remove some useless? custom code. 8648 if (item.isDefault) { 8649 me.scale = scale; 8650 } 8651 }); 8652 // clear up discarded scales 8653 helpers.each(updated, function(hasUpdated, id) { 8654 if (!hasUpdated) { 8655 delete scales[id]; 8656 } 8657 }); 8658 8659 me.scales = scales; 8660 8661 Chart.scaleService.addScalesToLayout(this); 8662 }, 8663 8664 buildOrUpdateControllers: function() { 8665 var me = this; 8666 var types = []; 8667 var newControllers = []; 8668 8669 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8670 var meta = me.getDatasetMeta(datasetIndex); 8671 var type = dataset.type || me.config.type; 8672 8673 if (meta.type && meta.type !== type) { 8674 me.destroyDatasetMeta(datasetIndex); 8675 meta = me.getDatasetMeta(datasetIndex); 8676 } 8677 meta.type = type; 8678 8679 types.push(meta.type); 8680 8681 if (meta.controller) { 8682 meta.controller.updateIndex(datasetIndex); 8683 meta.controller.linkScales(); 8684 } else { 8685 var ControllerClass = Chart.controllers[meta.type]; 8686 if (ControllerClass === undefined) { 8687 throw new Error('"' + meta.type + '" is not a chart type.'); 8688 } 8689 8690 meta.controller = new ControllerClass(me, datasetIndex); 8691 newControllers.push(meta.controller); 8692 } 8693 }, me); 8694 8695 return newControllers; 8696 }, 8697 8698 /** 8699 * Reset the elements of all datasets 8700 * @private 8701 */ 8702 resetElements: function() { 8703 var me = this; 8704 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8705 me.getDatasetMeta(datasetIndex).controller.reset(); 8706 }, me); 8707 }, 8708 8709 /** 8710 * Resets the chart back to it's state before the initial animation 8711 */ 8712 reset: function() { 8713 this.resetElements(); 8714 this.tooltip.initialize(); 8715 }, 8716 8717 update: function(config) { 8718 var me = this; 8719 8720 if (!config || typeof config !== 'object') { 8721 // backwards compatibility 8722 config = { 8723 duration: config, 8724 lazy: arguments[1] 8725 }; 8726 } 8727 8728 updateConfig(me); 8729 8730 // plugins options references might have change, let's invalidate the cache 8731 // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 8732 plugins._invalidate(me); 8733 8734 if (plugins.notify(me, 'beforeUpdate') === false) { 8735 return; 8736 } 8737 8738 // In case the entire data object changed 8739 me.tooltip._data = me.data; 8740 8741 // Make sure dataset controllers are updated and new controllers are reset 8742 var newControllers = me.buildOrUpdateControllers(); 8743 8744 // Make sure all dataset controllers have correct meta data counts 8745 helpers.each(me.data.datasets, function(dataset, datasetIndex) { 8746 me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); 8747 }, me); 8748 8749 me.updateLayout(); 8750 8751 // Can only reset the new controllers after the scales have been updated 8752 if (me.options.animation && me.options.animation.duration) { 8753 helpers.each(newControllers, function(controller) { 8754 controller.reset(); 8755 }); 8756 } 8757 8758 me.updateDatasets(); 8759 8760 // Need to reset tooltip in case it is displayed with elements that are removed 8761 // after update. 8762 me.tooltip.initialize(); 8763 8764 // Last active contains items that were previously in the tooltip. 8765 // When we reset the tooltip, we need to clear it 8766 me.lastActive = []; 8767 8768 // Do this before render so that any plugins that need final scale updates can use it 8769 plugins.notify(me, 'afterUpdate'); 8770 8771 if (me._bufferedRender) { 8772 me._bufferedRequest = { 8773 duration: config.duration, 8774 easing: config.easing, 8775 lazy: config.lazy 8776 }; 8777 } else { 8778 me.render(config); 8779 } 8780 }, 8781 8782 /** 8783 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` 8784 * hook, in which case, plugins will not be called on `afterLayout`. 8785 * @private 8786 */ 8787 updateLayout: function() { 8788 var me = this; 8789 8790 if (plugins.notify(me, 'beforeLayout') === false) { 8791 return; 8792 } 8793 8794 layouts.update(this, this.width, this.height); 8795 8796 /** 8797 * Provided for backward compatibility, use `afterLayout` instead. 8798 * @method IPlugin#afterScaleUpdate 8799 * @deprecated since version 2.5.0 8800 * @todo remove at version 3 8801 * @private 8802 */ 8803 plugins.notify(me, 'afterScaleUpdate'); 8804 plugins.notify(me, 'afterLayout'); 8805 }, 8806 8807 /** 8808 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` 8809 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. 8810 * @private 8811 */ 8812 updateDatasets: function() { 8813 var me = this; 8814 8815 if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { 8816 return; 8817 } 8818 8819 for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { 8820 me.updateDataset(i); 8821 } 8822 8823 plugins.notify(me, 'afterDatasetsUpdate'); 8824 }, 8825 8826 /** 8827 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` 8828 * hook, in which case, plugins will not be called on `afterDatasetUpdate`. 8829 * @private 8830 */ 8831 updateDataset: function(index) { 8832 var me = this; 8833 var meta = me.getDatasetMeta(index); 8834 var args = { 8835 meta: meta, 8836 index: index 8837 }; 8838 8839 if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { 8840 return; 8841 } 8842
8843 meta.controller.update(); 8844 8845 plugins.notify(me, 'afterDatasetUpdate', [args]); 8846 }, 8847 8848 render: function(config) { 8849 var me = this; 8850 8851 if (!config || typeof config !== 'object') { 8852 // backwards compatibility 8853 config = { 8854 duration: config, 8855 lazy: arguments[1] 8856 }; 8857 } 8858 8859 var duration = config.duration; 8860 var lazy = config.lazy; 8861 8862 if (plugins.notify(me, 'beforeRender') === false) { 8863 return; 8864 } 8865 8866 var animationOptions = me.options.animation; 8867 var onComplete = function(animation) { 8868 plugins.notify(me, 'afterRender'); 8869 helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); 8870 }; 8871 8872 if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { 8873 var animation = new Chart.Animation({ 8874 numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps 8875 easing: config.easing || animationOptions.easing, 8876 8877 render: function(chart, animationObject) { 8878 var easingFunction = helpers.easing.effects[animationObject.easing]; 8879 var currentStep = animationObject.currentStep; 8880 var stepDecimal = currentStep / animationObject.numSteps; 8881 8882 chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); 8883 }, 8884 8885 onAnimationProgress: animationOptions.onProgress, 8886 onAnimationComplete: onComplete 8887 }); 8888 8889 Chart.animationService.addAnimation(me, animation, duration, lazy); 8890 } else { 8891 me.draw(); 8892 8893 // See https://github.com/chartjs/Chart.js/issues/3781 8894 onComplete(new Chart.Animation({numSteps: 0, chart: me})); 8895 } 8896 8897 return me; 8898 }, 8899 8900 draw: function(easingValue) { 8901 var me = this; 8902 8903 me.clear(); 8904 8905 if (helpers.isNullOrUndef(easingValue)) { 8906 easingValue = 1; 8907 } 8908 8909 me.transition(easingValue); 8910 8911 if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { 8912 return; 8913 } 8914 8915 // Draw all the scales 8916 helpers.each(me.boxes, function(box) { 8917 box.draw(me.chartArea); 8918 }, me); 8919 8920 if (me.scale) { 8921 me.scale.draw(); 8922 } 8923 8924 me.drawDatasets(easingValue); 8925 me._drawTooltip(easingValue); 8926 8927 plugins.notify(me, 'afterDraw', [easingValue]); 8928 }, 8929 8930 /** 8931 * @private 8932 */ 8933 transition: function(easingValue) { 8934 var me = this; 8935 8936 for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { 8937 if (me.isDatasetVisible(i)) { 8938 me.getDatasetMeta(i).controller.transition(easingValue); 8939 } 8940 } 8941 8942 me.tooltip.transition(easingValue); 8943 }, 8944 8945 /** 8946 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` 8947 * hook, in which case, plugins will not be called on `afterDatasetsDraw`. 8948 * @private 8949 */ 8950 drawDatasets: function(easingValue) { 8951 var me = this; 8952 8953 if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { 8954 return; 8955 } 8956 8957 // Draw datasets reversed to support proper line stacking 8958 for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { 8959 if (me.isDatasetVisible(i)) { 8960 me.drawDataset(i, easingValue); 8961 } 8962 } 8963 8964 plugins.notify(me, 'afterDatasetsDraw', [easingValue]); 8965 }, 8966 8967 /** 8968 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` 8969 * hook, in which case, plugins will not be called on `afterDatasetDraw`. 8970 * @private 8971 */ 8972 drawDataset: function(index, easingValue) { 8973 var me = this; 8974 var meta = me.getDatasetMeta(index); 8975 var args = { 8976 meta: meta, 8977 index: index, 8978 easingValue: easingValue 8979 }; 8980 8981 if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { 8982 return; 8983 } 8984 8985 meta.controller.draw(easingValue); 8986 8987 plugins.notify(me, 'afterDatasetDraw', [args]); 8988 }, 8989 8990 /** 8991 * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` 8992 * hook, in which case, plugins will not be called on `afterTooltipDraw`. 8993 * @private 8994 */ 8995 _drawTooltip: function(easingValue) { 8996 var me = this; 8997 var tooltip = me.tooltip; 8998 var args = { 8999 tooltip: tooltip, 9000 easingValue: easingValue 9001 }; 9002 9003 if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { 9004 return; 9005 } 9006 9007 tooltip.draw(); 9008 9009 plugins.notify(me, 'afterTooltipDraw', [args]); 9010 }, 9011 9012 // Get the single element that was clicked on
vendor: 45,871 bytes, lines 9013-10617
9013 // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw 9014 getElementAtEvent: function(e) { 9015 return Interaction.modes.single(this, e); 9016 }, 9017 9018 getElementsAtEvent: function(e) { 9019 return Interaction.modes.label(this, e, {intersect: true}); 9020 }, 9021 9022 getElementsAtXAxis: function(e) { 9023 return Interaction.modes['x-axis'](this, e, {intersect: true}); 9024 }, 9025 9026 getElementsAtEventForMode: function(e, mode, options) { 9027 var method = Interaction.modes[mode]; 9028 if (typeof method === 'function') { 9029 return method(this, e, options); 9030 } 9031 9032 return []; 9033 }, 9034 9035 getDatasetAtEvent: function(e) { 9036 return Interaction.modes.dataset(this, e, {intersect: true}); 9037 }, 9038 9039 getDatasetMeta: function(datasetIndex) { 9040 var me = this; 9041 var dataset = me.data.datasets[datasetIndex]; 9042 if (!dataset._meta) { 9043 dataset._meta = {}; 9044 } 9045 9046 var meta = dataset._meta[me.id]; 9047 if (!meta) { 9048 meta = dataset._meta[me.id] = { 9049 type: null, 9050 data: [], 9051 dataset: null, 9052 controller: null, 9053 hidden: null, // See isDatasetVisible() comment 9054 xAxisID: null, 9055 yAxisID: null 9056 }; 9057 } 9058 9059 return meta; 9060 }, 9061 9062 getVisibleDatasetCount: function() { 9063 var count = 0; 9064 for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { 9065 if (this.isDatasetVisible(i)) { 9066 count++; 9067 } 9068 } 9069 return count; 9070 }, 9071 9072 isDatasetVisible: function(datasetIndex) { 9073 var meta = this.getDatasetMeta(datasetIndex); 9074 9075 // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, 9076 // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. 9077 return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; 9078 }, 9079 9080 generateLegend: function() { 9081 return this.options.legendCallback(this); 9082 }, 9083 9084 /** 9085 * @private 9086 */ 9087 destroyDatasetMeta: function(datasetIndex) { 9088 var id = this.id; 9089 var dataset = this.data.datasets[datasetIndex]; 9090 var meta = dataset._meta && dataset._meta[id]; 9091 9092 if (meta) { 9093 meta.controller.destroy(); 9094 delete dataset._meta[id]; 9095 } 9096 }, 9097 9098 destroy: function() { 9099 var me = this; 9100 var canvas = me.canvas; 9101 var i, ilen; 9102 9103 me.stop(); 9104 9105 // dataset controllers need to cleanup associated data 9106 for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { 9107 me.destroyDatasetMeta(i); 9108 } 9109 9110 if (canvas) { 9111 me.unbindEvents(); 9112 helpers.canvas.clear(me); 9113 platform.releaseContext(me.ctx); 9114 me.canvas = null; 9115 me.ctx = null; 9116 } 9117 9118 plugins.notify(me, 'destroy'); 9119 9120 delete Chart.instances[me.id]; 9121 }, 9122 9123 toBase64Image: function() { 9124 return this.canvas.toDataURL.apply(this.canvas, arguments); 9125 }, 9126 9127 initToolTip: function() { 9128 var me = this; 9129 me.tooltip = new Chart.Tooltip({ 9130 _chart: me, 9131 _chartInstance: me, // deprecated, backward compatibility 9132 _data: me.data, 9133 _options: me.options.tooltips 9134 }, me); 9135 }, 9136 9137 /** 9138 * @private 9139 */ 9140 bindEvents: function() { 9141 var me = this; 9142 var listeners = me._listeners = {}; 9143 var listener = function() { 9144 me.eventHandler.apply(me, arguments); 9145 }; 9146 9147 helpers.each(me.options.events, function(type) { 9148 platform.addEventListener(me, type, listener); 9149 listeners[type] = listener; 9150 }); 9151 9152 // Elements used to detect size change should not be injected for non responsive charts. 9153 // See https://github.com/chartjs/Chart.js/issues/2210 9154 if (me.options.responsive) { 9155 listener = function() { 9156 me.resize(); 9157 }; 9158 9159 platform.addEventListener(me, 'resize', listener); 9160 listeners.resize = listener; 9161 } 9162 }, 9163 9164 /** 9165 * @private 9166 */ 9167 unbindEvents: function() { 9168 var me = this; 9169 var listeners = me._listeners; 9170 if (!listeners) { 9171 return; 9172 } 9173 9174 delete me._listeners; 9175 helpers.each(listeners, function(listener, type) { 9176 platform.removeEventListener(me, type, listener); 9177 }); 9178 }, 9179 9180 updateHoverStyle: function(elements, mode, enabled) { 9181 var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; 9182 var element, i, ilen; 9183 9184 for (i = 0, ilen = elements.length; i < ilen; ++i) { 9185 element = elements[i]; 9186 if (element) { 9187 this.getDatasetMeta(element._datasetIndex).controller[method](element); 9188 } 9189 } 9190 }, 9191 9192 /** 9193 * @private 9194 */ 9195 eventHandler: function(e) { 9196 var me = this; 9197 var tooltip = me.tooltip; 9198 9199 if (plugins.notify(me, 'beforeEvent', [e]) === false) { 9200 return; 9201 } 9202 9203 // Buffer any update calls so that renders do not occur 9204 me._bufferedRender = true; 9205 me._bufferedRequest = null; 9206 9207 var changed = me.handleEvent(e); 9208 // for smooth tooltip animations issue #4989 9209 // the tooltip should be the source of change 9210 // Animation check workaround: 9211 // tooltip._start will be null when tooltip isn't animating 9212 if (tooltip) { 9213 changed = tooltip._start 9214 ? tooltip.handleEvent(e) 9215 : changed | tooltip.handleEvent(e); 9216 } 9217 9218 plugins.notify(me, 'afterEvent', [e]); 9219 9220 var bufferedRequest = me._bufferedRequest; 9221 if (bufferedRequest) { 9222 // If we have an update that was triggered, we need to do a normal render 9223 me.render(bufferedRequest); 9224 } else if (changed && !me.animating) { 9225 // If entering, leaving, or changing elements, animate the change via pivot 9226 me.stop(); 9227 9228 // We only need to render at this point. Updating will cause scales to be 9229 // recomputed generating flicker & using more memory than necessary. 9230 me.render(me.options.hover.animationDuration, true); 9231 } 9232 9233 me._bufferedRender = false; 9234 me._bufferedRequest = null; 9235 9236 return me; 9237 }, 9238 9239 /** 9240 * Handle an event 9241 * @private 9242 * @param {IEvent} event the event to handle 9243 * @return {Boolean} true if the chart needs to re-render 9244 */ 9245 handleEvent: function(e) { 9246 var me = this; 9247 var options = me.options || {}; 9248 var hoverOptions = options.hover; 9249 var changed = false; 9250 9251 me.lastActive = me.lastActive || []; 9252 9253 // Find Active Elements for hover and tooltips 9254 if (e.type === 'mouseout') { 9255 me.active = []; 9256 } else { 9257 me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); 9258 } 9259 9260 // Invoke onHover hook 9261 // Need to call with native event here to not break backwards compatibility 9262 helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); 9263 9264 if (e.type === 'mouseup' || e.type === 'click') { 9265 if (options.onClick) { 9266 // Use e.native here for backwards compatibility 9267 options.onClick.call(me, e.native, me.active); 9268 } 9269 } 9270 9271 // Remove styling for last active (even if it may still be active) 9272 if (me.lastActive.length) { 9273 me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); 9274 } 9275 9276 // Built in hover styling 9277 if (me.active.length && hoverOptions.mode) { 9278 me.updateHoverStyle(me.active, hoverOptions.mode, true); 9279 } 9280 9281 changed = !helpers.arrayEquals(me.active, me.lastActive); 9282 9283 // Remember Last Actives 9284 me.lastActive = me.active; 9285 9286 return changed; 9287 } 9288 }); 9289 9290 /** 9291 * Provided for backward compatibility, use Chart instead. 9292 * @class Chart.Controller 9293 * @deprecated since version 2.6.0 9294 * @todo remove at version 3 9295 * @private 9296 */ 9297 Chart.Controller = Chart; 9298}; 9299 9300},{"25":25,"28":28,"30":30,"31":31,"45":45,"48":48}],24:[function(require,module,exports){ 9301'use strict'; 9302 9303var helpers = require(45); 9304 9305module.exports = function(Chart) { 9306 9307 var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; 9308 9309 /** 9310 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', 9311 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are 9312 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. 9313 */ 9314 function listenArrayEvents(array, listener) { 9315 if (array._chartjs) { 9316 array._chartjs.listeners.push(listener); 9317 return; 9318 } 9319 9320 Object.defineProperty(array, '_chartjs', { 9321 configurable: true, 9322 enumerable: false, 9323 value: { 9324 listeners: [listener] 9325 } 9326 }); 9327 9328 arrayEvents.forEach(function(key) { 9329 var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); 9330 var base = array[key]; 9331 9332 Object.defineProperty(array, key, { 9333 configurable: true, 9334 enumerable: false, 9335 value: function() { 9336 var args = Array.prototype.slice.call(arguments); 9337 var res = base.apply(this, args); 9338 9339 helpers.each(array._chartjs.listeners, function(object) { 9340 if (typeof object[method] === 'function') { 9341 object[method].apply(object, args); 9342 } 9343 }); 9344 9345 return res; 9346 } 9347 }); 9348 }); 9349 } 9350 9351 /** 9352 * Removes the given array event listener and cleanup extra attached properties (such as 9353 * the _chartjs stub and overridden methods) if array doesn't have any more listeners. 9354 */ 9355 function unlistenArrayEvents(array, listener) { 9356 var stub = array._chartjs; 9357 if (!stub) { 9358 return; 9359 } 9360 9361 var listeners = stub.listeners; 9362 var index = listeners.indexOf(listener); 9363 if (index !== -1) { 9364 listeners.splice(index, 1); 9365 } 9366 9367 if (listeners.length > 0) { 9368 return; 9369 } 9370 9371 arrayEvents.forEach(function(key) { 9372 delete array[key]; 9373 }); 9374 9375 delete array._chartjs; 9376 } 9377 9378 // Base class for all dataset controllers (line, bar, etc) 9379 Chart.DatasetController = function(chart, datasetIndex) { 9380 this.initialize(chart, datasetIndex); 9381 }; 9382 9383 helpers.extend(Chart.DatasetController.prototype, { 9384 9385 /** 9386 * Element type used to generate a meta dataset (e.g. Chart.element.Line). 9387 * @type {Chart.core.element} 9388 */ 9389 datasetElementType: null, 9390 9391 /** 9392 * Element type used to generate a meta data (e.g. Chart.element.Point). 9393 * @type {Chart.core.element} 9394 */ 9395 dataElementType: null, 9396 9397 initialize: function(chart, datasetIndex) { 9398 var me = this; 9399 me.chart = chart; 9400 me.index = datasetIndex; 9401 me.linkScales(); 9402 me.addElements(); 9403 }, 9404 9405 updateIndex: function(datasetIndex) { 9406 this.index = datasetIndex; 9407 }, 9408 9409 linkScales: function() { 9410 var me = this; 9411 var meta = me.getMeta(); 9412 var dataset = me.getDataset(); 9413 9414 if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) { 9415 meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; 9416 } 9417 if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) { 9418 meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; 9419 } 9420 }, 9421 9422 getDataset: function() { 9423 return this.chart.data.datasets[this.index]; 9424 }, 9425 9426 getMeta: function() { 9427 return this.chart.getDatasetMeta(this.index); 9428 }, 9429 9430 getScaleForId: function(scaleID) { 9431 return this.chart.scales[scaleID]; 9432 }, 9433 9434 reset: function() { 9435 this.update(true); 9436 }, 9437 9438 /** 9439 * @private 9440 */ 9441 destroy: function() { 9442 if (this._data) { 9443 unlistenArrayEvents(this._data, this); 9444 } 9445 }, 9446 9447 createMetaDataset: function() { 9448 var me = this; 9449 var type = me.datasetElementType; 9450 return type && new type({ 9451 _chart: me.chart, 9452 _datasetIndex: me.index 9453 }); 9454 }, 9455 9456 createMetaData: function(index) { 9457 var me = this; 9458 var type = me.dataElementType; 9459 return type && new type({ 9460 _chart: me.chart, 9461 _datasetIndex: me.index, 9462 _index: index 9463 }); 9464 }, 9465 9466 addElements: function() { 9467 var me = this; 9468 var meta = me.getMeta(); 9469 var data = me.getDataset().data || []; 9470 var metaData = meta.data; 9471 var i, ilen; 9472 9473 for (i = 0, ilen = data.length; i < ilen; ++i) { 9474 metaData[i] = metaData[i] || me.createMetaData(i); 9475 } 9476 9477 meta.dataset = meta.dataset || me.createMetaDataset(); 9478 }, 9479 9480 addElementAndReset: function(index) { 9481 var element = this.createMetaData(index); 9482 this.getMeta().data.splice(index, 0, element); 9483 this.updateElement(element, index, true); 9484 }, 9485 9486 buildOrUpdateElements: function() { 9487 var me = this; 9488 var dataset = me.getDataset(); 9489 var data = dataset.data || (dataset.data = []); 9490 9491 // In order to correctly handle data addition/deletion animation (an thus simulate 9492 // real-time charts), we need to monitor these data modifications and synchronize 9493 // the internal meta data accordingly. 9494 if (me._data !== data) { 9495 if (me._data) { 9496 // This case happens when the user replaced the data array instance. 9497 unlistenArrayEvents(me._data, me); 9498 } 9499 9500 listenArrayEvents(data, me); 9501 me._data = data; 9502 } 9503 9504 // Re-sync meta data in case the user replaced the data array or if we missed 9505 // any updates and so make sure that we handle number of datapoints changing. 9506 me.resyncElements(); 9507 }, 9508 9509 update: helpers.noop, 9510 9511 transition: function(easingValue) { 9512 var meta = this.getMeta(); 9513 var elements = meta.data || []; 9514 var ilen = elements.length; 9515 var i = 0; 9516 9517 for (; i < ilen; ++i) { 9518 elements[i].transition(easingValue); 9519 } 9520 9521 if (meta.dataset) { 9522 meta.dataset.transition(easingValue); 9523 } 9524 }, 9525 9526 draw: function() { 9527 var meta = this.getMeta(); 9528 var elements = meta.data || []; 9529 var ilen = elements.length; 9530 var i = 0; 9531 9532 if (meta.dataset) { 9533 meta.dataset.draw(); 9534 } 9535 9536 for (; i < ilen; ++i) { 9537 elements[i].draw(); 9538 } 9539 }, 9540 9541 removeHoverStyle: function(element, elementOpts) { 9542 var dataset = this.chart.data.datasets[element._datasetIndex]; 9543 var index = element._index; 9544 var custom = element.custom || {}; 9545 var valueOrDefault = helpers.valueAtIndexOrDefault; 9546 var model = element._model; 9547 9548 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); 9549 model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); 9550 model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); 9551 }, 9552 9553 setHoverStyle: function(element) { 9554 var dataset = this.chart.data.datasets[element._datasetIndex]; 9555 var index = element._index; 9556 var custom = element.custom || {}; 9557 var valueOrDefault = helpers.valueAtIndexOrDefault; 9558 var getHoverColor = helpers.getHoverColor; 9559 var model = element._model; 9560 9561 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); 9562 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); 9563 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); 9564 }, 9565 9566 /** 9567 * @private 9568 */ 9569 resyncElements: function() { 9570 var me = this; 9571 var meta = me.getMeta(); 9572 var data = me.getDataset().data; 9573 var numMeta = meta.data.length; 9574 var numData = data.length; 9575 9576 if (numData < numMeta) { 9577 meta.data.splice(numData, numMeta - numData); 9578 } else if (numData > numMeta) { 9579 me.insertElements(numMeta, numData - numMeta); 9580 } 9581 }, 9582 9583 /** 9584 * @private 9585 */ 9586 insertElements: function(start, count) { 9587 for (var i = 0; i < count; ++i) { 9588 this.addElementAndReset(start + i); 9589 } 9590 }, 9591 9592 /** 9593 * @private 9594 */ 9595 onDataPush: function() { 9596 this.insertElements(this.getDataset().data.length - 1, arguments.length); 9597 }, 9598 9599 /** 9600 * @private 9601 */ 9602 onDataPop: function() { 9603 this.getMeta().data.pop(); 9604 }, 9605 9606 /** 9607 * @private 9608 */ 9609 onDataShift: function() { 9610 this.getMeta().data.shift(); 9611 }, 9612 9613 /** 9614 * @private 9615 */ 9616 onDataSplice: function(start, count) { 9617 this.getMeta().data.splice(start, count); 9618 this.insertElements(start, arguments.length - 2); 9619 }, 9620 9621 /** 9622 * @private 9623 */ 9624 onDataUnshift: function() { 9625 this.insertElements(0, arguments.length); 9626 } 9627 }); 9628 9629 Chart.DatasetController.extend = helpers.inherits; 9630}; 9631 9632},{"45":45}],25:[function(require,module,exports){ 9633'use strict'; 9634 9635var helpers = require(45); 9636 9637module.exports = { 9638 /** 9639 * @private 9640 */ 9641 _set: function(scope, values) { 9642 return helpers.merge(this[scope] || (this[scope] = {}), values); 9643 } 9644}; 9645 9646},{"45":45}],26:[function(require,module,exports){ 9647'use strict'; 9648 9649var color = require(2); 9650var helpers = require(45); 9651 9652function interpolate(start, view, model, ease) { 9653 var keys = Object.keys(model); 9654 var i, ilen, key, actual, origin, target, type, c0, c1; 9655 9656 for (i = 0, ilen = keys.length; i < ilen; ++i) { 9657 key = keys[i]; 9658 9659 target = model[key]; 9660 9661 // if a value is added to the model after pivot() has been called, the view 9662 // doesn't contain it, so let's initialize the view to the target value. 9663 if (!view.hasOwnProperty(key)) { 9664 view[key] = target; 9665 } 9666 9667 actual = view[key]; 9668 9669 if (actual === target || key[0] === '_') { 9670 continue; 9671 } 9672 9673 if (!start.hasOwnProperty(key)) { 9674 start[key] = actual; 9675 } 9676 9677 origin = start[key]; 9678 9679 type = typeof target; 9680 9681 if (type === typeof origin) { 9682 if (type === 'string') { 9683 c0 = color(origin); 9684 if (c0.valid) { 9685 c1 = color(target); 9686 if (c1.valid) { 9687 view[key] = c1.mix(c0, ease).rgbString(); 9688 continue; 9689 } 9690 } 9691 } else if (type === 'number' && isFinite(origin) && isFinite(target)) { 9692 view[key] = origin + (target - origin) * ease; 9693 continue; 9694 } 9695 } 9696 9697 view[key] = target; 9698 } 9699} 9700 9701var Element = function(configuration) { 9702 helpers.extend(this, configuration); 9703 this.initialize.apply(this, arguments); 9704}; 9705 9706helpers.extend(Element.prototype, { 9707 9708 initialize: function() { 9709 this.hidden = false; 9710 }, 9711 9712 pivot: function() { 9713 var me = this; 9714 if (!me._view) { 9715 me._view = helpers.clone(me._model); 9716 } 9717 me._start = {}; 9718 return me; 9719 }, 9720 9721 transition: function(ease) { 9722 var me = this; 9723 var model = me._model; 9724 var start = me._start; 9725 var view = me._view; 9726 9727 // No animation -> No Transition 9728 if (!model || ease === 1) { 9729 me._view = model; 9730 me._start = null; 9731 return me; 9732 } 9733 9734 if (!view) { 9735 view = me._view = {}; 9736 } 9737 9738 if (!start) { 9739 start = me._start = {}; 9740 } 9741 9742 interpolate(start, view, model, ease); 9743 9744 return me; 9745 }, 9746 9747 tooltipPosition: function() { 9748 return { 9749 x: this._model.x, 9750 y: this._model.y 9751 }; 9752 }, 9753 9754 hasValue: function() { 9755 return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); 9756 } 9757}); 9758 9759Element.extend = helpers.inherits; 9760 9761module.exports = Element; 9762 9763},{"2":2,"45":45}],27:[function(require,module,exports){ 9764/* global window: false */ 9765/* global document: false */ 9766'use strict'; 9767 9768var color = require(2); 9769var defaults = require(25); 9770var helpers = require(45); 9771 9772module.exports = function(Chart) { 9773 9774 // -- Basic js utility methods 9775 9776 helpers.configMerge = function(/* objects ... */) { 9777 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { 9778 merger: function(key, target, source, options) { 9779 var tval = target[key] || {}; 9780 var sval = source[key]; 9781 9782 if (key === 'scales') { 9783 // scale config merging is complex. Add our own function here for that 9784 target[key] = helpers.scaleMerge(tval, sval); 9785 } else if (key === 'scale') { 9786 // used in polar area & radar charts since there is only one scale 9787 target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]); 9788 } else { 9789 helpers._merger(key, target, source, options); 9790 } 9791 } 9792 }); 9793 }; 9794 9795 helpers.scaleMerge = function(/* objects ... */) { 9796 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { 9797 merger: function(key, target, source, options) { 9798 if (key === 'xAxes' || key === 'yAxes') { 9799 var slen = source[key].length; 9800 var i, type, scale; 9801 9802 if (!target[key]) { 9803 target[key] = []; 9804 } 9805 9806 for (i = 0; i < slen; ++i) { 9807 scale = source[key][i]; 9808 type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); 9809 9810 if (i >= target[key].length) { 9811 target[key].push({}); 9812 } 9813 9814 if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { 9815 // new/untyped scale or type changed: let's apply the new defaults 9816 // then merge source scale to correctly overwrite the defaults. 9817 helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]); 9818 } else { 9819 // scales type are the same 9820 helpers.merge(target[key][i], scale); 9821 } 9822 } 9823 } else { 9824 helpers._merger(key, target, source, options); 9825 } 9826 } 9827 }); 9828 }; 9829 9830 helpers.where = function(collection, filterCallback) { 9831 if (helpers.isArray(collection) && Array.prototype.filter) { 9832 return collection.filter(filterCallback); 9833 } 9834 var filtered = []; 9835 9836 helpers.each(collection, function(item) { 9837 if (filterCallback(item)) { 9838 filtered.push(item); 9839 } 9840 }); 9841 9842 return filtered; 9843 }; 9844 helpers.findIndex = Array.prototype.findIndex ? 9845 function(array, callback, scope) { 9846 return array.findIndex(callback, scope); 9847 } : 9848 function(array, callback, scope) { 9849 scope = scope === undefined ? array : scope; 9850 for (var i = 0, ilen = array.length; i < ilen; ++i) { 9851 if (callback.call(scope, array[i], i, array)) { 9852 return i; 9853 } 9854 } 9855 return -1; 9856 }; 9857 helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { 9858 // Default to start of the array 9859 if (helpers.isNullOrUndef(startIndex)) { 9860 startIndex = -1; 9861 } 9862 for (var i = startIndex + 1; i < arrayToSearch.length; i++) { 9863 var currentItem = arrayToSearch[i]; 9864 if (filterCallback(currentItem)) { 9865 return currentItem; 9866 } 9867 } 9868 }; 9869 helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { 9870 // Default to end of the array 9871 if (helpers.isNullOrUndef(startIndex)) { 9872 startIndex = arrayToSearch.length; 9873 } 9874 for (var i = startIndex - 1; i >= 0; i--) { 9875 var currentItem = arrayToSearch[i]; 9876 if (filterCallback(currentItem)) { 9877 return currentItem; 9878 } 9879 } 9880 }; 9881 9882 // -- Math methods 9883 helpers.isNumber = function(n) { 9884 return !isNaN(parseFloat(n)) && isFinite(n); 9885 }; 9886 helpers.almostEquals = function(x, y, epsilon) { 9887 return Math.abs(x - y) < epsilon; 9888 }; 9889 helpers.almostWhole = function(x, epsilon) { 9890 var rounded = Math.round(x); 9891 return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); 9892 }; 9893 helpers.max = function(array) { 9894 return array.reduce(function(max, value) { 9895 if (!isNaN(value)) { 9896 return Math.max(max, value); 9897 } 9898 return max; 9899 }, Number.NEGATIVE_INFINITY); 9900 }; 9901 helpers.min = function(array) { 9902 return array.reduce(function(min, value) { 9903 if (!isNaN(value)) { 9904 return Math.min(min, value); 9905 } 9906 return min; 9907 }, Number.POSITIVE_INFINITY); 9908 }; 9909 helpers.sign = Math.sign ? 9910 function(x) { 9911 return Math.sign(x); 9912 } : 9913 function(x) { 9914 x = +x; // convert to a number 9915 if (x === 0 || isNaN(x)) { 9916 return x; 9917 } 9918 return x > 0 ? 1 : -1; 9919 }; 9920 helpers.log10 = Math.log10 ? 9921 function(x) { 9922 return Math.log10(x); 9923 } : 9924 function(x) { 9925 var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10. 9926 // Check for whole powers of 10, 9927 // which due to floating point rounding error should be corrected. 9928 var powerOf10 = Math.round(exponent); 9929 var isPowerOf10 = x === Math.pow(10, powerOf10); 9930 9931 return isPowerOf10 ? powerOf10 : exponent; 9932 }; 9933 helpers.toRadians = function(degrees) { 9934 return degrees * (Math.PI / 180); 9935 }; 9936 helpers.toDegrees = function(radians) { 9937 return radians * (180 / Math.PI); 9938 }; 9939 // Gets the angle from vertical upright to the point about a centre. 9940 helpers.getAngleFromPoint = function(centrePoint, anglePoint) { 9941 var distanceFromXCenter = anglePoint.x - centrePoint.x; 9942 var distanceFromYCenter = anglePoint.y - centrePoint.y; 9943 var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); 9944 9945 var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); 9946 9947 if (angle < (-0.5 * Math.PI)) { 9948 angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] 9949 } 9950 9951 return { 9952 angle: angle, 9953 distance: radialDistanceFromCenter 9954 }; 9955 }; 9956 helpers.distanceBetweenPoints = function(pt1, pt2) { 9957 return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); 9958 }; 9959 helpers.aliasPixel = function(pixelWidth) { 9960 return (pixelWidth % 2 === 0) ? 0 : 0.5; 9961 }; 9962 helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { 9963 // Props to Rob Spencer at scaled innovation for his post on splining between points 9964 // http://scaledinnovation.com/analytics/splines/aboutSplines.html 9965 9966 // This function must also respect "skipped" points 9967 9968 var previous = firstPoint.skip ? middlePoint : firstPoint; 9969 var current = middlePoint; 9970 var next = afterPoint.skip ? middlePoint : afterPoint; 9971 9972 var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); 9973 var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); 9974 9975 var s01 = d01 / (d01 + d12); 9976 var s12 = d12 / (d01 + d12); 9977 9978 // If all points are the same, s01 & s02 will be inf 9979 s01 = isNaN(s01) ? 0 : s01; 9980 s12 = isNaN(s12) ? 0 : s12; 9981 9982 var fa = t * s01; // scaling factor for triangle Ta 9983 var fb = t * s12; 9984 9985 return { 9986 previous: { 9987 x: current.x - fa * (next.x - previous.x), 9988 y: current.y - fa * (next.y - previous.y) 9989 }, 9990 next: { 9991 x: current.x + fb * (next.x - previous.x), 9992 y: current.y + fb * (next.y - previous.y) 9993 } 9994 }; 9995 }; 9996 helpers.EPSILON = Number.EPSILON || 1e-14; 9997 helpers.splineCurveMonotone = function(points) { 9998 // This function calculates Bézier control points in a similar way than |splineCurve|, 9999 // but preserves monotonicity of the provided data and ensures no local extremums are added 10000 // between the dataset discrete points due to the interpolation. 10001 // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation 10002 10003 var pointsWithTangents = (points || []).map(function(point) { 10004 return { 10005 model: point._model, 10006 deltaK: 0, 10007 mK: 0 10008 }; 10009 }); 10010 10011 // Calculate slopes (deltaK) and initialize tangents (mK) 10012 var pointsLen = pointsWithTangents.length; 10013 var i, pointBefore, pointCurrent, pointAfter; 10014 for (i = 0; i < pointsLen; ++i) { 10015 pointCurrent = pointsWithTangents[i]; 10016 if (pointCurrent.model.skip) { 10017 continue; 10018 } 10019 10020 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; 10021 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; 10022 if (pointAfter && !pointAfter.model.skip) { 10023 var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); 10024 10025 // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 10026 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; 10027 } 10028 10029 if (!pointBefore || pointBefore.model.skip) { 10030 pointCurrent.mK = pointCurrent.deltaK; 10031 } else if (!pointAfter || pointAfter.model.skip) { 10032 pointCurrent.mK = pointBefore.deltaK; 10033 } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { 10034 pointCurrent.mK = 0; 10035 } else { 10036 pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; 10037 } 10038 } 10039 10040 // Adjust tangents to ensure monotonic properties 10041 var alphaK, betaK, tauK, squaredMagnitude; 10042 for (i = 0; i < pointsLen - 1; ++i) { 10043 pointCurrent = pointsWithTangents[i]; 10044 pointAfter = pointsWithTangents[i + 1]; 10045 if (pointCurrent.model.skip || pointAfter.model.skip) { 10046 continue; 10047 } 10048 10049 if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { 10050 pointCurrent.mK = pointAfter.mK = 0; 10051 continue; 10052 } 10053 10054 alphaK = pointCurrent.mK / pointCurrent.deltaK; 10055 betaK = pointAfter.mK / pointCurrent.deltaK; 10056 squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); 10057 if (squaredMagnitude <= 9) { 10058 continue; 10059 } 10060 10061 tauK = 3 / Math.sqrt(squaredMagnitude); 10062 pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; 10063 pointAfter.mK = betaK * tauK * pointCurrent.deltaK; 10064 } 10065 10066 // Compute control points 10067 var deltaX; 10068 for (i = 0; i < pointsLen; ++i) { 10069 pointCurrent = pointsWithTangents[i]; 10070 if (pointCurrent.model.skip) { 10071 continue; 10072 } 10073 10074 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; 10075 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; 10076 if (pointBefore && !pointBefore.model.skip) { 10077 deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; 10078 pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; 10079 pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; 10080 } 10081 if (pointAfter && !pointAfter.model.skip) { 10082 deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; 10083 pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; 10084 pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; 10085 } 10086 } 10087 }; 10088 helpers.nextItem = function(collection, index, loop) { 10089 if (loop) { 10090 return index >= collection.length - 1 ? collection[0] : collection[index + 1]; 10091 } 10092 return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; 10093 }; 10094 helpers.previousItem = function(collection, index, loop) { 10095 if (loop) { 10096 return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; 10097 } 10098 return index <= 0 ? collection[0] : collection[index - 1]; 10099 }; 10100 // Implementation of the nice number algorithm used in determining where axis labels will go 10101 helpers.niceNum = function(range, round) { 10102 var exponent = Math.floor(helpers.log10(range)); 10103 var fraction = range / Math.pow(10, exponent); 10104 var niceFraction; 10105 10106 if (round) { 10107 if (fraction < 1.5) { 10108 niceFraction = 1; 10109 } else if (fraction < 3) { 10110 niceFraction = 2; 10111 } else if (fraction < 7) { 10112 niceFraction = 5; 10113 } else { 10114 niceFraction = 10; 10115 } 10116 } else if (fraction <= 1.0) { 10117 niceFraction = 1; 10118 } else if (fraction <= 2) { 10119 niceFraction = 2; 10120 } else if (fraction <= 5) { 10121 niceFraction = 5; 10122 } else { 10123 niceFraction = 10; 10124 } 10125 10126 return niceFraction * Math.pow(10, exponent); 10127 }; 10128 // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ 10129 helpers.requestAnimFrame = (function() { 10130 if (typeof window === 'undefined') { 10131 return function(callback) { 10132 callback(); 10133 }; 10134 } 10135 return window.requestAnimationFrame || 10136 window.webkitRequestAnimationFrame || 10137 window.mozRequestAnimationFrame || 10138 window.oRequestAnimationFrame || 10139 window.msRequestAnimationFrame || 10140 function(callback) { 10141 return window.setTimeout(callback, 1000 / 60); 10142 }; 10143 }()); 10144 // -- DOM methods 10145 helpers.getRelativePosition = function(evt, chart) { 10146 var mouseX, mouseY; 10147 var e = evt.originalEvent || evt; 10148 var canvas = evt.currentTarget || evt.srcElement; 10149 var boundingRect = canvas.getBoundingClientRect(); 10150 10151 var touches = e.touches; 10152 if (touches && touches.length > 0) { 10153 mouseX = touches[0].clientX; 10154 mouseY = touches[0].clientY; 10155 10156 } else { 10157 mouseX = e.clientX; 10158 mouseY = e.clientY; 10159 } 10160 10161 // Scale mouse coordinates into canvas coordinates 10162 // by following the pattern laid out by 'jerryj' in the comments of 10163 // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ 10164 var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); 10165 var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); 10166 var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); 10167 var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); 10168 var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; 10169 var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; 10170 10171 // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However 10172 // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here 10173 mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); 10174 mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); 10175 10176 return { 10177 x: mouseX, 10178 y: mouseY 10179 }; 10180 10181 }; 10182 10183 // Private helper function to convert max-width/max-height values that may be percentages into a number 10184 function parseMaxStyle(styleValue, node, parentProperty) { 10185 var valueInPixels; 10186 if (typeof styleValue === 'string') { 10187 valueInPixels = parseInt(styleValue, 10); 10188 10189 if (styleValue.indexOf('%') !== -1) { 10190 // percentage * size in dimension 10191 valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; 10192 } 10193 } else { 10194 valueInPixels = styleValue; 10195 } 10196 10197 return valueInPixels; 10198 } 10199 10200 /** 10201 * Returns if the given value contains an effective constraint. 10202 * @private 10203 */ 10204 function isConstrainedValue(value) { 10205 return value !== undefined && value !== null && value !== 'none'; 10206 } 10207 10208 // Private helper to get a constraint dimension 10209 // @param domNode : the node to check the constraint on 10210 // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) 10211 // @param percentageProperty : property of parent to use when calculating width as a percentage 10212 // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser 10213 function getConstraintDimension(domNode, maxStyle, percentageProperty) { 10214 var view = document.defaultView; 10215 var parentNode = domNode.parentNode; 10216 var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; 10217 var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; 10218 var hasCNode = isConstrainedValue(constrainedNode); 10219 var hasCContainer = isConstrainedValue(constrainedContainer); 10220 var infinity = Number.POSITIVE_INFINITY; 10221 10222 if (hasCNode || hasCContainer) { 10223 return Math.min( 10224 hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, 10225 hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); 10226 } 10227 10228 return 'none'; 10229 } 10230 // returns Number or undefined if no constraint 10231 helpers.getConstraintWidth = function(domNode) { 10232 return getConstraintDimension(domNode, 'max-width', 'clientWidth'); 10233 }; 10234 // returns Number or undefined if no constraint 10235 helpers.getConstraintHeight = function(domNode) { 10236 return getConstraintDimension(domNode, 'max-height', 'clientHeight'); 10237 }; 10238 helpers.getMaximumWidth = function(domNode) { 10239 var container = domNode.parentNode; 10240 if (!container) { 10241 return domNode.clientWidth; 10242 } 10243 10244 var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); 10245 var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); 10246 var w = container.clientWidth - paddingLeft - paddingRight; 10247 var cw = helpers.getConstraintWidth(domNode); 10248 return isNaN(cw) ? w : Math.min(w, cw); 10249 }; 10250 helpers.getMaximumHeight = function(domNode) { 10251 var container = domNode.parentNode; 10252 if (!container) { 10253 return domNode.clientHeight; 10254 } 10255 10256 var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); 10257 var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); 10258 var h = container.clientHeight - paddingTop - paddingBottom; 10259 var ch = helpers.getConstraintHeight(domNode); 10260 return isNaN(ch) ? h : Math.min(h, ch); 10261 }; 10262 helpers.getStyle = function(el, property) { 10263 return el.currentStyle ? 10264 el.currentStyle[property] : 10265 document.defaultView.getComputedStyle(el, null).getPropertyValue(property); 10266 }; 10267 helpers.retinaScale = function(chart, forceRatio) { 10268 var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1; 10269 if (pixelRatio === 1) { 10270 return; 10271 } 10272 10273 var canvas = chart.canvas; 10274 var height = chart.height; 10275 var width = chart.width; 10276 10277 canvas.height = height * pixelRatio; 10278 canvas.width = width * pixelRatio; 10279 chart.ctx.scale(pixelRatio, pixelRatio); 10280 10281 // If no style has been set on the canvas, the render size is used as display size, 10282 // making the chart visually bigger, so let's enforce it to the "correct" values. 10283 // See https://github.com/chartjs/Chart.js/issues/3575 10284 if (!canvas.style.height && !canvas.style.width) { 10285 canvas.style.height = height + 'px'; 10286 canvas.style.width = width + 'px'; 10287 } 10288 }; 10289 // -- Canvas methods 10290 helpers.fontString = function(pixelSize, fontStyle, fontFamily) { 10291 return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; 10292 }; 10293 helpers.longestText = function(ctx, font, arrayOfThings, cache) { 10294 cache = cache || {}; 10295 var data = cache.data = cache.data || {}; 10296 var gc = cache.garbageCollect = cache.garbageCollect || []; 10297 10298 if (cache.font !== font) { 10299 data = cache.data = {}; 10300 gc = cache.garbageCollect = []; 10301 cache.font = font; 10302 } 10303 10304 ctx.font = font; 10305 var longest = 0; 10306 helpers.each(arrayOfThings, function(thing) { 10307 // Undefined strings and arrays should not be measured 10308 if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { 10309 longest = helpers.measureText(ctx, data, gc, longest, thing); 10310 } else if (helpers.isArray(thing)) { 10311 // if it is an array lets measure each element 10312 // to do maybe simplify this function a bit so we can do this more recursively? 10313 helpers.each(thing, function(nestedThing) { 10314 // Undefined strings and arrays should not be measured 10315 if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { 10316 longest = helpers.measureText(ctx, data, gc, longest, nestedThing); 10317 } 10318 }); 10319 } 10320 }); 10321 10322 var gcLen = gc.length / 2; 10323 if (gcLen > arrayOfThings.length) { 10324 for (var i = 0; i < gcLen; i++) { 10325 delete data[gc[i]]; 10326 } 10327 gc.splice(0, gcLen); 10328 } 10329 return longest; 10330 }; 10331 helpers.measureText = function(ctx, data, gc, longest, string) { 10332 var textWidth = data[string]; 10333 if (!textWidth) { 10334 textWidth = data[string] = ctx.measureText(string).width; 10335 gc.push(string); 10336 } 10337 if (textWidth > longest) { 10338 longest = textWidth; 10339 } 10340 return longest; 10341 }; 10342 helpers.numberOfLabelLines = function(arrayOfThings) { 10343 var numberOfLines = 1; 10344 helpers.each(arrayOfThings, function(thing) { 10345 if (helpers.isArray(thing)) { 10346 if (thing.length > numberOfLines) { 10347 numberOfLines = thing.length; 10348 } 10349 } 10350 }); 10351 return numberOfLines; 10352 }; 10353 10354 helpers.color = !color ? 10355 function(value) { 10356 console.error('Color.js not found!'); 10357 return value; 10358 } : 10359 function(value) { 10360 /* global CanvasGradient */ 10361 if (value instanceof CanvasGradient) { 10362 value = defaults.global.defaultColor; 10363 } 10364 10365 return color(value); 10366 }; 10367 10368 helpers.getHoverColor = function(colorValue) { 10369 /* global CanvasPattern */ 10370 return (colorValue instanceof CanvasPattern) ? 10371 colorValue : 10372 helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); 10373 }; 10374}; 10375 10376},{"2":2,"25":25,"45":45}],28:[function(require,module,exports){ 10377'use strict'; 10378 10379var helpers = require(45); 10380 10381/** 10382 * Helper function to get relative position for an event 10383 * @param {Event|IEvent} event - The event to get the position for 10384 * @param {Chart} chart - The chart 10385 * @returns {Point} the event position 10386 */ 10387function getRelativePosition(e, chart) { 10388 if (e.native) { 10389 return { 10390 x: e.x, 10391 y: e.y 10392 }; 10393 } 10394 10395 return helpers.getRelativePosition(e, chart); 10396} 10397 10398/** 10399 * Helper function to traverse all of the visible elements in the chart 10400 * @param chart {chart} the chart 10401 * @param handler {Function} the callback to execute for each visible item 10402 */ 10403function parseVisibleItems(chart, handler) { 10404 var datasets = chart.data.datasets; 10405 var meta, i, j, ilen, jlen; 10406 10407 for (i = 0, ilen = datasets.length; i < ilen; ++i) { 10408 if (!chart.isDatasetVisible(i)) { 10409 continue; 10410 } 10411 10412 meta = chart.getDatasetMeta(i); 10413 for (j = 0, jlen = meta.data.length; j < jlen; ++j) { 10414 var element = meta.data[j]; 10415 if (!element._view.skip) { 10416 handler(element); 10417 } 10418 } 10419 } 10420} 10421 10422/** 10423 * Helper function to get the items that intersect the event position 10424 * @param items {ChartElement[]} elements to filter 10425 * @param position {Point} the point to be nearest to 10426 * @return {ChartElement[]} the nearest items 10427 */ 10428function getIntersectItems(chart, position) { 10429 var elements = []; 10430 10431 parseVisibleItems(chart, function(element) { 10432 if (element.inRange(position.x, position.y)) { 10433 elements.push(element); 10434 } 10435 }); 10436 10437 return elements; 10438} 10439 10440/** 10441 * Helper function to get the items nearest to the event position considering all visible items in teh chart 10442 * @param chart {Chart} the chart to look at elements from 10443 * @param position {Point} the point to be nearest to 10444 * @param intersect {Boolean} if true, only consider items that intersect the position 10445 * @param distanceMetric {Function} function to provide the distance between points 10446 * @return {ChartElement[]} the nearest items 10447 */ 10448function getNearestItems(chart, position, intersect, distanceMetric) { 10449 var minDistance = Number.POSITIVE_INFINITY; 10450 var nearestItems = []; 10451 10452 parseVisibleItems(chart, function(element) { 10453 if (intersect && !element.inRange(position.x, position.y)) { 10454 return; 10455 } 10456 10457 var center = element.getCenterPoint(); 10458 var distance = distanceMetric(position, center); 10459 10460 if (distance < minDistance) { 10461 nearestItems = [element]; 10462 minDistance = distance; 10463 } else if (distance === minDistance) { 10464 // Can have multiple items at the same distance in which case we sort by size 10465 nearestItems.push(element); 10466 } 10467 }); 10468 10469 return nearestItems; 10470} 10471 10472/** 10473 * Get a distance metric function for two points based on the 10474 * axis mode setting 10475 * @param {String} axis the axis mode. x|y|xy 10476 */ 10477function getDistanceMetricForAxis(axis) { 10478 var useX = axis.indexOf('x') !== -1; 10479 var useY = axis.indexOf('y') !== -1; 10480 10481 return function(pt1, pt2) { 10482 var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; 10483 var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; 10484 return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); 10485 }; 10486} 10487 10488function indexMode(chart, e, options) { 10489 var position = getRelativePosition(e, chart); 10490 // Default axis for index mode is 'x' to match old behaviour 10491 options.axis = options.axis || 'x'; 10492 var distanceMetric = getDistanceMetricForAxis(options.axis); 10493 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); 10494 var elements = []; 10495 10496 if (!items.length) { 10497 return []; 10498 } 10499 10500 chart.data.datasets.forEach(function(dataset, datasetIndex) { 10501 if (chart.isDatasetVisible(datasetIndex)) { 10502 var meta = chart.getDatasetMeta(datasetIndex); 10503 var element = meta.data[items[0]._index]; 10504 10505 // don't count items that are skipped (null data) 10506 if (element && !element._view.skip) { 10507 elements.push(element); 10508 } 10509 } 10510 }); 10511 10512 return elements; 10513} 10514 10515/** 10516 * @interface IInteractionOptions 10517 */ 10518/** 10519 * If true, only consider items that intersect the point 10520 * @name IInterfaceOptions#boolean 10521 * @type Boolean 10522 */ 10523 10524/** 10525 * Contains interaction related functions 10526 * @namespace Chart.Interaction 10527 */ 10528module.exports = { 10529 // Helper function for different modes 10530 modes: { 10531 single: function(chart, e) { 10532 var position = getRelativePosition(e, chart); 10533 var elements = []; 10534 10535 parseVisibleItems(chart, function(element) { 10536 if (element.inRange(position.x, position.y)) { 10537 elements.push(element); 10538 return elements; 10539 } 10540 }); 10541 10542 return elements.slice(0, 1); 10543 }, 10544 10545 /** 10546 * @function Chart.Interaction.modes.label 10547 * @deprecated since version 2.4.0 10548 * @todo remove at version 3 10549 * @private 10550 */ 10551 label: indexMode, 10552 10553 /** 10554 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something 10555 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item 10556 * @function Chart.Interaction.modes.index 10557 * @since v2.4.0 10558 * @param chart {chart} the chart we are returning items from 10559 * @param e {Event} the event we are find things at 10560 * @param options {IInteractionOptions} options to use during interaction 10561 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10562 */ 10563 index: indexMode, 10564 10565 /** 10566 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something 10567 * If the options.intersect is false, we find the nearest item and return the items in that dataset 10568 * @function Chart.Interaction.modes.dataset 10569 * @param chart {chart} the chart we are returning items from 10570 * @param e {Event} the event we are find things at 10571 * @param options {IInteractionOptions} options to use during interaction 10572 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10573 */ 10574 dataset: function(chart, e, options) { 10575 var position = getRelativePosition(e, chart); 10576 options.axis = options.axis || 'xy'; 10577 var distanceMetric = getDistanceMetricForAxis(options.axis); 10578 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); 10579 10580 if (items.length > 0) { 10581 items = chart.getDatasetMeta(items[0]._datasetIndex).data; 10582 } 10583 10584 return items; 10585 }, 10586 10587 /** 10588 * @function Chart.Interaction.modes.x-axis 10589 * @deprecated since version 2.4.0. Use index mode and intersect == true 10590 * @todo remove at version 3 10591 * @private 10592 */ 10593 'x-axis': function(chart, e) { 10594 return indexMode(chart, e, {intersect: false}); 10595 }, 10596 10597 /** 10598 * Point mode returns all elements that hit test based on the event position 10599 * of the event 10600 * @function Chart.Interaction.modes.intersect 10601 * @param chart {chart} the chart we are returning items from 10602 * @param e {Event} the event we are find things at 10603 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10604 */ 10605 point: function(chart, e) { 10606 var position = getRelativePosition(e, chart); 10607 return getIntersectItems(chart, position); 10608 }, 10609 10610 /** 10611 * nearest mode returns the element closest to the point 10612 * @function Chart.Interaction.modes.intersect 10613 * @param chart {chart} the chart we are returning items from 10614 * @param e {Event} the event we are find things at 10615 * @param options {IInteractionOptions} options to use 10616 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10617 */
10618 nearest: function(chart, e, options) { 10619 var position = getRelativePosition(e, chart); 10620 options.axis = options.axis || 'xy'; 10621 var distanceMetric = getDistanceMetricForAxis(options.axis); 10622 var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); 10623 10624 // We have multiple items at the same distance from the event. Now sort by smallest 10625 if (nearestItems.length > 1) { 10626 nearestItems.sort(function(a, b) { 10627 var sizeA = a.getArea(); 10628 var sizeB = b.getArea(); 10629 var ret = sizeA - sizeB; 10630 10631 if (ret === 0) { 10632 // if equal sort by dataset index 10633 ret = a._datasetIndex - b._datasetIndex; 10634 } 10635 10636 return ret; 10637 }); 10638 } 10639 10640 // Return only 1 item 10641 return nearestItems.slice(0, 1); 10642 }, 10643 10644 /** 10645 * x mode returns the elements that hit-test at the current x coordinate 10646 * @function Chart.Interaction.modes.x 10647 * @param chart {chart} the chart we are returning items from 10648 * @param e {Event} the event we are find things at 10649 * @param options {IInteractionOptions} options to use 10650 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10651 */ 10652 x: function(chart, e, options) { 10653 var position = getRelativePosition(e, chart); 10654 var items = []; 10655 var intersectsItem = false; 10656 10657 parseVisibleItems(chart, function(element) { 10658 if (element.inXRange(position.x)) { 10659 items.push(element); 10660 } 10661 10662 if (element.inRange(position.x, position.y)) { 10663 intersectsItem = true; 10664 } 10665 }); 10666 10667 // If we want to trigger on an intersect and we don't have any items 10668 // that intersect the position, return nothing 10669 if (options.intersect && !intersectsItem) { 10670 items = []; 10671 } 10672 return items; 10673 }, 10674 10675 /** 10676 * y mode returns the elements that hit-test at the current y coordinate 10677 * @function Chart.Interaction.modes.y 10678 * @param chart {chart} the chart we are returning items from 10679 * @param e {Event} the event we are find things at 10680 * @param options {IInteractionOptions} options to use 10681 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned 10682 */ 10683 y: function(chart, e, options) { 10684 var position = getRelativePosition(e, chart); 10685 var items = []; 10686 var intersectsItem = false; 10687 10688 parseVisibleItems(chart, function(element) { 10689 if (element.inYRange(position.y)) { 10690 items.push(element); 10691 } 10692 10693 if (element.inRange(position.x, position.y)) { 10694 intersectsItem = true; 10695 } 10696 }); 10697 10698 // If we want to trigger on an intersect and we don't have any items 10699 // that intersect the position, return nothing 10700 if (options.intersect && !intersectsItem) { 10701 items = []; 10702 } 10703 return items; 10704 } 10705 } 10706}; 10707 10708},{"45":45}],29:[function(require,module,exports){ 10709'use strict'; 10710 10711var defaults = require(25); 10712 10713defaults._set('global', { 10714 responsive: true, 10715 responsiveAnimationDuration: 0, 10716 maintainAspectRatio: true, 10717 events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], 10718 hover: { 10719 onHover: null, 10720 mode: 'nearest', 10721 intersect: true, 10722 animationDuration: 400 10723 }, 10724 onClick: null, 10725 defaultColor: 'rgba(0,0,0,0.1)', 10726 defaultFontColor: '#666', 10727 defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", 10728 defaultFontSize: 12, 10729 defaultFontStyle: 'normal', 10730 showLines: true, 10731 10732 // Element defaults defined in element extensions 10733 elements: {}, 10734 10735 // Layout options such as padding 10736 layout: { 10737 padding: { 10738 top: 0, 10739 right: 0, 10740 bottom: 0, 10741 left: 0 10742 } 10743 } 10744}); 10745 10746module.exports = function() { 10747 10748 // Occupy the global variable of Chart, and create a simple base class 10749 var Chart = function(item, config) { 10750 this.construct(item, config); 10751 return this; 10752 }; 10753 10754 Chart.Chart = Chart; 10755 10756 return Chart; 10757}; 10758 10759},{"25":25}],30:[function(require,module,exports){ 10760'use strict'; 10761 10762var helpers = require(45); 10763 10764function filterByPosition(array, position) { 10765 return helpers.where(array, function(v) { 10766 return v.position === position; 10767 }); 10768} 10769 10770function sortByWeight(array, reverse) { 10771 array.forEach(function(v, i) { 10772 v._tmpIndex_ = i; 10773 return v; 10774 }); 10775 array.sort(function(a, b) { 10776 var v0 = reverse ? b : a; 10777 var v1 = reverse ? a : b; 10778 return v0.weight === v1.weight ? 10779 v0._tmpIndex_ - v1._tmpIndex_ : 10780 v0.weight - v1.weight; 10781 }); 10782 array.forEach(function(v) { 10783 delete v._tmpIndex_; 10784 }); 10785} 10786 10787/** 10788 * @interface ILayoutItem 10789 * @prop {String} position - The position of the item in the chart layout. Possible values are 10790 * 'left', 'top', 'right', 'bottom', and 'chartArea' 10791 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area 10792 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down 10793 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) 10794 * @prop {Function} update - Takes two parameters: width and height. Returns size of item 10795 * @prop {Function} getPadding - Returns an object with padding on the edges 10796 * @prop {Number} width - Width of item. Must be valid after update() 10797 * @prop {Number} height - Height of item. Must be valid after update() 10798 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update 10799 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update 10800 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update 10801 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update 10802 */ 10803 10804// The layout service is very self explanatory. It's responsible for the layout within a chart. 10805// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need 10806// It is this service's responsibility of carrying out that layout. 10807module.exports = { 10808 defaults: {}, 10809 10810 /** 10811 * Register a box to a chart. 10812 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. 10813 * @param {Chart} chart - the chart to use 10814 * @param {ILayoutItem} item - the item to add to be layed out 10815 */
10816 addBox: function(chart, item) { 10817 if (!chart.boxes) { 10818 chart.boxes = []; 10819 } 10820 10821 // initialize item with default values 10822 item.fullWidth = item.fullWidth || false; 10823 item.position = item.position || 'top'; 10824 item.weight = item.weight || 0; 10825 10826 chart.boxes.push(item); 10827 }, 10828 10829 /** 10830 * Remove a layoutItem from a chart 10831 * @param {Chart} chart - the chart to remove the box from 10832 * @param {Object} layoutItem - the item to remove from the layout 10833 */ 10834 removeBox: function(chart, layoutItem) { 10835 var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; 10836 if (index !== -1) { 10837 chart.boxes.splice(index, 1); 10838 } 10839 }, 10840 10841 /** 10842 * Sets (or updates) options on the given `item`. 10843 * @param {Chart} chart - the chart in which the item lives (or will be added to) 10844 * @param {Object} item - the item to configure with the given options 10845 * @param {Object} options - the new item options. 10846 */ 10847 configure: function(chart, item, options) { 10848 var props = ['fullWidth', 'position', 'weight']; 10849 var ilen = props.length; 10850 var i = 0; 10851 var prop; 10852 10853 for (; i < ilen; ++i) { 10854 prop = props[i]; 10855 if (options.hasOwnProperty(prop)) { 10856 item[prop] = options[prop]; 10857 } 10858 } 10859 }, 10860 10861 /** 10862 * Fits boxes of the given chart into the given size by having each box measure itself 10863 * then running a fitting algorithm 10864 * @param {Chart} chart - the chart 10865 * @param {Number} width - the width to fit into 10866 * @param {Number} height - the height to fit into 10867 */ 10868 update: function(chart, width, height) { 10869 if (!chart) { 10870 return; 10871 } 10872 10873 var layoutOptions = chart.options.layout || {}; 10874 var padding = helpers.options.toPadding(layoutOptions.padding); 10875 var leftPadding = padding.left; 10876 var rightPadding = padding.right; 10877 var topPadding = padding.top; 10878 var bottomPadding = padding.bottom; 10879 10880 var leftBoxes = filterByPosition(chart.boxes, 'left'); 10881 var rightBoxes = filterByPosition(chart.boxes, 'right'); 10882 var topBoxes = filterByPosition(chart.boxes, 'top'); 10883 var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); 10884 var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); 10885 10886 // Sort boxes by weight. A higher weight is further away from the chart area 10887 sortByWeight(leftBoxes, true); 10888 sortByWeight(rightBoxes, false); 10889 sortByWeight(topBoxes, true); 10890 sortByWeight(bottomBoxes, false); 10891 10892 // Essentially we now have any number of boxes on each of the 4 sides. 10893 // Our canvas looks like the following. 10894 // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and 10895 // B1 is the bottom axis 10896 // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays 10897 // These locations are single-box locations only, when trying to register a chartArea location that is already taken, 10898 // an error will be thrown. 10899 // 10900 // |----------------------------------------------------| 10901 // | T1 (Full Width) | 10902 // |----------------------------------------------------| 10903 // | | | T2 | | 10904 // | |----|-------------------------------------|----| 10905 // | | | C1 | | C2 | | 10906 // | | |----| |----| | 10907 // | | | | | 10908 // | L1 | L2 | ChartArea (C0) | R1 | 10909 // | | | | | 10910 // | | |----| |----| | 10911 // | | | C3 | | C4 | | 10912 // | |----|-------------------------------------|----| 10913 // | | | B1 | | 10914 // |----------------------------------------------------| 10915 // | B2 (Full Width) | 10916 // |----------------------------------------------------| 10917 // 10918 // What we do to find the best sizing, we do the following 10919 // 1. Determine the minimum size of the chart area. 10920 // 2. Split the remaining width equally between each vertical axis 10921 // 3. Split the remaining height equally between each horizontal axis 10922 // 4. Give each layout the maximum size it can be. The layout will return it's minimum size 10923 // 5. Adjust the sizes of each axis based on it's minimum reported size. 10924 // 6. Refit each axis 10925 // 7. Position each axis in the final location 10926 // 8. Tell the chart the final location of the chart area 10927 // 9. Tell any axes that overlay the chart area the positions of the chart area 10928 10929 // Step 1 10930 var chartWidth = width - leftPadding - rightPadding;
10931 var chartHeight = height - topPadding - bottomPadding; 10932 var chartAreaWidth = chartWidth / 2; // min 50% 10933 var chartAreaHeight = chartHeight / 2; // min 50% 10934 10935 // Step 2 10936 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); 10937 10938 // Step 3 10939 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); 10940 10941 // Step 4 10942 var maxChartAreaWidth = chartWidth; 10943 var maxChartAreaHeight = chartHeight; 10944 var minBoxSizes = []; 10945 10946 function getMinimumBoxSize(box) { 10947 var minSize; 10948 var isHorizontal = box.isHorizontal(); 10949 10950 if (isHorizontal) { 10951 minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); 10952 maxChartAreaHeight -= minSize.height; 10953 } else { 10954 minSize = box.update(verticalBoxWidth, maxChartAreaHeight); 10955 maxChartAreaWidth -= minSize.width; 10956 } 10957 10958 minBoxSizes.push({ 10959 horizontal: isHorizontal, 10960 minSize: minSize, 10961 box: box, 10962 }); 10963 } 10964 10965 helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); 10966 10967 // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) 10968 var maxHorizontalLeftPadding = 0; 10969 var maxHorizontalRightPadding = 0; 10970 var maxVerticalTopPadding = 0; 10971 var maxVerticalBottomPadding = 0; 10972 10973 helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { 10974 if (horizontalBox.getPadding) { 10975 var boxPadding = horizontalBox.getPadding(); 10976 maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); 10977 maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); 10978 } 10979 }); 10980 10981 helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { 10982 if (verticalBox.getPadding) { 10983 var boxPadding = verticalBox.getPadding(); 10984 maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); 10985 maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); 10986 } 10987 }); 10988 10989 // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could 10990 // be if the axes are drawn at their minimum sizes. 10991 // Steps 5 & 6 10992 var totalLeftBoxesWidth = leftPadding; 10993 var totalRightBoxesWidth = rightPadding; 10994 var totalTopBoxesHeight = topPadding; 10995 var totalBottomBoxesHeight = bottomPadding; 10996 10997 // Function to fit a box 10998 function fitBox(box) { 10999 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { 11000 return minBox.box === box; 11001 }); 11002 11003 if (minBoxSize) { 11004 if (box.isHorizontal()) { 11005 var scaleMargin = { 11006 left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), 11007 right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), 11008 top: 0, 11009 bottom: 0 11010 }; 11011 11012 // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends 11013 // on the margin. Sometimes they need to increase in size slightly 11014 box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); 11015 } else { 11016 box.update(minBoxSize.minSize.width, maxChartAreaHeight); 11017 } 11018 } 11019 } 11020 11021 // Update, and calculate the left and right margins for the horizontal boxes 11022 helpers.each(leftBoxes.concat(rightBoxes), fitBox); 11023 11024 helpers.each(leftBoxes, function(box) { 11025 totalLeftBoxesWidth += box.width; 11026 }); 11027 11028 helpers.each(rightBoxes, function(box) { 11029 totalRightBoxesWidth += box.width; 11030 }); 11031 11032 // Set the Left and Right margins for the horizontal boxes 11033 helpers.each(topBoxes.concat(bottomBoxes), fitBox); 11034 11035 // Figure out how much margin is on the top and bottom of the vertical boxes 11036 helpers.each(topBoxes, function(box) { 11037 totalTopBoxesHeight += box.height; 11038 }); 11039 11040 helpers.each(bottomBoxes, function(box) { 11041 totalBottomBoxesHeight += box.height; 11042 }); 11043 11044 function finalFitVerticalBox(box) { 11045 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { 11046 return minSize.box === box; 11047 }); 11048 11049 var scaleMargin = { 11050 left: 0, 11051 right: 0, 11052 top: totalTopBoxesHeight, 11053 bottom: totalBottomBoxesHeight 11054 }; 11055 11056 if (minBoxSize) { 11057 box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); 11058 } 11059 } 11060 11061 // Let the left layout know the final margin 11062 helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); 11063 11064 // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) 11065 totalLeftBoxesWidth = leftPadding; 11066 totalRightBoxesWidth = rightPadding;
11067 totalTopBoxesHeight = topPadding; 11068 totalBottomBoxesHeight = bottomPadding; 11069 11070 helpers.each(leftBoxes, function(box) { 11071 totalLeftBoxesWidth += box.width; 11072 }); 11073 11074 helpers.each(rightBoxes, function(box) { 11075 totalRightBoxesWidth += box.width; 11076 }); 11077 11078 helpers.each(topBoxes, function(box) { 11079 totalTopBoxesHeight += box.height; 11080 }); 11081 helpers.each(bottomBoxes, function(box) { 11082 totalBottomBoxesHeight += box.height; 11083 }); 11084 11085 // We may be adding some padding to account for rotated x axis labels 11086 var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); 11087 totalLeftBoxesWidth += leftPaddingAddition; 11088 totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); 11089 11090 var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); 11091 totalTopBoxesHeight += topPaddingAddition; 11092 totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); 11093 11094 // Figure out if our chart area changed. This would occur if the dataset layout label rotation 11095 // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do 11096 // without calling `fit` again 11097 var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; 11098 var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; 11099 11100 if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { 11101 helpers.each(leftBoxes, function(box) { 11102 box.height = newMaxChartAreaHeight; 11103 }); 11104 11105 helpers.each(rightBoxes, function(box) { 11106 box.height = newMaxChartAreaHeight; 11107 }); 11108 11109 helpers.each(topBoxes, function(box) { 11110 if (!box.fullWidth) { 11111 box.width = newMaxChartAreaWidth; 11112 } 11113 }); 11114 11115 helpers.each(bottomBoxes, function(box) { 11116 if (!box.fullWidth) { 11117 box.width = newMaxChartAreaWidth; 11118 } 11119 }); 11120 11121 maxChartAreaHeight = newMaxChartAreaHeight; 11122 maxChartAreaWidth = newMaxChartAreaWidth; 11123 } 11124 11125 // Step 7 - Position the boxes 11126 var left = leftPadding + leftPaddingAddition; 11127 var top = topPadding + topPaddingAddition; 11128 11129 function placeBox(box) { 11130 if (box.isHorizontal()) { 11131 box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; 11132 box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; 11133 box.top = top; 11134 box.bottom = top + box.height; 11135 11136 // Move to next point 11137 top = box.bottom; 11138 11139 } else { 11140 11141 box.left = left; 11142 box.right = left + box.width;
11143 box.top = totalTopBoxesHeight; 11144 box.bottom = totalTopBoxesHeight + maxChartAreaHeight; 11145 11146 // Move to next point 11147 left = box.right; 11148 } 11149 } 11150 11151 helpers.each(leftBoxes.concat(topBoxes), placeBox); 11152 11153 // Account for chart width and height 11154 left += maxChartAreaWidth; 11155 top += maxChartAreaHeight; 11156 11157 helpers.each(rightBoxes, placeBox); 11158 helpers.each(bottomBoxes, placeBox); 11159 11160 // Step 8 11161 chart.chartArea = { 11162 left: totalLeftBoxesWidth, 11163 top: totalTopBoxesHeight, 11164 right: totalLeftBoxesWidth + maxChartAreaWidth, 11165 bottom: totalTopBoxesHeight + maxChartAreaHeight 11166 }; 11167 11168 // Step 9 11169 helpers.each(chartAreaBoxes, function(box) { 11170 box.left = chart.chartArea.left; 11171 box.top = chart.chartArea.top; 11172 box.right = chart.chartArea.right; 11173 box.bottom = chart.chartArea.bottom; 11174 11175 box.update(maxChartAreaWidth, maxChartAreaHeight); 11176 }); 11177 } 11178}; 11179 11180},{"45":45}],31:[function(require,module,exports){ 11181'use strict'; 11182 11183var defaults = require(25); 11184var helpers = require(45); 11185 11186defaults._set('global', { 11187 plugins: {} 11188}); 11189 11190/** 11191 * The plugin service singleton 11192 * @namespace Chart.plugins 11193 * @since 2.1.0 11194 */ 11195module.exports = { 11196 /** 11197 * Globally registered plugins. 11198 * @private 11199 */ 11200 _plugins: [], 11201 11202 /** 11203 * This identifier is used to invalidate the descriptors cache attached to each chart 11204 * when a global plugin is registered or unregistered. In this case, the cache ID is 11205 * incremented and descriptors are regenerated during following API calls. 11206 * @private 11207 */ 11208 _cacheId: 0, 11209 11210 /** 11211 * Registers the given plugin(s) if not already registered. 11212 * @param {Array|Object} plugins plugin instance(s). 11213 */ 11214 register: function(plugins) { 11215 var p = this._plugins; 11216 ([]).concat(plugins).forEach(function(plugin) { 11217 if (p.indexOf(plugin) === -1) { 11218 p.push(plugin); 11219 } 11220 }); 11221 11222 this._cacheId++; 11223 }, 11224 11225 /** 11226 * Unregisters the given plugin(s) only if registered. 11227 * @param {Array|Object} plugins plugin instance(s). 11228 */ 11229 unregister: function(plugins) { 11230 var p = this._plugins; 11231 ([]).concat(plugins).forEach(function(plugin) { 11232 var idx = p.indexOf(plugin); 11233 if (idx !== -1) { 11234 p.splice(idx, 1); 11235 } 11236 }); 11237 11238 this._cacheId++; 11239 }, 11240 11241 /** 11242 * Remove all registered plugins. 11243 * @since 2.1.5 11244 */ 11245 clear: function() { 11246 this._plugins = []; 11247 this._cacheId++; 11248 }, 11249 11250 /** 11251 * Returns the number of registered plugins? 11252 * @returns {Number} 11253 * @since 2.1.5 11254 */ 11255 count: function() { 11256 return this._plugins.length; 11257 }, 11258 11259 /** 11260 * Returns all registered plugin instances. 11261 * @returns {Array} array of plugin objects. 11262 * @since 2.1.5 11263 */ 11264 getAll: function() { 11265 return this._plugins; 11266 }, 11267 11268 /** 11269 * Calls enabled plugins for `chart` on the specified hook and with the given args. 11270 * This method immediately returns as soon as a plugin explicitly returns false. The 11271 * returned value can be used, for instance, to interrupt the current action. 11272 * @param {Object} chart - The chart instance for which plugins should be called. 11273 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). 11274 * @param {Array} [args] - Extra arguments to apply to the hook call. 11275 * @returns {Boolean} false if any of the plugins return false, else returns true. 11276 */ 11277 notify: function(chart, hook, args) { 11278 var descriptors = this.descriptors(chart); 11279 var ilen = descriptors.length; 11280 var i, descriptor, plugin, params, method; 11281 11282 for (i = 0; i < ilen; ++i) { 11283 descriptor = descriptors[i]; 11284 plugin = descriptor.plugin; 11285 method = plugin[hook]; 11286 if (typeof method === 'function') { 11287 params = [chart].concat(args || []); 11288 params.push(descriptor.options); 11289 if (method.apply(plugin, params) === false) { 11290 return false; 11291 } 11292 } 11293 } 11294 11295 return true; 11296 }, 11297 11298 /** 11299 * Returns descriptors of enabled plugins for the given chart. 11300 * @returns {Array} [{ plugin, options }] 11301 * @private 11302 */ 11303 descriptors: function(chart) { 11304 var cache = chart.$plugins || (chart.$plugins = {}); 11305 if (cache.id === this._cacheId) { 11306 return cache.descriptors; 11307 } 11308 11309 var plugins = []; 11310 var descriptors = []; 11311 var config = (chart && chart.config) || {}; 11312 var options = (config.options && config.options.plugins) || {}; 11313
vendor: 9,411 bytes, lines 11314-11542
11314 this._plugins.concat(config.plugins || []).forEach(function(plugin) { 11315 var idx = plugins.indexOf(plugin); 11316 if (idx !== -1) { 11317 return; 11318 } 11319 11320 var id = plugin.id; 11321 var opts = options[id]; 11322 if (opts === false) { 11323 return; 11324 } 11325 11326 if (opts === true) { 11327 opts = helpers.clone(defaults.global.plugins[id]); 11328 } 11329 11330 plugins.push(plugin); 11331 descriptors.push({ 11332 plugin: plugin, 11333 options: opts || {} 11334 }); 11335 }); 11336 11337 cache.descriptors = descriptors; 11338 cache.id = this._cacheId; 11339 return descriptors; 11340 }, 11341 11342 /** 11343 * Invalidates cache for the given chart: descriptors hold a reference on plugin option, 11344 * but in some cases, this reference can be changed by the user when updating options. 11345 * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167 11346 * @private 11347 */ 11348 _invalidate: function(chart) { 11349 delete chart.$plugins; 11350 } 11351}; 11352 11353/** 11354 * Plugin extension hooks. 11355 * @interface IPlugin 11356 * @since 2.1.0 11357 */ 11358/** 11359 * @method IPlugin#beforeInit 11360 * @desc Called before initializing `chart`. 11361 * @param {Chart.Controller} chart - The chart instance. 11362 * @param {Object} options - The plugin options. 11363 */ 11364/** 11365 * @method IPlugin#afterInit 11366 * @desc Called after `chart` has been initialized and before the first update. 11367 * @param {Chart.Controller} chart - The chart instance. 11368 * @param {Object} options - The plugin options. 11369 */ 11370/** 11371 * @method IPlugin#beforeUpdate 11372 * @desc Called before updating `chart`. If any plugin returns `false`, the update 11373 * is cancelled (and thus subsequent render(s)) until another `update` is triggered. 11374 * @param {Chart.Controller} chart - The chart instance. 11375 * @param {Object} options - The plugin options. 11376 * @returns {Boolean} `false` to cancel the chart update. 11377 */ 11378/** 11379 * @method IPlugin#afterUpdate 11380 * @desc Called after `chart` has been updated and before rendering. Note that this 11381 * hook will not be called if the chart update has been previously cancelled. 11382 * @param {Chart.Controller} chart - The chart instance. 11383 * @param {Object} options - The plugin options. 11384 */ 11385/** 11386 * @method IPlugin#beforeDatasetsUpdate 11387 * @desc Called before updating the `chart` datasets. If any plugin returns `false`, 11388 * the datasets update is cancelled until another `update` is triggered. 11389 * @param {Chart.Controller} chart - The chart instance. 11390 * @param {Object} options - The plugin options. 11391 * @returns {Boolean} false to cancel the datasets update. 11392 * @since version 2.1.5 11393*/ 11394/** 11395 * @method IPlugin#afterDatasetsUpdate 11396 * @desc Called after the `chart` datasets have been updated. Note that this hook 11397 * will not be called if the datasets update has been previously cancelled. 11398 * @param {Chart.Controller} chart - The chart instance. 11399 * @param {Object} options - The plugin options. 11400 * @since version 2.1.5 11401 */ 11402/** 11403 * @method IPlugin#beforeDatasetUpdate 11404 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin 11405 * returns `false`, the datasets update is cancelled until another `update` is triggered. 11406 * @param {Chart} chart - The chart instance. 11407 * @param {Object} args - The call arguments. 11408 * @param {Number} args.index - The dataset index. 11409 * @param {Object} args.meta - The dataset metadata. 11410 * @param {Object} options - The plugin options. 11411 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11412 */ 11413/** 11414 * @method IPlugin#afterDatasetUpdate 11415 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note 11416 * that this hook will not be called if the datasets update has been previously cancelled. 11417 * @param {Chart} chart - The chart instance. 11418 * @param {Object} args - The call arguments. 11419 * @param {Number} args.index - The dataset index. 11420 * @param {Object} args.meta - The dataset metadata. 11421 * @param {Object} options - The plugin options. 11422 */ 11423/** 11424 * @method IPlugin#beforeLayout 11425 * @desc Called before laying out `chart`. If any plugin returns `false`, 11426 * the layout update is cancelled until another `update` is triggered. 11427 * @param {Chart.Controller} chart - The chart instance. 11428 * @param {Object} options - The plugin options. 11429 * @returns {Boolean} `false` to cancel the chart layout. 11430 */ 11431/** 11432 * @method IPlugin#afterLayout 11433 * @desc Called after the `chart` has been layed out. Note that this hook will not 11434 * be called if the layout update has been previously cancelled. 11435 * @param {Chart.Controller} chart - The chart instance. 11436 * @param {Object} options - The plugin options. 11437 */ 11438/** 11439 * @method IPlugin#beforeRender 11440 * @desc Called before rendering `chart`. If any plugin returns `false`, 11441 * the rendering is cancelled until another `render` is triggered. 11442 * @param {Chart.Controller} chart - The chart instance. 11443 * @param {Object} options - The plugin options. 11444 * @returns {Boolean} `false` to cancel the chart rendering. 11445 */ 11446/** 11447 * @method IPlugin#afterRender 11448 * @desc Called after the `chart` has been fully rendered (and animation completed). Note 11449 * that this hook will not be called if the rendering has been previously cancelled. 11450 * @param {Chart.Controller} chart - The chart instance. 11451 * @param {Object} options - The plugin options. 11452 */ 11453/** 11454 * @method IPlugin#beforeDraw 11455 * @desc Called before drawing `chart` at every animation frame specified by the given 11456 * easing value. If any plugin returns `false`, the frame drawing is cancelled until 11457 * another `render` is triggered. 11458 * @param {Chart.Controller} chart - The chart instance. 11459 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11460 * @param {Object} options - The plugin options. 11461 * @returns {Boolean} `false` to cancel the chart drawing. 11462 */ 11463/** 11464 * @method IPlugin#afterDraw 11465 * @desc Called after the `chart` has been drawn for the specific easing value. Note 11466 * that this hook will not be called if the drawing has been previously cancelled. 11467 * @param {Chart.Controller} chart - The chart instance. 11468 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11469 * @param {Object} options - The plugin options. 11470 */ 11471/** 11472 * @method IPlugin#beforeDatasetsDraw 11473 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, 11474 * the datasets drawing is cancelled until another `render` is triggered. 11475 * @param {Chart.Controller} chart - The chart instance. 11476 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11477 * @param {Object} options - The plugin options. 11478 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11479 */ 11480/** 11481 * @method IPlugin#afterDatasetsDraw 11482 * @desc Called after the `chart` datasets have been drawn. Note that this hook 11483 * will not be called if the datasets drawing has been previously cancelled. 11484 * @param {Chart.Controller} chart - The chart instance. 11485 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. 11486 * @param {Object} options - The plugin options. 11487 */ 11488/** 11489 * @method IPlugin#beforeDatasetDraw 11490 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets 11491 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing 11492 * is cancelled until another `render` is triggered. 11493 * @param {Chart} chart - The chart instance. 11494 * @param {Object} args - The call arguments. 11495 * @param {Number} args.index - The dataset index. 11496 * @param {Object} args.meta - The dataset metadata. 11497 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11498 * @param {Object} options - The plugin options. 11499 * @returns {Boolean} `false` to cancel the chart datasets drawing. 11500 */ 11501/** 11502 * @method IPlugin#afterDatasetDraw 11503 * @desc Called after the `chart` datasets at the given `args.index` have been drawn 11504 * (datasets are drawn in the reverse order). Note that this hook will not be called 11505 * if the datasets drawing has been previously cancelled. 11506 * @param {Chart} chart - The chart instance. 11507 * @param {Object} args - The call arguments. 11508 * @param {Number} args.index - The dataset index. 11509 * @param {Object} args.meta - The dataset metadata. 11510 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11511 * @param {Object} options - The plugin options. 11512 */ 11513/** 11514 * @method IPlugin#beforeTooltipDraw 11515 * @desc Called before drawing the `tooltip`. If any plugin returns `false`, 11516 * the tooltip drawing is cancelled until another `render` is triggered. 11517 * @param {Chart} chart - The chart instance. 11518 * @param {Object} args - The call arguments. 11519 * @param {Object} args.tooltip - The tooltip. 11520 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11521 * @param {Object} options - The plugin options. 11522 * @returns {Boolean} `false` to cancel the chart tooltip drawing. 11523 */ 11524/** 11525 * @method IPlugin#afterTooltipDraw 11526 * @desc Called after drawing the `tooltip`. Note that this hook will not 11527 * be called if the tooltip drawing has been previously cancelled. 11528 * @param {Chart} chart - The chart instance. 11529 * @param {Object} args - The call arguments. 11530 * @param {Object} args.tooltip - The tooltip. 11531 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. 11532 * @param {Object} options - The plugin options. 11533 */ 11534/** 11535 * @method IPlugin#beforeEvent 11536 * @desc Called before processing the specified `event`. If any plugin returns `false`, 11537 * the event will be discarded. 11538 * @param {Chart.Controller} chart - The chart instance. 11539 * @param {IEvent} event - The event object. 11540 * @param {Object} options - The plugin options. 11541 */ 11542/**
11543 * @method IPlugin#afterEvent 11544 * @desc Called after the `event` has been consumed. Note that this hook 11545 * will not be called if the `event` has been previously discarded. 11546 * @param {Chart.Controller} chart - The chart instance. 11547 * @param {IEvent} event - The event object. 11548 * @param {Object} options - The plugin options. 11549 */ 11550/** 11551 * @method IPlugin#resize 11552 * @desc Called after the chart as been resized. 11553 * @param {Chart.Controller} chart - The chart instance. 11554 * @param {Number} size - The new canvas display size (eq. canvas.style width & height). 11555 * @param {Object} options - The plugin options. 11556 */ 11557/** 11558 * @method IPlugin#destroy 11559 * @desc Called after the chart as been destroyed. 11560 * @param {Chart.Controller} chart - The chart instance. 11561 * @param {Object} options - The plugin options. 11562 */ 11563 11564},{"25":25,"45":45}],32:[function(require,module,exports){ 11565'use strict'; 11566 11567var defaults = require(25); 11568var Element = require(26); 11569var helpers = require(45); 11570var Ticks = require(34); 11571 11572defaults._set('scale', { 11573 display: true, 11574 position: 'left', 11575 offset: false, 11576 11577 // grid line settings 11578 gridLines: { 11579 display: true, 11580 color: 'rgba(0, 0, 0, 0.1)', 11581 lineWidth: 1, 11582 drawBorder: true, 11583 drawOnChartArea: true, 11584 drawTicks: true, 11585 tickMarkLength: 10, 11586 zeroLineWidth: 1, 11587 zeroLineColor: 'rgba(0,0,0,0.25)', 11588 zeroLineBorderDash: [], 11589 zeroLineBorderDashOffset: 0.0, 11590 offsetGridLines: false, 11591 borderDash: [], 11592 borderDashOffset: 0.0 11593 }, 11594 11595 // scale label 11596 scaleLabel: { 11597 // display property 11598 display: false, 11599 11600 // actual label 11601 labelString: '', 11602 11603 // line height 11604 lineHeight: 1.2, 11605 11606 // top/bottom padding 11607 padding: { 11608 top: 4, 11609 bottom: 4 11610 } 11611 }, 11612 11613 // label settings 11614 ticks: { 11615 beginAtZero: false, 11616 minRotation: 0, 11617 maxRotation: 50, 11618 mirror: false, 11619 padding: 0, 11620 reverse: false, 11621 display: true, 11622 autoSkip: true, 11623 autoSkipPadding: 0, 11624 labelOffset: 0, 11625 // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. 11626 callback: Ticks.formatters.values, 11627 minor: {}, 11628 major: {} 11629 } 11630}); 11631 11632function labelsFromTicks(ticks) { 11633 var labels = []; 11634 var i, ilen; 11635 11636 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 11637 labels.push(ticks[i].label); 11638 } 11639 11640 return labels; 11641} 11642 11643function getLineValue(scale, index, offsetGridLines) { 11644 var lineValue = scale.getPixelForTick(index); 11645 11646 if (offsetGridLines) { 11647 if (index === 0) { 11648 lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; 11649 } else { 11650 lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; 11651 } 11652 } 11653 return lineValue; 11654} 11655 11656module.exports = function(Chart) { 11657 11658 function computeTextSize(context, tick, font) { 11659 return helpers.isArray(tick) ? 11660 helpers.longestText(context, font, tick) : 11661 context.measureText(tick).width; 11662 } 11663 11664 function parseFontOptions(options) { 11665 var valueOrDefault = helpers.valueOrDefault; 11666 var globalDefaults = defaults.global; 11667 var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); 11668 var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); 11669 var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); 11670 11671 return { 11672 size: size, 11673 style: style, 11674 family: family, 11675 font: helpers.fontString(size, style, family) 11676 }; 11677 } 11678 11679 function parseLineHeight(options) { 11680 return helpers.options.toLineHeight( 11681 helpers.valueOrDefault(options.lineHeight, 1.2), 11682 helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); 11683 } 11684 11685 Chart.Scale = Element.extend({ 11686 /** 11687 * Get the padding needed for the scale 11688 * @method getPadding 11689 * @private 11690 * @returns {Padding} the necessary padding 11691 */ 11692 getPadding: function() { 11693 var me = this; 11694 return { 11695 left: me.paddingLeft || 0, 11696 top: me.paddingTop || 0, 11697 right: me.paddingRight || 0, 11698 bottom: me.paddingBottom || 0 11699 }; 11700 }, 11701 11702 /** 11703 * Returns the scale tick objects ({label, major}) 11704 * @since 2.7 11705 */ 11706 getTicks: function() { 11707 return this._ticks; 11708 }, 11709 11710 // These methods are ordered by lifecyle. Utilities then follow. 11711 // Any function defined here is inherited by all scale types. 11712 // Any function can be extended by the scale type 11713 11714 mergeTicksOptions: function() { 11715 var ticks = this.options.ticks; 11716 if (ticks.minor === false) { 11717 ticks.minor = { 11718 display: false 11719 }; 11720 } 11721 if (ticks.major === false) { 11722 ticks.major = { 11723 display: false 11724 }; 11725 } 11726 for (var key in ticks) { 11727 if (key !== 'major' && key !== 'minor') { 11728 if (typeof ticks.minor[key] === 'undefined') { 11729 ticks.minor[key] = ticks[key]; 11730 } 11731 if (typeof ticks.major[key] === 'undefined') { 11732 ticks.major[key] = ticks[key]; 11733 } 11734 } 11735 } 11736 }, 11737 beforeUpdate: function() { 11738 helpers.callback(this.options.beforeUpdate, [this]); 11739 }, 11740 update: function(maxWidth, maxHeight, margins) { 11741 var me = this; 11742 var i, ilen, labels, label, ticks, tick; 11743 11744 // Update Lifecycle - Probably don't want to ever extend or over
11744write this function ;) 11745 me.beforeUpdate(); 11746 11747 // Absorb the master measurements 11748 me.maxWidth = maxWidth; 11749 me.maxHeight = maxHeight; 11750 me.margins = helpers.extend({ 11751 left: 0, 11752 right: 0, 11753 top: 0, 11754 bottom: 0 11755 }, margins); 11756 me.longestTextCache = me.longestTextCache || {}; 11757 11758 // Dimensions 11759 me.beforeSetDimensions(); 11760 me.setDimensions(); 11761 me.afterSetDimensions(); 11762 11763 // Data min/max 11764 me.beforeDataLimits(); 11765 me.determineDataLimits(); 11766 me.afterDataLimits(); 11767 11768 // Ticks - `this.ticks` is now DEPRECATED! 11769 // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member 11770 // and must not be accessed directly from outside this class. `this.ticks` being 11771 // around for long time and not marked as private, we can't change its structure 11772 // without unexpected breaking changes. If you need to access the scale ticks, 11773 // use scale.getTicks() instead. 11774 11775 me.beforeBuildTicks(); 11776 11777 // New implementations should return an array of objects but for BACKWARD COMPAT, 11778 // we still support no return (`this.ticks` internally set by calling this method). 11779 ticks = me.buildTicks() || []; 11780 11781 me.afterBuildTicks(); 11782 11783 me.beforeTickToLabelConversion(); 11784 11785 // New implementations should return the formatted tick labels but for BACKWARD 11786 // COMPAT, we still support no return (`this.ticks` internally changed by calling 11787 // this method and supposed to contain only string values). 11788 labels = me.convertTicksToLabels(ticks) || me.ticks; 11789 11790 me.afterTickToLabelConversion(); 11791 11792 me.ticks = labels; // BACKWARD COMPATIBILITY 11793 11794 // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! 11795 11796 // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) 11797 for (i = 0, ilen = labels.length; i < ilen; ++i) { 11798 label = labels[i]; 11799 tick = ticks[i]; 11800 if (!tick) { 11801 ticks.push(tick = { 11802 label: label, 11803 major: false 11804 }); 11805 } else { 11806 tick.label = label; 11807 } 11808 } 11809 11810 me._ticks = ticks; 11811 11812 // Tick Rotation 11813 me.beforeCalculateTickRotation(); 11814 me.calculateTickRotation(); 11815 me.afterCalculateTickRotation(); 11816 // Fit 11817 me.beforeFit(); 11818 me.fit(); 11819 me.afterFit(); 11820 // 11821 me.afterUpdate(); 11822 11823 return me.minSize; 11824 11825 }, 11826 afterUpdate: function() { 11827 helpers.callback(this.options.afterUpdate, [this]); 11828 }, 11829 11830 // 11831 11832 beforeSetDimensions: function() { 11833 helpers.callback(this.options.beforeSetDimensions, [this]); 11834 }, 11835 setDimensions: function() { 11836 var me = this; 11837 // Set the unconstrained dimension before label rotation 11838 if (me.isHorizontal()) { 11839 // Reset position before calculating rotation 11840 me.width = me.maxWidth; 11841 me.left = 0; 11842 me.right = me.width; 11843 } else { 11844 me.height = me.maxHeight; 11845 11846 // Reset position before calculating rotation 11847 me.top = 0; 11848 me.bottom = me.height; 11849 } 11850 11851 // Reset padding 11852 me.paddingLeft = 0; 11853 me.paddingTop = 0; 11854 me.paddingRight = 0; 11855 me.paddingBottom = 0; 11856 }, 11857 afterSetDimensions: function() { 11858 helpers.callback(this.options.afterSetDimensions, [this]); 11859 }, 11860 11861 // Data limits 11862 beforeDataLimits: function() { 11863 helpers.callback(this.options.beforeDataLimits, [this]); 11864 }, 11865 determineDataLimits: helpers.noop, 11866 afterDataLimits: function() { 11867 helpers.callback(this.options.afterDataLimits, [this]); 11868 }, 11869 11870 // 11871 beforeBuildTicks: function() { 11872 helpers.callback(this.options.beforeBuildTicks, [this]); 11873 }, 11874 buildTicks: helpers.noop, 11875 afterBuildTicks: function() { 11876 helpers.callback(this.options.afterBuildTicks, [this]); 11877 }, 11878 11879 beforeTickToLabelConversion: function() { 11880 helpers.callback(this.options.beforeTickToLabelConversion, [this]); 11881 }, 11882 convertTicksToLabels: function() { 11883 var me = this; 11884 // Convert ticks to strings 11885 var tickOpts = me.options.ticks; 11886 me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); 11887 }, 11888 afterTickToLabelConversion: function() { 11889 helpers.callback(this.options.afterTickToLabelConversion, [this]); 11890 }, 11891 11892 // 11893 11894 beforeCalculateTickRotation: function() { 11895 helpers.callback(this.options.beforeCalculateTickRotation, [this]); 11896 }, 11897 calculateTickRotation: function() { 11898 var me = this; 11899 var context = me.ctx; 11900 var tickOpts = me.options.ticks; 11901 var labels = labelsFromTicks(me._ticks); 11902 11903 // Get the width of each grid by calculating the difference 11904 // between x offsets between 0 and 1.
11905 var tickFont = parseFontOptions(tickOpts); 11906 context.font = tickFont.font; 11907 11908 var labelRotation = tickOpts.minRotation || 0; 11909 11910 if (labels.length && me.options.display && me.isHorizontal()) { 11911 var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); 11912 var labelWidth = originalLabelWidth; 11913 var cosRotation, sinRotation; 11914 11915 // Allow 3 pixels x2 padding either side for label readability 11916 var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; 11917 11918 // Max label rotation can be set or default to 90 - also act as a loop counter 11919 while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { 11920 var angleRadians = helpers.toRadians(labelRotation); 11921 cosRotation = Math.cos(angleRadians); 11922 sinRotation = Math.sin(angleRadians); 11923 11924 if (sinRotation * originalLabelWidth > me.maxHeight) { 11925 // go back one step 11926 labelRotation--; 11927 break; 11928 } 11929 11930 labelRotation++; 11931 labelWidth = cosRotation * originalLabelWidth; 11932 } 11933 } 11934 11935 me.labelRotation = labelRotation; 11936 }, 11937 afterCalculateTickRotation: function() { 11938 helpers.callback(this.options.afterCalculateTickRotation, [this]); 11939 }, 11940 11941 // 11942 11943 beforeFit: function() { 11944 helpers.callback(this.options.beforeFit, [this]); 11945 }, 11946 fit: function() { 11947 var me = this; 11948 // Reset 11949 var minSize = me.minSize = { 11950 width: 0, 11951 height: 0 11952 }; 11953 11954 var labels = labelsFromTicks(me._ticks); 11955 11956 var opts = me.options; 11957 var tickOpts = opts.ticks; 11958 var scaleLabelOpts = opts.scaleLabel; 11959 var gridLineOpts = opts.gridLines; 11960 var display = opts.display; 11961 var isHorizontal = me.isHorizontal(); 11962 11963 var tickFont = parseFontOptions(tickOpts); 11964 var tickMarkLength = opts.gridLines.tickMarkLength; 11965 11966 // Width 11967 if (isHorizontal) { 11968 // subtract the margins to line up with the chartArea if we are a full width scale 11969 minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; 11970 } else { 11971 minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; 11972 } 11973 11974 // height 11975 if (isHorizontal) { 11976 minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; 11977 } else { 11978 minSize.height = me.maxHeight; // fill all the height 11979 } 11980 11981 // Are we showing a title for the scale? 11982 if (scaleLabelOpts.display && display) { 11983 var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); 11984 var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); 11985 var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; 11986 11987 if (isHorizontal) { 11988 minSize.height += deltaHeight; 11989 } else { 11990 minSize.width += deltaHeight; 11991 } 11992 } 11993 11994 // Don't bother fitting the ticks if we are not showing them 11995 if (tickOpts.display && display) { 11996 var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); 11997 var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); 11998 var lineSpace = tickFont.size * 0.5; 11999 var tickPadding = me.options.ticks.padding; 12000 12001 if (isHorizontal) { 12002 // A horizontal axis is more constrained by the height. 12003 me.longestLabelWidth = largestTextWidth; 12004 12005 var angleRadians = helpers.toRadians(me.labelRotation); 12006 var cosRotation = Math.cos(angleRadians); 12007 var sinRotation = Math.sin(angleRadians); 12008 12009 // TODO - improve this calculation 12010 var labelHeight = (sinRotation * largestTextWidth) 12011 + (tickFont.size * tallestLabelHeightInLines) 12012 + (lineSpace * (tallestLabelHeightInLines - 1)) 12013 + lineSpace; // padding 12014 12015 minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); 12016 12017 me.ctx.font = tickFont.font; 12018 var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); 12019 var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); 12020 12021 // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned 12022 // which means that the right padding is dominated by the font height 12023 if (me.labelRotation !== 0) { 12024 me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges 12025 me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; 12026 } else {
12027 me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges 12028 me.paddingRight = lastLabelWidth / 2 + 3; 12029 } 12030 } else { 12031 // A vertical axis is more constrained by the width. Labels are the 12032 // dominant factor here, so get that length first and account for padding 12033 if (tickOpts.mirror) { 12034 largestTextWidth = 0; 12035 } else { 12036 // use lineSpace for consistency with horizontal axis 12037 // tickPadding is not implemented for horizontal 12038 largestTextWidth += tickPadding + lineSpace; 12039 } 12040 12041 minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); 12042 12043 me.paddingTop = tickFont.size / 2; 12044 me.paddingBottom = tickFont.size / 2; 12045 } 12046 } 12047 12048 me.handleMargins(); 12049 12050 me.width = minSize.width; 12051 me.height = minSize.height; 12052 }, 12053 12054 /** 12055 * Handle margins and padding interactions 12056 * @private 12057 */ 12058 handleMargins: function() { 12059 var me = this; 12060 if (me.margins) { 12061 me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); 12062 me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); 12063 me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); 12064 me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); 12065 } 12066 }, 12067 12068 afterFit: function() { 12069 helpers.callback(this.options.afterFit, [this]); 12070 }, 12071 12072 // Shared Methods 12073 isHorizontal: function() { 12074 return this.options.position === 'top' || this.options.position === 'bottom'; 12075 }, 12076 isFullWidth: function() { 12077 return (this.options.fullWidth); 12078 }, 12079 12080 // 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 12081 getRightValue: function(rawValue) { 12082 // Null and undefined values first 12083 if (helpers.isNullOrUndef(rawValue)) { 12084 return NaN; 12085 } 12086 // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values 12087 if (typeof rawValue === 'number' && !isFinite(rawValue)) { 12088 return NaN; 12089 } 12090 // If it is in fact an object, dive in one more level 12091 if (rawValue) { 12092 if (this.isHorizontal()) { 12093 if (rawValue.x !== undefined) { 12094 return this.getRightValue(rawValue.x); 12095 } 12096 } else if (rawValue.y !== undefined) { 12097 return this.getRightValue(rawValue.y); 12098 } 12099 } 12100 12101 // Value is good, return it 12102 return rawValue; 12103 }, 12104 12105 /** 12106 * Used to get the value to display in the tooltip for the data at the given index 12107 * @param index 12108 * @param datasetIndex 12109 */ 12110 getLabelForIndex: helpers.noop, 12111 12112 /** 12113 * Returns the location of the given data point. Value can either be an index or a numerical value 12114 * The coordinate (0, 0) is at the upper-left corner of the canvas 12115 * @param value 12116 * @param index 12117 * @param datasetIndex 12118 */ 12119 getPixelForValue: helpers.noop, 12120 12121 /** 12122 * Used to get the data value from a given pixel. This is the inverse of getPixelForValue 12123 * The coordinate (0, 0) is at the upper-left corner of the canvas 12124 * @param pixel 12125 */ 12126 getValueForPixel: helpers.noop, 12127 12128 /** 12129 * Returns the location of the tick at the given index 12130 * The coordinate (0, 0) is at the upper-left corner of the canvas 12131 */ 12132 getPixelForTick: function(index) { 12133 var me = this; 12134 var offset = me.options.offset; 12135 if (me.isHorizontal()) { 12136 var innerWidth = me.width - (me.paddingLeft + me.paddingRight); 12137 var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); 12138 var pixel = (tickWidth * index) + me.paddingLeft; 12139 12140 if (offset) { 12141 pixel += tickWidth / 2; 12142 } 12143 12144 var finalVal = me.left + Math.round(pixel); 12145 finalVal += me.isFullWidth() ? me.margins.left : 0; 12146 return finalVal; 12147 } 12148 var innerHeight = me.height - (me.paddingTop + me.paddingBottom); 12149 return me.top + (index * (innerHeight / (me._ticks.length - 1))); 12150 }, 12151 12152 /** 12153 * Utility for getting the pixel location of a percentage of scale 12154 * The coordinate (0, 0) is at the upper-left corner of the canvas 12155 */ 12156 getPixelForDecimal: function(decimal) { 12157 var me = this; 12158 if (me.isHorizontal()) { 12159 var innerWidth = me.width - (me.paddingLeft + me.paddingRight); 12160 var valueOffset = (innerWidth * decimal) + me.paddingLeft; 12161 12162 var finalVal = me.left + Math.round(valueOffset); 12163 finalVal += me.isFullWidth() ? me.margins.left : 0; 12164 return finalVal; 12165 } 12166 return me.top + (decimal * me.height); 12167 }, 12168 12169 /** 12170 * Returns the pixel for the minimum chart value 12171 * The coordinate (0, 0) is at the upper-left corner of the canvas 12172 */ 12173 getBasePixel: function() { 12174 return this.getPixelForValue(this.getBaseValue()); 12175 }, 12176 12177 getBaseValue: function() { 12178 var me = this; 12179 var min = me.min; 12180 var max = me.max; 12181 12182 return me.beginAtZero ? 0 : 12183 min < 0 && max < 0 ? max : 12184 min > 0 && max > 0 ? min : 12185 0; 12186 }, 12187 12188 /** 12189 * Returns a subset of ticks to be plotted to avoid overlapping labels. 12190 * @private 12191 */ 12192 _autoSkip: function(ticks) { 12193 var skipRatio; 12194 var me = this; 12195 var isHorizontal = me.isHorizontal(); 12196 var optionTicks = me.options.ticks.minor; 12197 var tickCount = ticks.length; 12198 var labelRotationRadians = helpers.toRadians(me.labelRotation); 12199 var cosRotation = Math.cos(labelRotationRadians); 12200 var longestRotatedLabel = me.longestLabelWidth * cosRotation; 12201 var result = []; 12202 var i, tick, shouldSkip; 12203
12204 // figure out the maximum number of gridlines to show 12205 var maxTicks; 12206 if (optionTicks.maxTicksLimit) { 12207 maxTicks = optionTicks.maxTicksLimit; 12208 } 12209 12210 if (isHorizontal) { 12211 skipRatio = false; 12212 12213 if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { 12214 skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); 12215 } 12216 12217 // if they defined a max number of optionTicks, 12218 // increase skipRatio until that number is met 12219 if (maxTicks && tickCount > maxTicks) { 12220 skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); 12221 } 12222 } 12223 12224 for (i = 0; i < tickCount; i++) { 12225 tick = ticks[i]; 12226 12227 // Since we always show the last tick,we need may need to hide the last shown one before 12228 shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); 12229 if (shouldSkip && i !== tickCount - 1) { 12230 // leave tick in place but make sure it's not displayed (#4635) 12231 delete tick.label; 12232 } 12233 result.push(tick); 12234 } 12235 return result; 12236 }, 12237 12238 // Actually draw the scale on the canvas 12239 // @param {rectangle} chartArea : the area of the chart to draw full grid lines on 12240 draw: function(chartArea) { 12241 var me = this; 12242 var options = me.options; 12243 if (!options.display) { 12244 return; 12245 } 12246 12247 var context = me.ctx; 12248 var globalDefaults = defaults.global; 12249 var optionTicks = options.ticks.minor; 12250 var optionMajorTicks = options.ticks.major || optionTicks; 12251 var gridLines = options.gridLines; 12252 var scaleLabel = options.scaleLabel; 12253 12254 var isRotated = me.labelRotation !== 0; 12255 var isHorizontal = me.isHorizontal(); 12256 12257 var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); 12258 var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); 12259 var tickFont = parseFontOptions(optionTicks); 12260 var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); 12261 var majorTickFont = parseFontOptions(optionMajorTicks); 12262 12263 var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; 12264 12265 var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); 12266 var scaleLabelFont = parseFontOptions(scaleLabel); 12267 var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); 12268 var labelRotationRadians = helpers.toRadians(me.labelRotation); 12269 12270 var itemsToDraw = []; 12271 12272 var axisWidth = me.options.gridLines.lineWidth; 12273 var xTickStart = options.position === 'right' ? me.right : me.right - axisWidth - tl; 12274 var xTickEnd = options.position === 'right' ? me.right + tl : me.right; 12275 var yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth; 12276 var yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth; 12277 12278 helpers.each(ticks, function(tick, index) { 12279 // autoskipper skipped this tick (#4635) 12280 if (helpers.isNullOrUndef(tick.label)) { 12281 return; 12282 } 12283 12284 var label = tick.label; 12285 var lineWidth, lineColor, borderDash, borderDashOffset; 12286 if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { 12287 // Draw the first index specially 12288 lineWidth = gridLines.zeroLineWidth; 12289 lineColor = gridLines.zeroLineColor; 12290 borderDash = gridLines.zeroLineBorderDash; 12291 borderDashOffset = gridLines.zeroLineBorderDashOffset; 12292 } else { 12293 lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); 12294 lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); 12295 borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); 12296 borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); 12297 } 12298 12299 // Common properties 12300 var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; 12301 var textAlign = 'middle'; 12302 var textBaseline = 'middle'; 12303 var tickPadding = optionTicks.padding; 12304 12305 if (isHorizontal) { 12306 var labelYOffset = tl + tickPadding; 12307 12308 if (options.position === 'bottom') { 12309 // bottom 12310 textBaseline = !isRotated ? 'top' : 'middle'; 12311 textAlign = !isRotated ? 'center' : 'right'; 12312 labelY = me.top + labelYOffset; 12313 } else { 12314 // top 12315 textBaseline = !isRotated ? 'bottom' : 'middle'; 12316 textAlign = !isRotated ? 'center' : 'left'; 12317 labelY = me.bottom - labelYOffset; 12318 } 12319 12320 var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); 12321 if (xLineValue < me.left) { 12322 lineColor = 'rgba(0,0,0,0)'; 12323 } 12324 xLineValue += helpers.aliasPixel(lineWidth); 12325 12326 labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) 12327 12328 tx1 = tx2 = x1 = x2 = xLineValue; 12329 ty1 = yTickStart; 12330 ty2 = yTickEnd; 12331 y1 = chartArea.top; 12332 y2 = chartArea.bottom + axisWidth; 12333 } else { 12334 var isLeft = options.position === 'left'; 12335 var labelXOffset; 12336 12337 if (optionTicks.mirror) { 12338 textAlign = isLeft ? 'left' : 'right'; 12339 labelXOffset = tickPadding; 12340 } else { 12341 textAlign = isLeft ? 'right' : 'left'; 12342 labelXOffset = tl + tickPadding; 12343 } 12344 12345 labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; 12346 12347 var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); 12348 if (yLineValue < me.top) { 12349 lineColor = 'rgba(0,0,0,0)'; 12350 } 12351 yLineValue += helpers.aliasPixel(lineWidth); 12352 12353 labelY = me.getPixelForTick(index) + optionTicks.labelOffset; 12354 12355 tx1 = xTickStart; 12356 tx2 = xTickEnd; 12357 x1 = chartArea.left; 12358 x2 = chartArea.right + axisWidth; 12359 ty1 = ty2 = y1 = y2 = yLineValue; 12360 } 12361 12362 itemsToDraw.push({ 12363 tx1: tx1, 12364 ty1: ty1, 12365 tx2: tx2, 12366 ty2: ty2, 12367 x1: x1, 12368 y1: y1, 12369 x2: x2, 12370 y2: y2, 12371 labelX: labelX, 12372 labelY: labelY, 12373 glWidth: lineWidth, 12374 glColor: lineColor, 12375 glBorderDash: borderDash, 12376 glBorderDashOffset: borderDashOffset, 12377 rotation: -1 * labelRotationRadians, 12378 label: label, 12379 major: tick.major, 12380 textBaseline: textBaseline, 12381 textAlign: textAlign 12382 }); 12383 }); 12384 12385 // Draw all of the tick labels, tick marks, and grid lines at the correct places 12386 helpers.each(itemsToDraw, function(itemToDraw) { 12387 if (gridLines.display) { 12388 context.save(); 12389 context.lineWidth = itemToDraw.glWidth; 12390 context.strokeStyle = itemToDraw.glColor; 12391 if (context.setLineDash) { 12392 context.setLineDash(itemToDraw.glBorderDash); 12393 context.lineDashOffset = itemToDraw.glBorderDashOffset; 12394 } 12395 12396 context.beginPath(); 12397 12398 if (gridLines.drawTicks) { 12399 context.moveTo(itemToDraw.tx1, itemToDraw.ty1); 12400 context.lineTo(itemToDraw.tx2, itemToDraw.ty2); 12401 } 12402 12403 if (gridLines.drawOnChartArea) { 12404 context.moveTo(itemToDraw.x1, itemToDraw.y1); 12405 context.lineTo(itemToDraw.x2, itemToDraw.y2); 12406 } 12407 12408 context.stroke();
12409 context.restore(); 12410 } 12411 12412 if (optionTicks.display) { 12413 // Make sure we draw text in the correct color and font 12414 context.save(); 12415 context.translate(itemToDraw.labelX, itemToDraw.labelY); 12416 context.rotate(itemToDraw.rotation); 12417 context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; 12418 context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; 12419 context.textBaseline = itemToDraw.textBaseline; 12420 context.textAlign = itemToDraw.textAlign; 12421 12422 var label = itemToDraw.label; 12423 if (helpers.isArray(label)) { 12424 var lineCount = label.length; 12425 var lineHeight = tickFont.size * 1.5; 12426 var y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2; 12427 12428 for (var i = 0; i < lineCount; ++i) { 12429 // We just make sure the multiline element is a string here.. 12430 context.fillText('' + label[i], 0, y); 12431 // apply same lineSpacing as calculated @ L#320 12432 y += lineHeight; 12433 } 12434 } else { 12435 context.fillText(label, 0, 0); 12436 } 12437 context.restore(); 12438 } 12439 }); 12440 12441 if (scaleLabel.display) { 12442 // Draw the scale label 12443 var scaleLabelX; 12444 var scaleLabelY; 12445 var rotation = 0; 12446 var halfLineHeight = parseLineHeight(scaleLabel) / 2; 12447 12448 if (isHorizontal) { 12449 scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width 12450 scaleLabelY = options.position === 'bottom' 12451 ? me.bottom - halfLineHeight - scaleLabelPadding.bottom 12452 : me.top + halfLineHeight + scaleLabelPadding.top; 12453 } else { 12454 var isLeft = options.position === 'left'; 12455 scaleLabelX = isLeft 12456 ? me.left + halfLineHeight + scaleLabelPadding.top 12457 : me.right - halfLineHeight - scaleLabelPadding.top; 12458 scaleLabelY = me.top + ((me.bottom - me.top) / 2); 12459 rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; 12460 } 12461 12462 context.save(); 12463 context.translate(scaleLabelX, scaleLabelY); 12464 context.rotate(rotation); 12465 context.textAlign = 'center'; 12466 context.textBaseline = 'middle'; 12467 context.fillStyle = scaleLabelFontColor; // render in correct colour 12468 context.font = scaleLabelFont.font;
12469 context.fillText(scaleLabel.labelString, 0, 0); 12470 context.restore(); 12471 } 12472 12473 if (gridLines.drawBorder) { 12474 // Draw the line at the edge of the axis 12475 context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); 12476 context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); 12477 var x1 = me.left; 12478 var x2 = me.right + axisWidth; 12479 var y1 = me.top; 12480 var y2 = me.bottom + axisWidth; 12481 12482 var aliasPixel = helpers.aliasPixel(context.lineWidth); 12483 if (isHorizontal) { 12484 y1 = y2 = options.position === 'top' ? me.bottom : me.top; 12485 y1 += aliasPixel; 12486 y2 += aliasPixel; 12487 } else { 12488 x1 = x2 = options.position === 'left' ? me.right : me.left; 12489 x1 += aliasPixel; 12490 x2 += aliasPixel; 12491 } 12492 12493 context.beginPath(); 12494 context.moveTo(x1, y1); 12495 context.lineTo(x2, y2); 12496 context.stroke(); 12497 } 12498 } 12499 }); 12500}; 12501 12502},{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){ 12503'use strict'; 12504 12505var defaults = require(25); 12506var helpers = require(45); 12507var layouts = require(30); 12508 12509module.exports = function(Chart) { 12510 12511 Chart.scaleService = { 12512 // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then 12513 // use the new chart options to grab the correct scale 12514 constructors: {}, 12515 // Use a registration function so that we can move to an ES6 map when we no longer need to support 12516 // old browsers 12517 12518 // Scale config defaults 12519 defaults: {}, 12520 registerScaleType: function(type, scaleConstructor, scaleDefaults) { 12521 this.constructors[type] = scaleConstructor; 12522 this.defaults[type] = helpers.clone(scaleDefaults); 12523 }, 12524 getScaleConstructor: function(type) { 12525 return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; 12526 }, 12527 getScaleDefaults: function(type) { 12528 // Return the scale defaults merged with the global settings so that we always use the latest ones 12529 return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; 12530 }, 12531 updateScaleDefaults: function(type, additions) { 12532 var me = this; 12533 if (me.defaults.hasOwnProperty(type)) { 12534 me.defaults[type] = helpers.extend(me.defaults[type], additions); 12535 } 12536 }, 12537 addScalesToLayout: function(chart) { 12538 // Adds each scale to the chart.boxes array to be sized accordingly 12539 helpers.each(chart.scales, function(scale) { 12540 // Set ILayoutItem parameters for backwards compatibility 12541 scale.fullWidth = scale.options.fullWidth; 12542 scale.position = scale.options.position; 12543 scale.weight = scale.options.weight; 12544 layouts.addBox(chart, scale); 12545 }); 12546 } 12547 }; 12548}; 12549 12550},{"25":25,"30":30,"45":45}],34:[function(require,module,exports){ 12551'use strict'; 12552 12553var helpers = require(45); 12554 12555/** 12556 * Namespace to hold static tick generation functions 12557 * @namespace Chart.Ticks 12558 */ 12559module.exports = { 12560 /** 12561 * Namespace to hold formatters for different types of ticks 12562 * @namespace Chart.Ticks.formatters 12563 */ 12564 formatters: { 12565 /** 12566 * Formatter for value labels 12567 * @method Chart.Ticks.formatters.values 12568 * @param value the value to display 12569 * @return {String|Array} the label to display 12570 */ 12571 values: function(value) { 12572 return helpers.isArray(value) ? value : '' + value; 12573 }, 12574 12575 /** 12576 * Formatter for linear numeric ticks 12577 * @method Chart.Ticks.formatters.linear 12578 * @param tickValue {Number} the value to be formatted 12579 * @param index {Number} the position of the tickValue parameter in the ticks array 12580 * @param ticks {Array<Number>} the list of ticks being converted 12581 * @return {String} string representation of the tickValue parameter 12582 */ 12583 linear: function(tickValue, index, ticks) { 12584 // If we have lots of ticks, don't use the ones 12585 var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; 12586 12587 // If we have a number like 2.5 as the delta, figure out how many decimal places we need 12588 if (Math.abs(delta) > 1) { 12589 if (tickValue !== Math.floor(tickValue)) { 12590 // not an integer 12591 delta = tickValue - Math.floor(tickValue); 12592 } 12593 } 12594 12595 var logDelta = helpers.log10(Math.abs(delta)); 12596 var tickString = ''; 12597 12598 if (tickValue !== 0) { 12599 var numDecimal = -1 * Math.floor(logDelta); 12600 numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places 12601 tickString = tickValue.toFixed(numDecimal); 12602 } else { 12603 tickString = '0'; // never show decimal places for 0 12604 } 12605 12606 return tickString; 12607 }, 12608 12609 logarithmic: function(tickValue, index, ticks) { 12610 var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); 12611 12612 if (tickValue === 0) { 12613 return '0'; 12614 } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { 12615 return tickValue.toExponential(); 12616 } 12617 return ''; 12618 } 12619 } 12620}; 12621 12622},{"45":45}],35:[function(require,module,exports){ 12623'use strict'; 12624 12625var defaults = require(25); 12626var Element = require(26); 12627var helpers = require(45); 12628 12629defaults._set('global', { 12630 tooltips: { 12631 enabled: true, 12632 custom: null, 12633 mode: 'nearest', 12634 position: 'average', 12635 intersect: true, 12636 backgroundColor: 'rgba(0,0,0,0.8)', 12637 titleFontStyle: 'bold', 12638 titleSpacing: 2, 12639 titleMarginBottom: 6, 12640 titleFontColor: '#fff', 12641 titleAlign: 'left', 12642 bodySpacing: 2, 12643 bodyFontColor: '#fff', 12644 bodyAlign: 'left', 12645 footerFontStyle: 'bold', 12646 footerSpacing: 2, 12647 footerMarginTop: 6, 12648 footerFontColor: '#fff', 12649 footerAlign: 'left', 12650 yPadding: 6, 12651 xPadding: 6, 12652 caretPadding: 2, 12653 caretSize: 5, 12654 cornerRadius: 6,
12655 multiKeyBackground: '#fff', 12656 displayColors: true, 12657 borderColor: 'rgba(0,0,0,0)', 12658 borderWidth: 0, 12659 callbacks: { 12660 // Args are: (tooltipItems, data) 12661 beforeTitle: helpers.noop, 12662 title: function(tooltipItems, data) { 12663 // Pick first xLabel for now 12664 var title = ''; 12665 var labels = data.labels; 12666 var labelCount = labels ? labels.length : 0; 12667 12668 if (tooltipItems.length > 0) { 12669 var item = tooltipItems[0]; 12670 12671 if (item.xLabel) { 12672 title = item.xLabel; 12673 } else if (labelCount > 0 && item.index < labelCount) { 12674 title = labels[item.index]; 12675 } 12676 } 12677 12678 return title; 12679 }, 12680 afterTitle: helpers.noop, 12681 12682 // Args are: (tooltipItems, data) 12683 beforeBody: helpers.noop, 12684 12685 // Args are: (tooltipItem, data) 12686 beforeLabel: helpers.noop, 12687 label: function(tooltipItem, data) { 12688 var label = data.datasets[tooltipItem.datasetIndex].label || ''; 12689 12690 if (label) { 12691 label += ': '; 12692 } 12693 label += tooltipItem.yLabel; 12694 return label; 12695 }, 12696 labelColor: function(tooltipItem, chart) { 12697 var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); 12698 var activeElement = meta.data[tooltipItem.index]; 12699 var view = activeElement._view; 12700 return { 12701 borderColor: view.borderColor, 12702 backgroundColor: view.backgroundColor 12703 }; 12704 }, 12705 labelTextColor: function() { 12706 return this._options.bodyFontColor; 12707 }, 12708 afterLabel: helpers.noop, 12709 12710 // Args are: (tooltipItems, data) 12711 afterBody: helpers.noop, 12712 12713 // Args are: (tooltipItems, data) 12714 beforeFooter: helpers.noop, 12715 footer: helpers.noop, 12716 afterFooter: helpers.noop 12717 } 12718 } 12719}); 12720 12721module.exports = function(Chart) { 12722 12723 /** 12724 * Helper method to merge the opacity into a color 12725 */
vendor: 9,332 bytes, lines 12726-13029
12726 function mergeOpacity(colorString, opacity) { 12727 var color = helpers.color(colorString); 12728 return color.alpha(opacity * color.alpha()).rgbaString(); 12729 } 12730 12731 // Helper to push or concat based on if the 2nd parameter is an array or not 12732 function pushOrConcat(base, toPush) { 12733 if (toPush) { 12734 if (helpers.isArray(toPush)) { 12735 // base = base.concat(toPush); 12736 Array.prototype.push.apply(base, toPush); 12737 } else { 12738 base.push(toPush); 12739 } 12740 } 12741 12742 return base; 12743 } 12744 12745 // Private helper to create a tooltip item model 12746 // @param element : the chart element (point, arc, bar) to create the tooltip item for 12747 // @return : new tooltip item 12748 function createTooltipItem(element) { 12749 var xScale = element._xScale; 12750 var yScale = element._yScale || element._scale; // handle radar || polarArea charts 12751 var index = element._index; 12752 var datasetIndex = element._datasetIndex; 12753 12754 return { 12755 xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', 12756 yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', 12757 index: index, 12758 datasetIndex: datasetIndex, 12759 x: element._model.x, 12760 y: element._model.y 12761 }; 12762 } 12763 12764 /** 12765 * Helper to get the reset model for the tooltip 12766 * @param tooltipOpts {Object} the tooltip options 12767 */ 12768 function getBaseModel(tooltipOpts) { 12769 var globalDefaults = defaults.global; 12770 var valueOrDefault = helpers.valueOrDefault; 12771 12772 return { 12773 // Positioning 12774 xPadding: tooltipOpts.xPadding, 12775 yPadding: tooltipOpts.yPadding, 12776 xAlign: tooltipOpts.xAlign, 12777 yAlign: tooltipOpts.yAlign, 12778 12779 // Body 12780 bodyFontColor: tooltipOpts.bodyFontColor, 12781 _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), 12782 _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), 12783 _bodyAlign: tooltipOpts.bodyAlign, 12784 bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), 12785 bodySpacing: tooltipOpts.bodySpacing, 12786 12787 // Title 12788 titleFontColor: tooltipOpts.titleFontColor, 12789 _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), 12790 _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), 12791 titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), 12792 _titleAlign: tooltipOpts.titleAlign, 12793 titleSpacing: tooltipOpts.titleSpacing, 12794 titleMarginBottom: tooltipOpts.titleMarginBottom, 12795 12796 // Footer 12797 footerFontColor: tooltipOpts.footerFontColor, 12798 _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), 12799 _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), 12800 footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), 12801 _footerAlign: tooltipOpts.footerAlign, 12802 footerSpacing: tooltipOpts.footerSpacing, 12803 footerMarginTop: tooltipOpts.footerMarginTop, 12804 12805 // Appearance 12806 caretSize: tooltipOpts.caretSize, 12807 cornerRadius: tooltipOpts.cornerRadius, 12808 backgroundColor: tooltipOpts.backgroundColor, 12809 opacity: 0, 12810 legendColorBackground: tooltipOpts.multiKeyBackground, 12811 displayColors: tooltipOpts.displayColors, 12812 borderColor: tooltipOpts.borderColor, 12813 borderWidth: tooltipOpts.borderWidth 12814 }; 12815 } 12816 12817 /** 12818 * Get the size of the tooltip 12819 */ 12820 function getTooltipSize(tooltip, model) { 12821 var ctx = tooltip._chart.ctx; 12822 12823 var height = model.yPadding * 2; // Tooltip Padding 12824 var width = 0; 12825 12826 // Count of all lines in the body 12827 var body = model.body; 12828 var combinedBodyLength = body.reduce(function(count, bodyItem) { 12829 return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; 12830 }, 0); 12831 combinedBodyLength += model.beforeBody.length + model.afterBody.length; 12832 12833 var titleLineCount = model.title.length; 12834 var footerLineCount = model.footer.length; 12835 var titleFontSize = model.titleFontSize; 12836 var bodyFontSize = model.bodyFontSize; 12837 var footerFontSize = model.footerFontSize; 12838 12839 height += titleLineCount * titleFontSize; // Title Lines 12840 height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing 12841 height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin 12842 height += combinedBodyLength * bodyFontSize; // Body Lines 12843 height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing 12844 height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin 12845 height += footerLineCount * (footerFontSize); // Footer Lines 12846 height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing 12847 12848 // Title width 12849 var widthPadding = 0; 12850 var maxLineWidth = function(line) { 12851 width = Math.max(width, ctx.measureText(line).width + widthPadding); 12852 }; 12853 12854 ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); 12855 helpers.each(model.title, maxLineWidth); 12856 12857 // Body width 12858 ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); 12859 helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); 12860 12861 // Body lines may include some extra width due to the color box 12862 widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; 12863 helpers.each(body, function(bodyItem) { 12864 helpers.each(bodyItem.before, maxLineWidth); 12865 helpers.each(bodyItem.lines, maxLineWidth); 12866 helpers.each(bodyItem.after, maxLineWidth); 12867 }); 12868 12869 // Reset back to 0 12870 widthPadding = 0; 12871 12872 // Footer width 12873 ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); 12874 helpers.each(model.footer, maxLineWidth); 12875 12876 // Add padding 12877 width += 2 * model.xPadding; 12878 12879 return { 12880 width: width, 12881 height: height 12882 }; 12883 } 12884 12885 /** 12886 * Helper to get the alignment of a tooltip given the size 12887 */ 12888 function determineAlignment(tooltip, size) { 12889 var model = tooltip._model; 12890 var chart = tooltip._chart; 12891 var chartArea = tooltip._chart.chartArea; 12892 var xAlign = 'center'; 12893 var yAlign = 'center'; 12894 12895 if (model.y < size.height) { 12896 yAlign = 'top'; 12897 } else if (model.y > (chart.height - size.height)) { 12898 yAlign = 'bottom'; 12899 } 12900 12901 var lf, rf; // functions to determine left, right alignment 12902 var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart 12903 var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges 12904 var midX = (chartArea.left + chartArea.right) / 2; 12905 var midY = (chartArea.top + chartArea.bottom) / 2; 12906 12907 if (yAlign === 'center') { 12908 lf = function(x) { 12909 return x <= midX; 12910 }; 12911 rf = function(x) { 12912 return x > midX; 12913 }; 12914 } else { 12915 lf = function(x) { 12916 return x <= (size.width / 2); 12917 }; 12918 rf = function(x) { 12919 return x >= (chart.width - (size.width / 2)); 12920 }; 12921 } 12922 12923 olf = function(x) { 12924 return x + size.width + model.caretSize + model.caretPadding > chart.width; 12925 }; 12926 orf = function(x) { 12927 return x - size.width - model.caretSize - model.caretPadding < 0; 12928 }; 12929 yf = function(y) { 12930 return y <= midY ? 'top' : 'bottom'; 12931 }; 12932 12933 if (lf(model.x)) { 12934 xAlign = 'left'; 12935 12936 // Is tooltip too wide and goes over the right side of the chart.? 12937 if (olf(model.x)) { 12938 xAlign = 'center'; 12939 yAlign = yf(model.y); 12940 } 12941 } else if (rf(model.x)) { 12942 xAlign = 'right'; 12943 12944 // Is tooltip too wide and goes outside left edge of canvas? 12945 if (orf(model.x)) { 12946 xAlign = 'center'; 12947 yAlign = yf(model.y); 12948 } 12949 } 12950 12951 var opts = tooltip._options; 12952 return { 12953 xAlign: opts.xAlign ? opts.xAlign : xAlign, 12954 yAlign: opts.yAlign ? opts.yAlign : yAlign 12955 }; 12956 } 12957 12958 /** 12959 * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment 12960 */ 12961 function getBackgroundPoint(vm, size, alignment, chart) { 12962 // Background Position 12963 var x = vm.x; 12964 var y = vm.y; 12965 12966 var caretSize = vm.caretSize; 12967 var caretPadding = vm.caretPadding; 12968 var cornerRadius = vm.cornerRadius; 12969 var xAlign = alignment.xAlign; 12970 var yAlign = alignment.yAlign; 12971 var paddingAndSize = caretSize + caretPadding; 12972 var radiusAndPadding = cornerRadius + caretPadding; 12973 12974 if (xAlign === 'right') { 12975 x -= size.width; 12976 } else if (xAlign === 'center') { 12977 x -= (size.width / 2); 12978 if (x + size.width > chart.width) { 12979 x = chart.width - size.width; 12980 } 12981 if (x < 0) { 12982 x = 0; 12983 } 12984 } 12985 12986 if (yAlign === 'top') { 12987 y += paddingAndSize; 12988 } else if (yAlign === 'bottom') { 12989 y -= size.height + paddingAndSize; 12990 } else { 12991 y -= (size.height / 2); 12992 } 12993 12994 if (yAlign === 'center') { 12995 if (xAlign === 'left') { 12996 x += paddingAndSize; 12997 } else if (xAlign === 'right') { 12998 x -= paddingAndSize; 12999 } 13000 } else if (xAlign === 'left') { 13001 x -= radiusAndPadding; 13002 } else if (xAlign === 'right') { 13003 x += radiusAndPadding; 13004 } 13005 13006 return { 13007 x: x, 13008 y: y 13009 }; 13010 } 13011 13012 Chart.Tooltip = Element.extend({ 13013 initialize: function() { 13014 this._model = getBaseModel(this._options); 13015 this._lastActive = []; 13016 }, 13017 13018 // Get the title 13019 // Args are: (tooltipItem, data) 13020 getTitle: function() { 13021 var me = this; 13022 var opts = me._options; 13023 var callbacks = opts.callbacks; 13024 13025 var beforeTitle = callbacks.beforeTitle.apply(me, arguments); 13026 var title = callbacks.title.apply(me, arguments); 13027 var afterTitle = callbacks.afterTitle.apply(me, arguments); 13028 13029 var lines = [];
13030 lines = pushOrConcat(lines, beforeTitle); 13031 lines = pushOrConcat(lines, title); 13032 lines = pushOrConcat(lines, afterTitle); 13033 13034 return lines; 13035 }, 13036 13037 // Args are: (tooltipItem, data) 13038 getBeforeBody: function() { 13039 var lines = this._options.callbacks.beforeBody.apply(this, arguments); 13040 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; 13041 }, 13042 13043 // Args are: (tooltipItem, data) 13044 getBody: function(tooltipItems, data) { 13045 var me = this; 13046 var callbacks = me._options.callbacks; 13047 var bodyItems = []; 13048 13049 helpers.each(tooltipItems, function(tooltipItem) { 13050 var bodyItem = { 13051 before: [], 13052 lines: [], 13053 after: [] 13054 }; 13055 pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); 13056 pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); 13057 pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); 13058 13059 bodyItems.push(bodyItem); 13060 }); 13061 13062 return bodyItems; 13063 }, 13064 13065 // Args are: (tooltipItem, data) 13066 getAfterBody: function() { 13067 var lines = this._options.callbacks.afterBody.apply(this, arguments); 13068 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; 13069 }, 13070 13071 // Get the footer and beforeFooter and afterFooter lines 13072 // Args are: (tooltipItem, data) 13073 getFooter: function() { 13074 var me = this; 13075 var callbacks = me._options.callbacks; 13076 13077 var beforeFooter = callbacks.beforeFooter.apply(me, arguments); 13078 var footer = callbacks.footer.apply(me, arguments); 13079 var afterFooter = callbacks.afterFooter.apply(me, arguments); 13080 13081 var lines = []; 13082 lines = pushOrConcat(lines, beforeFooter); 13083 lines = pushOrConcat(lines, footer); 13084 lines = pushOrConcat(lines, afterFooter); 13085 13086 return lines; 13087 }, 13088 13089 update: function(changed) { 13090 var me = this; 13091 var opts = me._options; 13092 13093 // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition 13094 // 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 13095 // which breaks any animations. 13096 var existingModel = me._model; 13097 var model = me._model = getBaseModel(opts); 13098 var active = me._active; 13099 13100 var data = me._data; 13101 13102 // In the case where active.length === 0 we need to keep these at existing values for good animations 13103 var alignment = { 13104 xAlign: existingModel.xAlign, 13105 yAlign: existingModel.yAlign 13106 }; 13107 var backgroundPoint = { 13108 x: existingModel.x, 13109 y: existingModel.y 13110 }; 13111 var tooltipSize = { 13112 width: existingModel.width, 13113 height: existingModel.height 13114 }; 13115 var tooltipPosition = { 13116 x: existingModel.caretX, 13117 y: existingModel.caretY 13118 }; 13119 13120 var i, len; 13121 13122 if (active.length) { 13123 model.opacity = 1; 13124 13125 var labelColors = []; 13126 var labelTextColors = []; 13127 tooltipPosition = Chart.Tooltip.positioners[opts.position].call(me, active, me._eventPosition); 13128 13129 var tooltipItems = []; 13130 for (i = 0, len = active.length; i < len; ++i) { 13131 tooltipItems.push(createTooltipItem(active[i])); 13132 } 13133 13134 // If the user provided a filter function, use it to modify the tooltip items 13135 if (opts.filter) { 13136 tooltipItems = tooltipItems.filter(function(a) { 13137 return opts.filter(a, data); 13138 }); 13139 } 13140 13141 // If the user provided a sorting function, use it to modify the tooltip items 13142 if (opts.itemSort) { 13143 tooltipItems = tooltipItems.sort(function(a, b) { 13144 return opts.itemSort(a, b, data); 13145 }); 13146 } 13147 13148 // Determine colors for boxes 13149 helpers.each(tooltipItems, function(tooltipItem) { 13150 labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); 13151 labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); 13152 }); 13153 13154 13155 // Build the Text Lines 13156 model.title = me.getTitle(tooltipItems, data); 13157 model.beforeBody = me.getBeforeBody(tooltipItems, data); 13158 model.body = me.getBody(tooltipItems, data); 13159 model.afterBody = me.getAfterBody(tooltipItems, data); 13160 model.footer = me.getFooter(tooltipItems, data); 13161 13162 // Initial positioning and colors 13163 model.x = Math.round(tooltipPosition.x); 13164 model.y = Math.round(tooltipPosition.y); 13165 model.caretPadding = opts.caretPadding; 13166 model.labelColors = labelColors; 13167 model.labelTextColors = labelTextColors; 13168 13169 // data points 13170 model.dataPoints = tooltipItems; 13171 13172 // We need to determine alignment of the tooltip 13173 tooltipSize = getTooltipSize(this, model); 13174 alignment = determineAlignment(this, tooltipSize); 13175 // Final Size and Position
13176 backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart); 13177 } else { 13178 model.opacity = 0; 13179 } 13180 13181 model.xAlign = alignment.xAlign; 13182 model.yAlign = alignment.yAlign; 13183 model.x = backgroundPoint.x; 13184 model.y = backgroundPoint.y; 13185 model.width = tooltipSize.width; 13186 model.height = tooltipSize.height; 13187 13188 // Point where the caret on the tooltip points to 13189 model.caretX = tooltipPosition.x; 13190 model.caretY = tooltipPosition.y; 13191 13192 me._model = model; 13193 13194 if (changed && opts.custom) { 13195 opts.custom.call(me, model); 13196 } 13197 13198 return me; 13199 }, 13200 drawCaret: function(tooltipPoint, size) { 13201 var ctx = this._chart.ctx; 13202 var vm = this._view; 13203 var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); 13204 13205 ctx.lineTo(caretPosition.x1, caretPosition.y1); 13206 ctx.lineTo(caretPosition.x2, caretPosition.y2); 13207 ctx.lineTo(caretPosition.x3, caretPosition.y3); 13208 }, 13209 getCaretPosition: function(tooltipPoint, size, vm) { 13210 var x1, x2, x3, y1, y2, y3; 13211 var caretSize = vm.caretSize; 13212 var cornerRadius = vm.cornerRadius; 13213 var xAlign = vm.xAlign; 13214 var yAlign = vm.yAlign; 13215 var ptX = tooltipPoint.x; 13216 var ptY = tooltipPoint.y; 13217 var width = size.width; 13218 var height = size.height; 13219 13220 if (yAlign === 'center') { 13221 y2 = ptY + (height / 2); 13222 13223 if (xAlign === 'left') { 13224 x1 = ptX; 13225 x2 = x1 - caretSize; 13226 x3 = x1; 13227 13228 y1 = y2 + caretSize; 13229 y3 = y2 - caretSize; 13230 } else { 13231 x1 = ptX + width; 13232 x2 = x1 + caretSize; 13233 x3 = x1; 13234 13235 y1 = y2 - caretSize; 13236 y3 = y2 + caretSize; 13237 } 13238 } else { 13239 if (xAlign === 'left') { 13240 x2 = ptX + cornerRadius + (caretSize); 13241 x1 = x2 - caretSize; 13242 x3 = x2 + caretSize; 13243 } else if (xAlign === 'right') { 13244 x2 = ptX + width - cornerRadius - caretSize; 13245 x1 = x2 - caretSize; 13246 x3 = x2 + caretSize; 13247 } else { 13248 x2 = vm.caretX; 13249 x1 = x2 - caretSize; 13250 x3 = x2 + caretSize; 13251 } 13252 if (yAlign === 'top') { 13253 y1 = ptY; 13254 y2 = y1 - caretSize; 13255 y3 = y1; 13256 } else { 13257 y1 = ptY + height; 13258 y2 = y1 + caretSize; 13259 y3 = y1; 13260 // invert drawing order 13261 var tmp = x3; 13262 x3 = x1; 13263 x1 = tmp; 13264 } 13265 } 13266 return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; 13267 }, 13268 drawTitle: function(pt, vm, ctx, opacity) { 13269 var title = vm.title; 13270 13271 if (title.length) { 13272 ctx.textAlign = vm._titleAlign; 13273 ctx.textBaseline = 'top'; 13274 13275 var titleFontSize = vm.titleFontSize; 13276 var titleSpacing = vm.titleSpacing; 13277 13278 ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); 13279 ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); 13280 13281 var i, len; 13282 for (i = 0, len = title.length; i < len; ++i) { 13283 ctx.fillText(title[i], pt.x, pt.y); 13284 pt.y += titleFontSize + titleSpacing; // Line Height and spacing 13285 13286 if (i + 1 === title.length) { 13287 pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing 13288 } 13289 } 13290 } 13291 }, 13292 drawBody: function(pt, vm, ctx, opacity) { 13293 var bodyFontSize = vm.bodyFontSize; 13294 var bodySpacing = vm.bodySpacing; 13295 var body = vm.body; 13296 13297 ctx.textAlign = vm._bodyAlign; 13298 ctx.textBaseline = 'top'; 13299 ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); 13300 13301 // Before Body 13302 var xLinePadding = 0; 13303 var fillLineOfText = function(line) { 13304 ctx.fillText(line, pt.x + xLinePadding, pt.y); 13305 pt.y += bodyFontSize + bodySpacing; 13306 }; 13307 13308 // Before body lines 13309 ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity); 13310 helpers.each(vm.beforeBody, fillLineOfText); 13311 13312 var drawColorBoxes = vm.displayColors; 13313 xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; 13314 13315 // Draw body lines now 13316 helpers.each(body, function(bodyItem, i) { 13317 var textColor = mergeOpacity(vm.labelTextColors[i], opacity); 13318 ctx.fillStyle = textColor; 13319 helpers.each(bodyItem.before, fillLineOfText); 13320 13321 helpers.each(bodyItem.lines, function(line) { 13322 // Draw Legend-like boxes if needed 13323 if (drawColorBoxes) { 13324 // Fill a white rect so that colours merge nicely if the opacity is < 1
13325 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); 13326 ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); 13327 13328 // Border 13329 ctx.lineWidth = 1; 13330 ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); 13331 ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); 13332 13333 // Inner square 13334 ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); 13335 ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); 13336 ctx.fillStyle = textColor; 13337 } 13338 13339 fillLineOfText(line); 13340 }); 13341 13342 helpers.each(bodyItem.after, fillLineOfText); 13343 }); 13344 13345 // Reset back to 0 for after body 13346 xLinePadding = 0; 13347 13348 // After body lines 13349 helpers.each(vm.afterBody, fillLineOfText); 13350 pt.y -= bodySpacing; // Remove last body spacing 13351 }, 13352 drawFooter: function(pt, vm, ctx, opacity) { 13353 var footer = vm.footer; 13354 13355 if (footer.length) { 13356 pt.y += vm.footerMarginTop; 13357 13358 ctx.textAlign = vm._footerAlign; 13359 ctx.textBaseline = 'top'; 13360 13361 ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); 13362 ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); 13363 13364 helpers.each(footer, function(line) { 13365 ctx.fillText(line, pt.x, pt.y); 13366 pt.y += vm.footerFontSize + vm.footerSpacing; 13367 }); 13368 } 13369 }, 13370 drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { 13371 ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); 13372 ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); 13373 ctx.lineWidth = vm.borderWidth; 13374 var xAlign = vm.xAlign; 13375 var yAlign = vm.yAlign; 13376 var x = pt.x; 13377 var y = pt.y; 13378 var width = tooltipSize.width; 13379 var height = tooltipSize.height; 13380 var radius = vm.cornerRadius; 13381 13382 ctx.beginPath(); 13383 ctx.moveTo(x + radius, y); 13384 if (yAlign === 'top') { 13385 this.drawCaret(pt, tooltipSize); 13386 } 13387 ctx.lineTo(x + width - radius, y); 13388 ctx.quadraticCurveTo(x + width, y, x + width, y + radius); 13389 if (yAlign === 'center' && xAlign === 'right') { 13390 this.drawCaret(pt, tooltipSize); 13391 } 13392 ctx.lineTo(x + width, y + height - radius); 13393 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); 13394 if (yAlign === 'bottom') { 13395 this.drawCaret(pt, tooltipSize); 13396 } 13397 ctx.lineTo(x + radius, y + height); 13398 ctx.quadraticCurveTo(x, y + height, x, y + height - radius); 13399 if (yAlign === 'center' && xAlign === 'left') { 13400 this.drawCaret(pt, tooltipSize); 13401 } 13402 ctx.lineTo(x, y + radius); 13403 ctx.quadraticCurveTo(x, y, x + radius, y); 13404 ctx.closePath(); 13405 13406 ctx.fill(); 13407 13408 if (vm.borderWidth > 0) { 13409 ctx.stroke(); 13410 } 13411 }, 13412 draw: function() { 13413 var ctx = this._chart.ctx; 13414 var vm = this._view; 13415 13416 if (vm.opacity === 0) { 13417 return; 13418 } 13419 13420 var tooltipSize = { 13421 width: vm.width, 13422 height: vm.height 13423 }; 13424 var pt = { 13425 x: vm.x, 13426 y: vm.y 13427 }; 13428 13429 // IE11/Edge does not like very small opacities, so snap to 0 13430 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; 13431 13432 // Truthy/falsey value for empty tooltip 13433 var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; 13434 13435 if (this._options.enabled && hasTooltipContent) { 13436 // Draw Background 13437 this.drawBackground(pt, vm, ctx, tooltipSize, opacity); 13438 13439 // Draw Title, Body, and Footer 13440 pt.x += vm.xPadding; 13441 pt.y += vm.yPadding; 13442 13443 // Titles 13444 this.drawTitle(pt, vm, ctx, opacity); 13445 13446 // Body 13447 this.drawBody(pt, vm, ctx, opacity); 13448 13449 // Footer 13450 this.drawFooter(pt, vm, ctx, opacity); 13451 } 13452 }, 13453 13454 /** 13455 * Handle an event 13456 * @private 13457 * @param {IEvent} event - The event to handle 13458 * @returns {Boolean} true if the tooltip changed 13459 */ 13460 handleEvent: function(e) { 13461 var me = this; 13462 var options = me._options; 13463 var changed = false; 13464 13465 me._lastActive = me._lastActive || []; 13466 13467 // Find Active Elements for tooltips 13468 if (e.type === 'mouseout') { 13469 me._active = []; 13470 } else { 13471 me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); 13472 } 13473 13474 // Remember Last Actives 13475 changed = !helpers.arrayEquals(me._active, me._lastActive); 13476 13477 // Only handle target event on tooltip change 13478 if (changed) { 13479 me._lastActive = me._active; 13480 13481 if (options.enabled || options.custom) { 13482 me._eventPosition = { 13483 x: e.x, 13484 y: e.y 13485 }; 13486 13487 me.update(true); 13488 me.pivot(); 13489 } 13490 } 13491 13492 return changed; 13493 } 13494 }); 13495 13496 /** 13497 * @namespace Chart.Tooltip.positioners 13498 */ 13499 Chart.Tooltip.positioners = { 13500 /** 13501 * Average mode places the tooltip at the average position of the elements shown 13502 * @function Chart.Tooltip.positioners.average 13503 * @param elements {ChartElement[]} the elements being displayed in the tooltip 13504 * @returns {Point} tooltip position 13505 */ 13506 average: function(elements) { 13507 if (!elements.length) { 13508 return false;
13509 } 13510 13511 var i, len; 13512 var x = 0; 13513 var y = 0; 13514 var count = 0; 13515 13516 for (i = 0, len = elements.length; i < len; ++i) { 13517 var el = elements[i]; 13518 if (el && el.hasValue()) { 13519 var pos = el.tooltipPosition(); 13520 x += pos.x; 13521 y += pos.y; 13522 ++count; 13523 } 13524 } 13525 13526 return { 13527 x: Math.round(x / count), 13528 y: Math.round(y / count) 13529 }; 13530 }, 13531 13532 /** 13533 * Gets the tooltip position nearest of the item nearest to the event position 13534 * @function Chart.Tooltip.positioners.nearest 13535 * @param elements {Chart.Element[]} the tooltip elements 13536 * @param eventPosition {Point} the position of the event in canvas coordinates 13537 * @returns {Point} the tooltip position 13538 */ 13539 nearest: function(elements, eventPosition) { 13540 var x = eventPosition.x; 13541 var y = eventPosition.y; 13542 var minDistance = Number.POSITIVE_INFINITY; 13543 var i, len, nearestElement; 13544 13545 for (i = 0, len = elements.length; i < len; ++i) { 13546 var el = elements[i]; 13547 if (el && el.hasValue()) { 13548 var center = el.getCenterPoint(); 13549 var d = helpers.distanceBetweenPoints(eventPosition, center); 13550 13551 if (d < minDistance) { 13552 minDistance = d; 13553 nearestElement = el; 13554 } 13555 } 13556 } 13557 13558 if (nearestElement) { 13559 var tp = nearestElement.tooltipPosition(); 13560 x = tp.x; 13561 y = tp.y; 13562 } 13563 13564 return { 13565 x: x, 13566 y: y 13567 }; 13568 } 13569 }; 13570}; 13571 13572},{"25":25,"26":26,"45":45}],36:[function(require,module,exports){ 13573'use strict'; 13574 13575var defaults = require(25); 13576var Element = require(26); 13577var helpers = require(45); 13578 13579defaults._set('global', { 13580 elements: { 13581 arc: { 13582 backgroundColor: defaults.global.defaultColor, 13583 borderColor: '#fff', 13584 borderWidth: 2 13585 } 13586 } 13587}); 13588 13589module.exports = Element.extend({ 13590 inLabelRange: function(mouseX) { 13591 var vm = this._view; 13592 13593 if (vm) { 13594 return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); 13595 } 13596 return false; 13597 }, 13598 13599 inRange: function(chartX, chartY) { 13600 var vm = this._view; 13601 13602 if (vm) { 13603 var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); 13604 var angle = pointRelativePosition.angle; 13605 var distance = pointRelativePosition.distance; 13606 13607 // Sanitise angle range 13608 var startAngle = vm.startAngle; 13609 var endAngle = vm.endAngle; 13610 while (endAngle < startAngle) { 13611 endAngle += 2.0 * Math.PI; 13612 } 13613 while (angle > endAngle) { 13614 angle -= 2.0 * Math.PI; 13615 } 13616 while (angle < startAngle) { 13617 angle += 2.0 * Math.PI; 13618 } 13619 13620 // Check if within the range of the open/close angle 13621 var betweenAngles = (angle >= startAngle && angle <= endAngle); 13622 var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); 13623 13624 return (betweenAngles && withinRadius); 13625 } 13626 return false; 13627 }, 13628 13629 getCenterPoint: function() { 13630 var vm = this._view; 13631 var halfAngle = (vm.startAngle + vm.endAngle) / 2; 13632 var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; 13633 return { 13634 x: vm.x + Math.cos(halfAngle) * halfRadius, 13635 y: vm.y + Math.sin(halfAngle) * halfRadius 13636 }; 13637 }, 13638 13639 getArea: function() { 13640 var vm = this._view; 13641 return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); 13642 }, 13643 13644 tooltipPosition: function() { 13645 var vm = this._view; 13646 var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); 13647 var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; 13648 13649 return { 13650 x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), 13651 y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) 13652 }; 13653 }, 13654 13655 draw: function() { 13656 var ctx = this._chart.ctx; 13657 var vm = this._view; 13658 var sA = vm.startAngle; 13659 var eA = vm.endAngle; 13660 13661 ctx.beginPath(); 13662 13663 ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); 13664 ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); 13665 13666 ctx.closePath(); 13667 ctx.strokeStyle = vm.borderColor; 13668 ctx.lineWidth = vm.borderWidth; 13669 13670 ctx.fillStyle = vm.backgroundColor; 13671 13672 ctx.fill(); 13673 ctx.lineJoin = 'bevel'; 13674 13675 if (vm.borderWidth) { 13676 ctx.stroke(); 13677 } 13678 } 13679}); 13680 13681},{"25":25,"26":26,"45":45}],37:[function(require,module,exports){ 13682'use strict'; 13683 13684var defaults = require(25); 13685var Element = require(26); 13686var helpers = require(45); 13687 13688var globalDefaults = defaults.global; 13689 13690defaults._set('global', { 13691 elements: { 13692 line: { 13693 tension: 0.4, 13694 backgroundColor: globalDefaults.defaultColor, 13695 borderWidth: 3, 13696 borderColor: globalDefaults.defaultColor, 13697 borderCapStyle: 'butt', 13698 borderDash: [], 13699 borderDashOffset: 0.0, 13700 borderJoinStyle: 'miter', 13701 capBezierPoints: true, 13702 fill: true, // do we fill in the area between the line and its base axis 13703 } 13704 } 13705}); 13706 13707module.exports = Element.extend({ 13708 draw: function() { 13709 var me = this; 13710 var vm = me._view; 13711 var ctx = me._chart.ctx; 13712 var spanGaps = vm.spanGaps; 13713 var points = me._children.slice(); // clone array 13714 var globalOptionLineElements = globalDefaults.elements.line; 13715 var lastDrawnIndex = -1; 13716 var index, current, previous, currentVM; 13717 13718 // If we are looping, adding the first point again 13719 if (me._loop && points.length) { 13720 points.push(points[0]); 13721 } 13722 13723 ctx.save(); 13724 13725 // Stroke Line Options 13726 ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; 13727 13728 // IE 9 and 10 do not support line dash 13729 if (ctx.setLineDash) { 13730 ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); 13731 } 13732 13733 ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; 13734 ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; 13735 ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; 13736 ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; 13737 13738 // Stroke Line
13739 ctx.beginPath(); 13740 lastDrawnIndex = -1; 13741 13742 for (index = 0; index < points.length; ++index) { 13743 current = points[index]; 13744 previous = helpers.previousItem(points, index); 13745 currentVM = current._view; 13746 13747 // First point moves to it's starting position no matter what 13748 if (index === 0) { 13749 if (!currentVM.skip) { 13750 ctx.moveTo(currentVM.x, currentVM.y); 13751 lastDrawnIndex = index; 13752 } 13753 } else { 13754 previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; 13755 13756 if (!currentVM.skip) { 13757 if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { 13758 // There was a gap and this is the first point after the gap 13759 ctx.moveTo(currentVM.x, currentVM.y); 13760 } else { 13761 // Line to next point 13762 helpers.canvas.lineTo(ctx, previous._view, current._view); 13763 } 13764 lastDrawnIndex = index; 13765 } 13766 } 13767 } 13768 13769 ctx.stroke(); 13770 ctx.restore(); 13771 } 13772}); 13773 13774},{"25":25,"26":26,"45":45}],38:[function(require,module,exports){ 13775'use strict'; 13776 13777var defaults = require(25); 13778var Element = require(26); 13779var helpers = require(45); 13780 13781var defaultColor = defaults.global.defaultColor; 13782 13783defaults._set('global', { 13784 elements: { 13785 point: { 13786 radius: 3, 13787 pointStyle: 'circle', 13788 backgroundColor: defaultColor, 13789 borderColor: defaultColor, 13790 borderWidth: 1, 13791 // Hover 13792 hitRadius: 1, 13793 hoverRadius: 4, 13794 hoverBorderWidth: 1 13795 } 13796 } 13797}); 13798 13799function xRange(mouseX) { 13800 var vm = this._view; 13801 return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false; 13802} 13803 13804function yRange(mouseY) { 13805 var vm = this._view; 13806 return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false; 13807} 13808 13809module.exports = Element.extend({ 13810 inRange: function(mouseX, mouseY) { 13811 var vm = this._view; 13812 return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; 13813 }, 13814 13815 inLabelRange: xRange, 13816 inXRange: xRange, 13817 inYRange: yRange, 13818 13819 getCenterPoint: function() { 13820 var vm = this._view; 13821 return { 13822 x: vm.x, 13823 y: vm.y 13824 }; 13825 }, 13826 13827 getArea: function() { 13828 return Math.PI * Math.pow(this._view.radius, 2); 13829 }, 13830 13831 tooltipPosition: function() { 13832 var vm = this._view; 13833 return { 13834 x: vm.x, 13835 y: vm.y, 13836 padding: vm.radius + vm.borderWidth 13837 }; 13838 }, 13839 13840 draw: function(chartArea) { 13841 var vm = this._view; 13842 var model = this._model; 13843 var ctx = this._chart.ctx; 13844 var pointStyle = vm.pointStyle; 13845 var radius = vm.radius; 13846 var x = vm.x; 13847 var y = vm.y; 13848 var color = helpers.color; 13849 var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) 13850 var ratio = 0; 13851 13852 if (vm.skip) { 13853 return; 13854 } 13855 13856 ctx.strokeStyle = vm.borderColor || defaultColor; 13857 ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); 13858 ctx.fillStyle = vm.backgroundColor || defaultColor; 13859 13860 // Cliping for Points. 13861 // going out from inner charArea? 13862 if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) { 13863 // Point fade out 13864 if (model.x < chartArea.left) { 13865 ratio = (x - model.x) / (chartArea.left - model.x); 13866 } else if (chartArea.right * errMargin < model.x) { 13867 ratio = (model.x - x) / (model.x - chartArea.right); 13868 } else if (model.y < chartArea.top) { 13869 ratio = (y - model.y) / (chartArea.top - model.y); 13870 } else if (chartArea.bottom * errMargin < model.y) { 13871 ratio = (model.y - y) / (model.y - chartArea.bottom); 13872 } 13873 ratio = Math.round(ratio * 100) / 100; 13874 ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); 13875 ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); 13876 } 13877 13878 helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y); 13879 } 13880}); 13881 13882},{"25":25,"26":26,"45":45}],39:[function(require,module,exports){ 13883'use strict'; 13884 13885var defaults = require(25); 13886var Element = require(26); 13887 13888defaults._set('global', { 13889 elements: { 13890 rectangle: { 13891 backgroundColor: defaults.global.defaultColor, 13892 borderColor: defaults.global.defaultColor, 13893 borderSkipped: 'bottom', 13894 borderWidth: 0 13895 } 13896 } 13897}); 13898 13899function isVertical(bar) { 13900 return bar._view.width !== undefined; 13901} 13902 13903/** 13904 * Helper function to get the bounds of the bar regardless of the orientation 13905 * @param bar {Chart.Element.Rectangle} the bar 13906 * @return {Bounds} bounds of the bar 13907 * @private 13908 */ 13909function getBarBounds(bar) { 13910 var vm = bar._view; 13911 var x1, x2, y1, y2; 13912 13913 if (isVertical(bar)) { 13914 // vertical 13915 var halfWidth = vm.width / 2; 13916 x1 = vm.x - halfWidth; 13917 x2 = vm.x + halfWidth; 13918 y1 = Math.min(vm.y, vm.base); 13919 y2 = Math.max(vm.y, vm.base); 13920 } else { 13921 // horizontal bar 13922 var halfHeight = vm.height / 2; 13923 x1 = Math.min(vm.x, vm.base); 13924 x2 = Math.max(vm.x, vm.base); 13925 y1 = vm.y - halfHeight; 13926 y2 = vm.y + halfHeight; 13927 } 13928 13929 return { 13930 left: x1, 13931 top: y1, 13932 right: x2, 13933 bottom: y2 13934 }; 13935} 13936 13937module.exports = Element.extend({ 13938 draw: function() { 13939 var ctx = this._chart.ctx; 13940 var vm = this._view; 13941 var left, right, top, bottom, signX, signY, borderSkipped; 13942 var borderWidth = vm.borderWidth; 13943 13944 if (!vm.horizontal) { 13945 // bar 13946 left = vm.x - vm.width / 2; 13947 right = vm.x + vm.width / 2; 13948 top = vm.y; 13949 bottom = vm.base; 13950 signX = 1; 13951 signY = bottom > top ? 1 : -1; 13952 borderSkipped = vm.borderSkipped || 'bottom'; 13953 } else { 13954 // horizontal bar 13955 left = vm.base; 13956 right = vm.x; 13957 top = vm.y - vm.height / 2; 13958 bottom = vm.y + vm.height / 2; 13959 signX = right > left ? 1 : -1; 13960 signY = 1; 13961 borderSkipped = vm.borderSkipped || 'left'; 13962 } 13963 13964 // Canvas doesn't allow us to stroke inside the width so we can 13965 // adjust the sizes to fit if we're setting a stroke on the line 13966 if (borderWidth) { 13967 // borderWidth shold be less than bar width and bar height. 13968 var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); 13969 borderWidth = borderWidth > barSize ? barSize : borderWidth; 13970 var halfStroke = borderWidth / 2; 13971 // Adjust borderWidth when bar top position is near vm.base(zero). 13972 var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); 13973 var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); 13974 var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); 13975 var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); 13976 // not become a vertical line? 13977 if (borderLeft !== borderRight) { 13978 top = borderTop; 13979 bottom = borderBottom; 13980 } 13981 // not become a horizontal line? 13982 if (borderTop !== borderBottom) { 13983 left = borderLeft; 13984 right = borderRight; 13985 } 13986 } 13987 13988 ctx.beginPath(); 13989 ctx.fillStyle = vm.backgroundColor; 13990 ctx.strokeStyle = vm.borderColor; 13991 ctx.lineWidth = borderWidth; 13992 13993 // Corner points, from bottom-left to bottom-right clockwise 13994 // | 1 2 | 13995 // | 0 3 | 13996 var corners = [ 13997 [left, bottom], 13998 [left, top], 13999 [right, top], 14000 [right, bottom] 14001 ]; 14002 14003 // Find first (starting) corner with fallback to 'bottom' 14004 var borders = ['bottom', 'left', 'top', 'right']; 14005 var startCorner = borders.indexOf(borderSkipped, 0); 14006 if (startCorner === -1) { 14007 startCorner = 0; 14008 } 14009 14010 function cornerAt(index) { 14011 return corners[(startCorner + index) % 4]; 14012 } 14013 14014 // Draw rectangle from 'startCorner' 14015 var corner = cornerAt(0); 14016 ctx.moveTo(corner[0], corner[1]); 14017 14018 for (var i = 1; i < 4; i++) { 14019 corner = cornerAt(i); 14020 ctx.lineTo(corner[0], corner[1]); 14021 } 14022 14023 ctx.fill(); 14024 if (borderWidth) { 14025 ctx.stroke(); 14026 } 14027 }, 14028 14029 height: function() { 14030 var vm = this._view; 14031 return vm.base - vm.y; 14032 }, 14033 14034 inRange: function(mouseX, mouseY) { 14035 var inRange = false;
14036 14037 if (this._view) { 14038 var bounds = getBarBounds(this); 14039 inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; 14040 } 14041 14042 return inRange; 14043 }, 14044 14045 inLabelRange: function(mouseX, mouseY) { 14046 var me = this; 14047 if (!me._view) { 14048 return false; 14049 } 14050 14051 var inRange = false; 14052 var bounds = getBarBounds(me); 14053 14054 if (isVertical(me)) { 14055 inRange = mouseX >= bounds.left && mouseX <= bounds.right; 14056 } else { 14057 inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; 14058 } 14059 14060 return inRange; 14061 }, 14062 14063 inXRange: function(mouseX) { 14064 var bounds = getBarBounds(this); 14065 return mouseX >= bounds.left && mouseX <= bounds.right; 14066 }, 14067 14068 inYRange: function(mouseY) { 14069 var bounds = getBarBounds(this); 14070 return mouseY >= bounds.top && mouseY <= bounds.bottom; 14071 }, 14072 14073 getCenterPoint: function() { 14074 var vm = this._view; 14075 var x, y; 14076 if (isVertical(this)) { 14077 x = vm.x; 14078 y = (vm.y + vm.base) / 2; 14079 } else { 14080 x = (vm.x + vm.base) / 2; 14081 y = vm.y; 14082 } 14083 14084 return {x: x, y: y}; 14085 }, 14086 14087 getArea: function() { 14088 var vm = this._view; 14089 return vm.width * Math.abs(vm.y - vm.base); 14090 }, 14091 14092 tooltipPosition: function() { 14093 var vm = this._view; 14094 return { 14095 x: vm.x, 14096 y: vm.y 14097 }; 14098 } 14099}); 14100 14101},{"25":25,"26":26}],40:[function(require,module,exports){ 14102'use strict'; 14103 14104module.exports = {}; 14105module.exports.Arc = require(36); 14106module.exports.Line = require(37); 14107module.exports.Point = require(38); 14108module.exports.Rectangle = require(39); 14109 14110},{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){ 14111'use strict'; 14112 14113var helpers = require(42); 14114 14115/** 14116 * @namespace Chart.helpers.canvas 14117 */ 14118var exports = module.exports = { 14119 /** 14120 * Clears the entire canvas associated to the given `chart`. 14121 * @param {Chart} chart - The chart for which to clear the canvas. 14122 */ 14123 clear: function(chart) { 14124 chart.ctx.clearRect(0, 0, chart.width, chart.height); 14125 }, 14126 14127 /** 14128 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a 14129 * given size (width, height) and the same `radius` for all corners. 14130 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. 14131 * @param {Number} x - The x axis of the coordinate for the rectangle starting point. 14132 * @param {Number} y - The y axis of the coordinate for the rectangle starting point. 14133 * @param {Number} width - The rectangle's width. 14134 * @param {Number} height - The rectangle's height. 14135 * @param {Number} radius - The rounded amount (in pixels) for the four corners. 14136 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? 14137 */ 14138 roundedRect: function(ctx, x, y, width, height, radius) { 14139 if (radius) { 14140 var rx = Math.min(radius, width / 2); 14141 var ry = Math.min(radius, height / 2); 14142 14143 ctx.moveTo(x + rx, y); 14144 ctx.lineTo(x + width - rx, y); 14145 ctx.quadraticCurveTo(x + width, y, x + width, y + ry); 14146 ctx.lineTo(x + width, y + height - ry); 14147 ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); 14148 ctx.lineTo(x + rx, y + height); 14149 ctx.quadraticCurveTo(x, y + height, x, y + height - ry); 14150 ctx.lineTo(x, y + ry); 14151 ctx.quadraticCurveTo(x, y, x + rx, y); 14152 } else { 14153 ctx.rect(x, y, width, height); 14154 } 14155 }, 14156 14157 drawPoint: function(ctx, style, radius, x, y) { 14158 var type, edgeLength, xOffset, yOffset, height, size; 14159 14160 if (style && typeof style === 'object') { 14161 type = style.toString(); 14162 if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { 14163 ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); 14164 return; 14165 } 14166 } 14167 14168 if (isNaN(radius) || radius <= 0) { 14169 return; 14170 } 14171 14172 switch (style) { 14173 // Default includes circle 14174 default: 14175 ctx.beginPath(); 14176 ctx.arc(x, y, radius, 0, Math.PI * 2); 14177 ctx.closePath(); 14178 ctx.fill(); 14179 break; 14180 case 'triangle': 14181 ctx.beginPath(); 14182 edgeLength = 3 * radius / Math.sqrt(3); 14183 height = edgeLength * Math.sqrt(3) / 2; 14184 ctx.moveTo(x - edgeLength / 2, y + height / 3); 14185 ctx.lineTo(x + edgeLength / 2, y + height / 3); 14186 ctx.lineTo(x, y - 2 * height / 3); 14187 ctx.closePath(); 14188 ctx.fill(); 14189 break; 14190 case 'rect': 14191 size = 1 / Math.SQRT2 * radius; 14192 ctx.beginPath(); 14193 ctx.fillRect(x - size, y - size, 2 * size, 2 * size); 14194 ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); 14195 break; 14196 case 'rectRounded': 14197 var offset = radius / Math.SQRT2; 14198 var leftX = x - offset; 14199 var topY = y - offset; 14200 var sideSize = Math.SQRT2 * radius; 14201 ctx.beginPath(); 14202 this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2); 14203 ctx.closePath(); 14204 ctx.fill(); 14205 break; 14206 case 'rectRot': 14207 size = 1 / Math.SQRT2 * radius; 14208 ctx.beginPath(); 14209 ctx.moveTo(x - size, y); 14210 ctx.lineTo(x, y + size); 14211 ctx.lineTo(x + size, y); 14212 ctx.lineTo(x, y - size); 14213 ctx.closePath(); 14214 ctx.fill(); 14215 break; 14216 case 'cross': 14217 ctx.beginPath(); 14218 ctx.moveTo(x, y + radius);
14219 ctx.lineTo(x, y - radius); 14220 ctx.moveTo(x - radius, y); 14221 ctx.lineTo(x + radius, y); 14222 ctx.closePath(); 14223 break; 14224 case 'crossRot': 14225 ctx.beginPath(); 14226 xOffset = Math.cos(Math.PI / 4) * radius; 14227 yOffset = Math.sin(Math.PI / 4) * radius; 14228 ctx.moveTo(x - xOffset, y - yOffset); 14229 ctx.lineTo(x + xOffset, y + yOffset); 14230 ctx.moveTo(x - xOffset, y + yOffset); 14231 ctx.lineTo(x + xOffset, y - yOffset); 14232 ctx.closePath(); 14233 break; 14234 case 'star': 14235 ctx.beginPath(); 14236 ctx.moveTo(x, y + radius); 14237 ctx.lineTo(x, y - radius); 14238 ctx.moveTo(x - radius, y); 14239 ctx.lineTo(x + radius, y); 14240 xOffset = Math.cos(Math.PI / 4) * radius; 14241 yOffset = Math.sin(Math.PI / 4) * radius; 14242 ctx.moveTo(x - xOffset, y - yOffset); 14243 ctx.lineTo(x + xOffset, y + yOffset); 14244 ctx.moveTo(x - xOffset, y + yOffset); 14245 ctx.lineTo(x + xOffset, y - yOffset); 14246 ctx.closePath(); 14247 break; 14248 case 'line': 14249 ctx.beginPath(); 14250 ctx.moveTo(x - radius, y); 14251 ctx.lineTo(x + radius, y); 14252 ctx.closePath(); 14253 break; 14254 case 'dash': 14255 ctx.beginPath(); 14256 ctx.moveTo(x, y); 14257 ctx.lineTo(x + radius, y); 14258 ctx.closePath(); 14259 break; 14260 } 14261 14262 ctx.stroke(); 14263 }, 14264 14265 clipArea: function(ctx, area) { 14266 ctx.save(); 14267 ctx.beginPath(); 14268 ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); 14269 ctx.clip(); 14270 }, 14271 14272 unclipArea: function(ctx) { 14273 ctx.restore(); 14274 }, 14275 14276 lineTo: function(ctx, previous, target, flip) { 14277 if (target.steppedLine) { 14278 if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { 14279 ctx.lineTo(previous.x, target.y); 14280 } else { 14281 ctx.lineTo(target.x, previous.y); 14282 } 14283 ctx.lineTo(target.x, target.y); 14284 return; 14285 } 14286 14287 if (!target.tension) { 14288 ctx.lineTo(target.x, target.y); 14289 return; 14290 } 14291 14292 ctx.bezierCurveTo( 14293 flip ? previous.controlPointPreviousX : previous.controlPointNextX, 14294 flip ? previous.controlPointPreviousY : previous.controlPointNextY, 14295 flip ? target.controlPointNextX : target.controlPointPreviousX, 14296 flip ? target.controlPointNextY : target.controlPointPreviousY, 14297 target.x, 14298 target.y); 14299 } 14300}; 14301 14302// DEPRECATIONS 14303 14304/** 14305 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. 14306 * @namespace Chart.helpers.clear 14307 * @deprecated since version 2.7.0 14308 * @todo remove at version 3 14309 * @private 14310 */ 14311helpers.clear = exports.clear; 14312 14313/** 14314 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. 14315 * @namespace Chart.helpers.drawRoundedRectangle 14316 * @deprecated since version 2.7.0 14317 * @todo remove at version 3 14318 * @private 14319 */ 14320helpers.drawRoundedRectangle = function(ctx) { 14321 ctx.beginPath(); 14322 exports.roundedRect.apply(exports, arguments); 14323 ctx.closePath(); 14324}; 14325 14326},{"42":42}],42:[function(require,module,exports){ 14327'use strict'; 14328 14329/** 14330 * @namespace Chart.helpers 14331 */ 14332var helpers = { 14333 /** 14334 * An empty function that can be used, for example, for optional callback. 14335 */ 14336 noop: function() {}, 14337 14338 /** 14339 * Returns a unique id, sequentially generated from a global variable. 14340 * @returns {Number} 14341 * @function 14342 */ 14343 uid: (function() { 14344 var id = 0; 14345 return function() { 14346 return id++; 14347 }; 14348 }()), 14349 14350 /** 14351 * Returns true if `value` is neither null nor undefined, else returns false. 14352 * @param {*} value - The value to test. 14353 * @returns {Boolean} 14354 * @since 2.7.0 14355 */ 14356 isNullOrUndef: function(value) { 14357 return value === null || typeof value === 'undefined'; 14358 }, 14359 14360 /** 14361 * Returns true if `value` is an array, else returns false. 14362 * @param {*} value - The value to test. 14363 * @returns {Boolean} 14364 * @function 14365 */ 14366 isArray: Array.isArray ? Array.isArray : function(value) { 14367 return Object.prototype.toString.call(value) === '[object Array]'; 14368 }, 14369 14370 /** 14371 * Returns true if `value` is an object (excluding null), else returns false. 14372 * @param {*} value - The value to test. 14373 * @returns {Boolean} 14374 * @since 2.7.0 14375 */ 14376 isObject: function(value) { 14377 return value !== null && Object.prototype.toString.call(value) === '[object Object]'; 14378 }, 14379 14380 /** 14381 * Returns `value` if defined, else returns `defaultValue`. 14382 * @param {*} value - The value to return if defined. 14383 * @param {*} defaultValue - The value to return if `value` is undefined. 14384 * @returns {*} 14385 */ 14386 valueOrDefault: function(value, defaultValue) { 14387 return typeof value === 'undefined' ? defaultValue : value; 14388 }, 14389 14390 /** 14391 * Returns value at the given `index` in array if defined, else returns `defaultValue`. 14392 * @param {Array} value - The array to lookup for value at `index`. 14393 * @param {Number} index - The index in `value` to lookup for value. 14394 * @param {*} defaultValue - The value to return if `value[index]` is undefined. 14395 * @returns {*} 14396 */ 14397 valueAtIndexOrDefault: function(value, index, defaultValue) { 14398 return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); 14399 }, 14400 14401 /** 14402 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the 14403 * value returned by `fn`. If `fn` is not a function, this method returns undefined. 14404 * @param {Function} fn - The function to call. 14405 * @param {Array|undefined|null} args - The arguments with which `fn` should be called. 14406 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. 14407 * @returns {*} 14408 */ 14409 callback: function(fn, args, thisArg) { 14410 if (fn && typeof fn.call === 'function') { 14411 return fn.apply(thisArg, args); 14412 } 14413 }, 14414 14415 /** 14416 * Note(SB) for performance sake, this method should only be used when loopable type 14417 * is unknown or in none intensive code (not called often and small loopable). Else 14418 * it's preferable to use a regular for() loop and save extra function calls. 14419 * @param {Object|Array} loopable - The object or array to be iterated. 14420 * @param {Function} fn - The function to call for each item. 14421 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. 14422 * @param {Boolean} [reverse] - If true, iterates backward on the loopable. 14423 */ 14424 each: function(loopable, fn, thisArg, reverse) { 14425 var i, len, keys; 14426 if (helpers.isArray(loopable)) { 14427 len = loopable.length; 14428 if (reverse) { 14429 for (i = len - 1; i >= 0; i--) { 14430 fn.call(thisArg, loopable[i], i); 14431 } 14432 } else { 14433 for (i = 0; i < len; i++) { 14434 fn.call(thisArg, loopable[i], i); 14435 } 14436 } 14437 } else if (helpers.isObject(loopable)) { 14438 keys = Object.keys(loopable); 14439 len = keys.length; 14440 for (i = 0; i < len; i++) { 14441 fn.call(thisArg, loopable[keys[i]], keys[i]); 14442 } 14443 } 14444 }, 14445 14446 /** 14447 * Returns true if the `a0` and `a1` arrays have the same content, else returns false. 14448 * @see http://stackoverflow.com/a/14853974 14449 * @param {Array} a0 - The array to compare 14450 * @param {Array} a1 - The array to compare 14451 * @returns {Boolean} 14452 */ 14453 arrayEquals: function(a0, a1) { 14454 var i, ilen, v0, v1; 14455 14456 if (!a0 || !a1 || a0.length !== a1.length) { 14457 return false;
vendor: 10,367 bytes, lines 14458-14911
14458 } 14459 14460 for (i = 0, ilen = a0.length; i < ilen; ++i) { 14461 v0 = a0[i]; 14462 v1 = a1[i]; 14463 14464 if (v0 instanceof Array && v1 instanceof Array) { 14465 if (!helpers.arrayEquals(v0, v1)) { 14466 return false; 14467 } 14468 } else if (v0 !== v1) { 14469 // NOTE: two different object instances will never be equal: {x:20} != {x:20} 14470 return false; 14471 } 14472 } 14473 14474 return true; 14475 }, 14476 14477 /** 14478 * Returns a deep copy of `source` without keeping references on objects and arrays. 14479 * @param {*} source - The value to clone. 14480 * @returns {*} 14481 */ 14482 clone: function(source) { 14483 if (helpers.isArray(source)) { 14484 return source.map(helpers.clone); 14485 } 14486 14487 if (helpers.isObject(source)) { 14488 var target = {}; 14489 var keys = Object.keys(source); 14490 var klen = keys.length; 14491 var k = 0; 14492 14493 for (; k < klen; ++k) { 14494 target[keys[k]] = helpers.clone(source[keys[k]]); 14495 } 14496 14497 return target; 14498 } 14499 14500 return source; 14501 }, 14502 14503 /** 14504 * The default merger when Chart.helpers.merge is called without merger option. 14505 * Note(SB): this method is also used by configMerge and scaleMerge as fallback. 14506 * @private 14507 */ 14508 _merger: function(key, target, source, options) { 14509 var tval = target[key]; 14510 var sval = source[key]; 14511 14512 if (helpers.isObject(tval) && helpers.isObject(sval)) { 14513 helpers.merge(tval, sval, options); 14514 } else { 14515 target[key] = helpers.clone(sval); 14516 } 14517 }, 14518 14519 /** 14520 * Merges source[key] in target[key] only if target[key] is undefined. 14521 * @private 14522 */ 14523 _mergerIf: function(key, target, source) { 14524 var tval = target[key]; 14525 var sval = source[key]; 14526 14527 if (helpers.isObject(tval) && helpers.isObject(sval)) { 14528 helpers.mergeIf(tval, sval); 14529 } else if (!target.hasOwnProperty(key)) { 14530 target[key] = helpers.clone(sval); 14531 } 14532 }, 14533 14534 /** 14535 * Recursively deep copies `source` properties into `target` with the given `options`. 14536 * IMPORTANT: `target` is not cloned and will be updated with `source` properties. 14537 * @param {Object} target - The target object in which all sources are merged into. 14538 * @param {Object|Array(Object)} source - Object(s) to merge into `target`. 14539 * @param {Object} [options] - Merging options: 14540 * @param {Function} [options.merger] - The merge method (key, target, source, options) 14541 * @returns {Object} The `target` object. 14542 */ 14543 merge: function(target, source, options) { 14544 var sources = helpers.isArray(source) ? source : [source]; 14545 var ilen = sources.length; 14546 var merge, i, keys, klen, k; 14547 14548 if (!helpers.isObject(target)) { 14549 return target; 14550 } 14551 14552 options = options || {}; 14553 merge = options.merger || helpers._merger; 14554 14555 for (i = 0; i < ilen; ++i) { 14556 source = sources[i]; 14557 if (!helpers.isObject(source)) { 14558 continue; 14559 } 14560 14561 keys = Object.keys(source); 14562 for (k = 0, klen = keys.length; k < klen; ++k) { 14563 merge(keys[k], target, source, options); 14564 } 14565 } 14566 14567 return target; 14568 }, 14569 14570 /** 14571 * Recursively deep copies `source` properties into `target` *only* if not defined in target. 14572 * IMPORTANT: `target` is not cloned and will be updated with `source` properties. 14573 * @param {Object} target - The target object in which all sources are merged into. 14574 * @param {Object|Array(Object)} source - Object(s) to merge into `target`. 14575 * @returns {Object} The `target` object. 14576 */ 14577 mergeIf: function(target, source) { 14578 return helpers.merge(target, source, {merger: helpers._mergerIf}); 14579 }, 14580 14581 /** 14582 * Applies the contents of two or more objects together into the first object. 14583 * @param {Object} target - The target object in which all objects are merged into. 14584 * @param {Object} arg1 - Object containing additional properties to merge in target. 14585 * @param {Object} argN - Additional objects containing properties to merge in target. 14586 * @returns {Object} The `target` object. 14587 */ 14588 extend: function(target) { 14589 var setFn = function(value, key) { 14590 target[key] = value; 14591 }; 14592 for (var i = 1, ilen = arguments.length; i < ilen; ++i) { 14593 helpers.each(arguments[i], setFn); 14594 } 14595 return target; 14596 }, 14597 14598 /** 14599 * Basic javascript inheritance based on the model created in Backbone.js 14600 */ 14601 inherits: function(extensions) { 14602 var me = this; 14603 var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { 14604 return me.apply(this, arguments); 14605 }; 14606 14607 var Surrogate = function() { 14608 this.constructor = ChartElement; 14609 }; 14610 14611 Surrogate.prototype = me.prototype; 14612 ChartElement.prototype = new Surrogate(); 14613 ChartElement.extend = helpers.inherits; 14614 14615 if (extensions) { 14616 helpers.extend(ChartElement.prototype, extensions); 14617 } 14618 14619 ChartElement.__super__ = me.prototype; 14620 return ChartElement; 14621 } 14622}; 14623 14624module.exports = helpers; 14625 14626// DEPRECATIONS 14627 14628/** 14629 * Provided for backward compatibility, use Chart.helpers.callback instead. 14630 * @function Chart.helpers.callCallback 14631 * @deprecated since version 2.6.0 14632 * @todo remove at version 3 14633 * @private 14634 */ 14635helpers.callCallback = helpers.callback; 14636 14637/** 14638 * Provided for backward compatibility, use Array.prototype.indexOf instead. 14639 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ 14640 * @function Chart.helpers.indexOf 14641 * @deprecated since version 2.7.0 14642 * @todo remove at version 3 14643 * @private 14644 */ 14645helpers.indexOf = function(array, item, fromIndex) { 14646 return Array.prototype.indexOf.call(array, item, fromIndex); 14647}; 14648 14649/** 14650 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. 14651 * @function Chart.helpers.getValueOrDefault 14652 * @deprecated since version 2.7.0 14653 * @todo remove at version 3 14654 * @private 14655 */ 14656helpers.getValueOrDefault = helpers.valueOrDefault; 14657 14658/** 14659 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. 14660 * @function Chart.helpers.getValueAtIndexOrDefault 14661 * @deprecated since version 2.7.0 14662 * @todo remove at version 3 14663 * @private 14664 */ 14665helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; 14666 14667},{}],43:[function(require,module,exports){ 14668'use strict'; 14669 14670var helpers = require(42); 14671 14672/** 14673 * Easing functions adapted from Robert Penner's easing equations. 14674 * @namespace Chart.helpers.easingEffects 14675 * @see http://www.robertpenner.com/easing/ 14676 */ 14677var effects = { 14678 linear: function(t) { 14679 return t; 14680 }, 14681 14682 easeInQuad: function(t) { 14683 return t * t; 14684 }, 14685 14686 easeOutQuad: function(t) { 14687 return -t * (t - 2); 14688 }, 14689 14690 easeInOutQuad: function(t) { 14691 if ((t /= 0.5) < 1) { 14692 return 0.5 * t * t; 14693 } 14694 return -0.5 * ((--t) * (t - 2) - 1); 14695 }, 14696 14697 easeInCubic: function(t) { 14698 return t * t * t; 14699 }, 14700 14701 easeOutCubic: function(t) { 14702 return (t = t - 1) * t * t + 1; 14703 }, 14704 14705 easeInOutCubic: function(t) { 14706 if ((t /= 0.5) < 1) { 14707 return 0.5 * t * t * t; 14708 } 14709 return 0.5 * ((t -= 2) * t * t + 2); 14710 }, 14711 14712 easeInQuart: function(t) { 14713 return t * t * t * t; 14714 }, 14715 14716 easeOutQuart: function(t) { 14717 return -((t = t - 1) * t * t * t - 1); 14718 }, 14719 14720 easeInOutQuart: function(t) { 14721 if ((t /= 0.5) < 1) { 14722 return 0.5 * t * t * t * t; 14723 } 14724 return -0.5 * ((t -= 2) * t * t * t - 2); 14725 }, 14726 14727 easeInQuint: function(t) { 14728 return t * t * t * t * t; 14729 }, 14730 14731 easeOutQuint: function(t) { 14732 return (t = t - 1) * t * t * t * t + 1; 14733 }, 14734 14735 easeInOutQuint: function(t) { 14736 if ((t /= 0.5) < 1) { 14737 return 0.5 * t * t * t * t * t; 14738 } 14739 return 0.5 * ((t -= 2) * t * t * t * t + 2); 14740 }, 14741 14742 easeInSine: function(t) { 14743 return -Math.cos(t * (Math.PI / 2)) + 1; 14744 }, 14745 14746 easeOutSine: function(t) { 14747 return Math.sin(t * (Math.PI / 2)); 14748 }, 14749 14750 easeInOutSine: function(t) { 14751 return -0.5 * (Math.cos(Math.PI * t) - 1); 14752 }, 14753 14754 easeInExpo: function(t) { 14755 return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); 14756 }, 14757 14758 easeOutExpo: function(t) { 14759 return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; 14760 }, 14761 14762 easeInOutExpo: function(t) { 14763 if (t === 0) { 14764 return 0; 14765 } 14766 if (t === 1) { 14767 return 1; 14768 } 14769 if ((t /= 0.5) < 1) { 14770 return 0.5 * Math.pow(2, 10 * (t - 1)); 14771 } 14772 return 0.5 * (-Math.pow(2, -10 * --t) + 2); 14773 }, 14774 14775 easeInCirc: function(t) { 14776 if (t >= 1) { 14777 return t; 14778 } 14779 return -(Math.sqrt(1 - t * t) - 1); 14780 }, 14781 14782 easeOutCirc: function(t) { 14783 return Math.sqrt(1 - (t = t - 1) * t); 14784 }, 14785 14786 easeInOutCirc: function(t) { 14787 if ((t /= 0.5) < 1) { 14788 return -0.5 * (Math.sqrt(1 - t * t) - 1); 14789 } 14790 return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); 14791 }, 14792 14793 easeInElastic: function(t) { 14794 var s = 1.70158; 14795 var p = 0; 14796 var a = 1; 14797 if (t === 0) { 14798 return 0; 14799 } 14800 if (t === 1) { 14801 return 1; 14802 } 14803 if (!p) { 14804 p = 0.3; 14805 } 14806 if (a < 1) { 14807 a = 1; 14808 s = p / 4; 14809 } else { 14810 s = p / (2 * Math.PI) * Math.asin(1 / a); 14811 } 14812 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); 14813 }, 14814 14815 easeOutElastic: function(t) { 14816 var s = 1.70158; 14817 var p = 0; 14818 var a = 1; 14819 if (t === 0) { 14820 return 0; 14821 } 14822 if (t === 1) { 14823 return 1; 14824 } 14825 if (!p) { 14826 p = 0.3; 14827 } 14828 if (a < 1) { 14829 a = 1; 14830 s = p / 4; 14831 } else { 14832 s = p / (2 * Math.PI) * Math.asin(1 / a); 14833 } 14834 return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; 14835 }, 14836 14837 easeInOutElastic: function(t) { 14838 var s = 1.70158; 14839 var p = 0; 14840 var a = 1; 14841 if (t === 0) { 14842 return 0; 14843 } 14844 if ((t /= 0.5) === 2) { 14845 return 1; 14846 } 14847 if (!p) { 14848 p = 0.45; 14849 } 14850 if (a < 1) { 14851 a = 1; 14852 s = p / 4; 14853 } else { 14854 s = p / (2 * Math.PI) * Math.asin(1 / a); 14855 } 14856 if (t < 1) { 14857 return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); 14858 } 14859 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; 14860 }, 14861 easeInBack: function(t) { 14862 var s = 1.70158; 14863 return t * t * ((s + 1) * t - s); 14864 }, 14865 14866 easeOutBack: function(t) { 14867 var s = 1.70158; 14868 return (t = t - 1) * t * ((s + 1) * t + s) + 1; 14869 }, 14870 14871 easeInOutBack: function(t) { 14872 var s = 1.70158; 14873 if ((t /= 0.5) < 1) { 14874 return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); 14875 } 14876 return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); 14877 }, 14878 14879 easeInBounce: function(t) { 14880 return 1 - effects.easeOutBounce(1 - t); 14881 }, 14882 14883 easeOutBounce: function(t) { 14884 if (t < (1 / 2.75)) { 14885 return 7.5625 * t * t; 14886 } 14887 if (t < (2 / 2.75)) { 14888 return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; 14889 } 14890 if (t < (2.5 / 2.75)) { 14891 return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; 14892 } 14893 return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; 14894 }, 14895 14896 easeInOutBounce: function(t) { 14897 if (t < 0.5) { 14898 return effects.easeInBounce(t * 2) * 0.5; 14899 } 14900 return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; 14901 } 14902}; 14903 14904module.exports = { 14905 effects: effects 14906}; 14907 14908// DEPRECATIONS 14909 14910/** 14911 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
14912 * @function Chart.helpers.easingEffects 14913 * @deprecated since version 2.7.0 14914 * @todo remove at version 3 14915 * @private 14916 */ 14917helpers.easingEffects = effects; 14918 14919},{"42":42}],44:[function(require,module,exports){ 14920'use strict'; 14921 14922var helpers = require(42); 14923 14924/** 14925 * @alias Chart.helpers.options 14926 * @namespace 14927 */ 14928module.exports = { 14929 /** 14930 * Converts the given line height `value` in pixels for a specific font `size`. 14931 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). 14932 * @param {Number} size - The font size (in pixels) used to resolve relative `value`. 14933 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). 14934 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height 14935 * @since 2.7.0 14936 */ 14937 toLineHeight: function(value, size) { 14938 var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); 14939 if (!matches || matches[1] === 'normal') { 14940 return size * 1.2; 14941 } 14942 14943 value = +matches[2]; 14944 14945 switch (matches[3]) { 14946 case 'px': 14947 return value; 14948 case '%': 14949 value /= 100; 14950 break; 14951 default: 14952 break; 14953 } 14954 14955 return size * value; 14956 }, 14957 14958 /** 14959 * Converts the given value into a padding object with pre-computed width/height. 14960 * @param {Number|Object} value - If a number, set the value to all TRBL component, 14961 * else, if and object, use defined properties and sets undefined ones to 0. 14962 * @returns {Object} The padding values (top, right, bottom, left, width, height) 14963 * @since 2.7.0 14964 */ 14965 toPadding: function(value) { 14966 var t, r, b, l; 14967 14968 if (helpers.isObject(value)) { 14969 t = +value.top || 0; 14970 r = +value.right || 0; 14971 b = +value.bottom || 0; 14972 l = +value.left || 0; 14973 } else { 14974 t = r = b = l = +value || 0; 14975 } 14976 14977 return { 14978 top: t, 14979 right: r, 14980 bottom: b, 14981 left: l, 14982 height: t + b, 14983 width: l + r 14984 }; 14985 }, 14986 14987 /** 14988 * Evaluates the given `inputs` sequentially and returns the first defined value. 14989 * @param {Array[]} inputs - An array of values, falling back to the last value. 14990 * @param {Object} [context] - If defined and the current value is a function, the value 14991 * is called with `context` as first argument and the result becomes the new input. 14992 * @param {Number} [index] - If defined and the current value is an array, the value 14993 * at `index` become the new input. 14994 * @since 2.7.0 14995 */ 14996 resolve: function(inputs, context, index) { 14997 var i, ilen, value; 14998 14999 for (i = 0, ilen = inputs.length; i < ilen; ++i) { 15000 value = inputs[i]; 15001 if (value === undefined) { 15002 continue; 15003 } 15004 if (context !== undefined && typeof value === 'function') { 15005 value = value(context); 15006 } 15007 if (index !== undefined && helpers.isArray(value)) { 15008 value = value[index]; 15009 } 15010 if (value !== undefined) { 15011 return value; 15012 } 15013 } 15014 } 15015}; 15016 15017},{"42":42}],45:[function(require,module,exports){ 15018'use strict'; 15019 15020module.exports = require(42); 15021module.exports.easing = require(43); 15022module.exports.canvas = require(41); 15023module.exports.options = require(44); 15024 15025},{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){ 15026/** 15027 * Platform fallback implementation (minimal). 15028 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 15029 */ 15030 15031module.exports = { 15032 acquireContext: function(item) { 15033 if (item && item.canvas) { 15034 // Support for any object associated to a canvas (including a context2d) 15035 item = item.canvas; 15036 } 15037 15038 return item && item.getContext('2d') || null; 15039 } 15040}; 15041 15042},{}],47:[function(require,module,exports){ 15043/** 15044 * Chart.Platform implementation for targeting a web browser 15045 */ 15046 15047'use strict'; 15048 15049var helpers = require(45); 15050 15051var EXPANDO_KEY = '$chartjs'; 15052var CSS_PREFIX = 'chartjs-'; 15053var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; 15054var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; 15055var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; 15056 15057/** 15058 * DOM event types -> Chart.js event types. 15059 * Note: only events with different types are mapped. 15060 * @see https://developer.mozilla.org/en-US/docs/Web/Events 15061 */ 15062var EVENT_TYPES = { 15063 touchstart: 'mousedown', 15064 touchmove: 'mousemove', 15065 touchend: 'mouseup', 15066 pointerenter: 'mouseenter', 15067 pointerdown: 'mousedown', 15068 pointermove: 'mousemove', 15069 pointerup: 'mouseup', 15070 pointerleave: 'mouseout', 15071 pointerout: 'mouseout' 15072}; 15073 15074/** 15075 * The "used" size is the final value of a dimension property after all calculations have 15076 * been performed. This method uses the computed style of `element` but returns undefined 15077 * if the computed style is not expressed in pixels. That can happen in some cases where 15078 * `element` has a size relative to its parent and this last one is not yet displayed, 15079 * for example because of `display: none` on a parent node. 15080 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value 15081 * @returns {Number} Size in pixels or undefined if unknown. 15082 */ 15083function readUsedSize(element, property) { 15084 var value = helpers.getStyle(element, property); 15085 var matches = value && value.match(/^(\d+)(\.\d+)?px$/); 15086 return matches ? Number(matches[1]) : undefined; 15087} 15088 15089/** 15090 * Initializes the canvas style and render size without modifying the canvas display size, 15091 * since responsiveness is handled by the controller.resize() method. The config is used 15092 * to determine the aspect ratio to apply in case no explicit height has been specified. 15093 */ 15094function initCanvas(canvas, config) { 15095 var style = canvas.style; 15096 15097 // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it 15098 // returns null or '' if no explicit value has been set to the canvas attribute. 15099 var renderHeight = canvas.getAttribute('height'); 15100 var renderWidth = canvas.getAttribute('width'); 15101 15102 // Chart.js modifies some canvas values that we want to restore on de
15102stroy 15103 canvas[EXPANDO_KEY] = { 15104 initial: { 15105 height: renderHeight, 15106 width: renderWidth, 15107 style: { 15108 display: style.display, 15109 height: style.height, 15110 width: style.width 15111 } 15112 } 15113 }; 15114 15115 // Force canvas to display as block to avoid extra space caused by inline 15116 // elements, which would interfere with the responsive resize process. 15117 // https://github.com/chartjs/Chart.js/issues/2538 15118 style.display = style.display || 'block'; 15119 15120 if (renderWidth === null || renderWidth === '') { 15121 var displayWidth = readUsedSize(canvas, 'width'); 15122 if (displayWidth !== undefined) { 15123 canvas.width = displayWidth; 15124 } 15125 } 15126 15127 if (renderHeight === null || renderHeight === '') { 15128 if (canvas.style.height === '') { 15129 // If no explicit render height and style height, let's apply the aspect ratio, 15130 // which one can be specified by the user but also by charts as default option 15131 // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. 15132 canvas.height = canvas.width / (config.options.aspectRatio || 2); 15133 } else { 15134 var displayHeight = readUsedSize(canvas, 'height'); 15135 if (displayWidth !== undefined) { 15136 canvas.height = displayHeight; 15137 } 15138 } 15139 } 15140 15141 return canvas; 15142} 15143 15144/** 15145 * Detects support for options object argument in addEventListener. 15146 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support 15147 * @private 15148 */ 15149var supportsEventListenerOptions = (function() { 15150 var supports = false; 15151 try { 15152 var options = Object.defineProperty({}, 'passive', { 15153 get: function() { 15154 supports = true; 15155 } 15156 }); 15157 window.addEventListener('e', null, options); 15158 } catch (e) { 15159 // continue regardless of error 15160 } 15161 return supports; 15162}()); 15163 15164// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. 15165// https://github.com/chartjs/Chart.js/issues/4287 15166var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; 15167 15168function addEventListener(node, type, listener) { 15169 node.addEventListener(type, listener, eventListenerOptions); 15170} 15171 15172function removeEventListener(node, type, listener) { 15173 node.removeEventListener(type, listener, eventListenerOptions); 15174} 15175 15176function createEvent(type, chart, x, y, nativeEvent) { 15177 return { 15178 type: type, 15179 chart: chart, 15180 native: nativeEvent || null, 15181 x: x !== undefined ? x : null, 15182 y: y !== undefined ? y : null, 15183 }; 15184} 15185 15186function fromNativeEvent(event, chart) { 15187 var type = EVENT_TYPES[event.type] || event.type; 15188 var pos = helpers.getRelativePosition(event, chart); 15189 return createEvent(type, chart, pos.x, pos.y, event); 15190} 15191 15192function throttled(fn, thisArg) { 15193 var ticking = false; 15194 var args = []; 15195 15196 return function() { 15197 args = Array.prototype.slice.call(arguments); 15198 thisArg = thisArg || this; 15199 15200 if (!ticking) { 15201 ticking = true; 15202 helpers.requestAnimFrame.call(window, function() { 15203 ticking = false; 15204 fn.apply(thisArg, args); 15205 }); 15206 } 15207 }; 15208} 15209 15210// Implementation based on https://github.com/marcj/css-element-queries 15211function createResizer(handler) { 15212 var resizer = document.createElement('div'); 15213 var cls = CSS_PREFIX + 'size-monitor'; 15214 var maxSize = 1000000; 15215 var style = 15216 'position:absolute;' + 15217 'left:0;' + 15218 'top:0;' + 15219 'right:0;' + 15220 'bottom:0;' + 15221 'overflow:hidden;' + 15222 'pointer-events:none;' + 15223 'visibility:hidden;' + 15224 'z-index:-1;'; 15225 15226 resizer.style.cssText = style; 15227 resizer.className = cls; 15228 resizer.innerHTML = 15229 '<div class="' + cls + '-expand" style="' + style + '">' + 15230 '<div style="' + 15231 'position:absolute;' + 15232 'width:' + maxSize + 'px;' + 15233 'height:' + maxSize + 'px;' + 15234 'left:0;' + 15235 'top:0">' + 15236 '</div>' + 15237 '</div>' + 15238 '<div class="' + cls + '-shrink" style="' + style + '">' + 15239 '<div style="' + 15240 'position:absolute;' + 15241 'width:200%;' + 15242 'height:200%;' + 15243 'left:0; ' + 15244 'top:0">' + 15245 '</div>' + 15246 '</div>'; 15247 15248 var expand = resizer.childNodes[0]; 15249 var shrink = resizer.childNodes[1]; 15250 15251 resizer._reset = function() { 15252 expand.scrollLeft = maxSize; 15253 expand.scrollTop = maxSize; 15254 shrink.scrollLeft = maxSize; 15255 shrink.scrollTop = maxSize; 15256 }; 15257 var onScroll = function() { 15258 resizer._reset(); 15259 handler(); 15260 }; 15261 15262 addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); 15263 addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); 15264 15265 return resizer; 15266} 15267 15268// https://davidwalsh.name/detect-node-insertion 15269function watchForRender(node, handler) { 15270 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); 15271 var proxy = expando.renderProxy = function(e) { 15272 if (e.animationName === CSS_RENDER_ANIMATION) { 15273 handler(); 15274 } 15275 }; 15276
15277 helpers.each(ANIMATION_START_EVENTS, function(type) { 15278 addEventListener(node, type, proxy); 15279 }); 15280 15281 // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class 15282 // is removed then added back immediately (same animation frame?). Accessing the 15283 // `offsetParent` property will force a reflow and re-evaluate the CSS animation. 15284 // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics 15285 // https://github.com/chartjs/Chart.js/issues/4737 15286 expando.reflow = !!node.offsetParent; 15287 15288 node.classList.add(CSS_RENDER_MONITOR); 15289} 15290 15291function unwatchForRender(node) { 15292 var expando = node[EXPANDO_KEY] || {}; 15293 var proxy = expando.renderProxy; 15294 15295 if (proxy) { 15296 helpers.each(ANIMATION_START_EVENTS, function(type) { 15297 removeEventListener(node, type, proxy); 15298 }); 15299 15300 delete expando.renderProxy; 15301 } 15302 15303 node.classList.remove(CSS_RENDER_MONITOR); 15304} 15305 15306function addResizeListener(node, listener, chart) { 15307 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); 15308 15309 // Let's keep track of this added resizer and thus avoid DOM query when removing it. 15310 var resizer = expando.resizer = createResizer(throttled(function() { 15311 if (expando.resizer) { 15312 return listener(createEvent('resize', chart)); 15313 } 15314 })); 15315 15316 // The resizer needs to be attached to the node parent, so we first need to be 15317 // sure that `node` is attached to the DOM before injecting the resizer element. 15318 watchForRender(node, function() { 15319 if (expando.resizer) { 15320 var container = node.parentNode; 15321 if (container && container !== resizer.parentNode) { 15322 container.insertBefore(resizer, container.firstChild); 15323 } 15324 15325 // The container size might have changed, let's reset the resizer state. 15326 resizer._reset(); 15327 } 15328 }); 15329} 15330 15331function removeResizeListener(node) { 15332 var expando = node[EXPANDO_KEY] || {}; 15333 var resizer = expando.resizer; 15334 15335 delete expando.resizer; 15336 unwatchForRender(node); 15337 15338 if (resizer && resizer.parentNode) { 15339 resizer.parentNode.removeChild(resizer); 15340 } 15341} 15342 15343function injectCSS(platform, css) { 15344 // http://stackoverflow.com/q/3922139 15345 var style = platform._style || document.createElement('style'); 15346 if (!platform._style) { 15347 platform._style = style; 15348 css = '/* Chart.js */\n' + css; 15349 style.setAttribute('type', 'text/css'); 15350 document.getElementsByTagName('head')[0].appendChild(style); 15351 } 15352 15353 style.appendChild(document.createTextNode(css)); 15354} 15355 15356module.exports = { 15357 /** 15358 * This property holds whether this platform is enabled for the current environment. 15359 * Currently used by platform.js to select the proper implementation. 15360 * @private 15361 */ 15362 _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', 15363 15364 initialize: function() { 15365 var keyframes = 'from{opacity:0.99}to{opacity:1}'; 15366 15367 injectCSS(this, 15368 // DOM rendering detection 15369 // https://davidwalsh.name/detect-node-insertion 15370 '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + 15371 '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + 15372 '.' + CSS_RENDER_MONITOR + '{' + 15373 '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 15374 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + 15375 '}' 15376 ); 15377 }, 15378 15379 acquireContext: function(item, config) { 15380 if (typeof item === 'string') { 15381 item = document.getElementById(item); 15382 } else if (item.length) { 15383 // Support for array based queries (such as jQuery) 15384 item = item[0]; 15385 } 15386 15387 if (item && item.canvas) { 15388 // Support for any object associated to a canvas (including a context2d) 15389 item = item.canvas; 15390 } 15391 15392 // To prevent canvas fingerprinting, some add-ons undefine the getContext 15393 // method, for example: https://github.com/kkapsner/CanvasBlocker 15394 // https://github.com/chartjs/Chart.js/issues/2807 15395 var context = item && item.getContext && item.getContext('2d'); 15396 15397 // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is 15398 // inside an iframe or when running in a protected environment. We could guess the 15399 // types from their toString() value but let's keep things flexible and assume it's 15400 // a sufficient condition if the item has a context2D which has item as `canvas`. 15401 // https://github.com/chartjs/Chart.js/issues/3887 15402 // https://github.com/chartjs/Chart.js/issues/4102 15403 // https://github.com/chartjs/Chart.js/issues/4152 15404 if (context && context.canvas === item) { 15405 initCanvas(item, config); 15406 return context; 15407 } 15408 15409 return null; 15410 }, 15411 15412 releaseContext: function(context) { 15413 var canvas = context.canvas; 15414 if (!canvas[EXPANDO_KEY]) { 15415 return; 15416 } 15417 15418 var initial = canvas[EXPANDO_KEY].initial; 15419 ['height', 'width'].forEach(function(prop) { 15420 var value = initial[prop]; 15421 if (helpers.isNullOrUndef(value)) { 15422 canvas.removeAttribute(prop); 15423 } else { 15424 canvas.setAttribute(prop, value); 15425 } 15426 }); 15427 15428 helpers.each(initial.style || {}, function(value, key) { 15429 canvas.style[key] = value; 15430 }); 15431 15432 // The canvas render size might have been changed (and thus the state stack discarded),
15433 // we can't use save() and restore() to restore the initial state. So make sure that at 15434 // least the canvas context is reset to the default state by setting the canvas width. 15435 // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html 15436 canvas.width = canvas.width; 15437 15438 delete canvas[EXPANDO_KEY]; 15439 }, 15440 15441 addEventListener: function(chart, type, listener) { 15442 var canvas = chart.canvas; 15443 if (type === 'resize') { 15444 // Note: the resize event is not supported on all browsers. 15445 addResizeListener(canvas, listener, chart); 15446 return; 15447 } 15448 15449 var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); 15450 var proxies = expando.proxies || (expando.proxies = {}); 15451 var proxy = proxies[chart.id + '_' + type] = function(event) { 15452 listener(fromNativeEvent(event, chart)); 15453 }; 15454 15455 addEventListener(canvas, type, proxy); 15456 }, 15457 15458 removeEventListener: function(chart, type, listener) { 15459 var canvas = chart.canvas; 15460 if (type === 'resize') { 15461 // Note: the resize event is not supported on all browsers. 15462 removeResizeListener(canvas, listener); 15463 return; 15464 } 15465 15466 var expando = listener[EXPANDO_KEY] || {}; 15467 var proxies = expando.proxies || {}; 15468 var proxy = proxies[chart.id + '_' + type]; 15469 if (!proxy) { 15470 return; 15471 } 15472 15473 removeEventListener(canvas, type, proxy); 15474 } 15475}; 15476 15477// DEPRECATIONS 15478 15479/** 15480 * Provided for backward compatibility, use EventTarget.addEventListener instead. 15481 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ 15482 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener 15483 * @function Chart.helpers.addEvent 15484 * @deprecated since version 2.7.0 15485 * @todo remove at version 3 15486 * @private 15487 */ 15488helpers.addEvent = addEventListener; 15489 15490/** 15491 * Provided for backward compatibility, use EventTarget.removeEventListener instead. 15492 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ 15493 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener 15494 * @function Chart.helpers.removeEvent 15495 * @deprecated since version 2.7.0 15496 * @todo remove at version 3 15497 * @private 15498 */ 15499helpers.removeEvent = removeEventListener; 15500 15501},{"45":45}],48:[function(require,module,exports){ 15502'use strict'; 15503 15504var helpers = require(45); 15505var basic = require(46); 15506var dom = require(47); 15507 15508// @TODO Make possible to select another platform at build time. 15509var implementation = dom._enabled ? dom : basic; 15510 15511/** 15512 * @namespace Chart.platform 15513 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html 15514 * @since 2.4.0 15515 */ 15516module.exports = helpers.extend({ 15517 /** 15518 * @since 2.7.0 15519 */ 15520 initialize: function() {}, 15521 15522 /** 15523 * Called at chart construction time, returns a context2d instance implementing 15524 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. 15525 * @param {*} item - The native item from which to acquire context (platform specific) 15526 * @param {Object} options - The chart options 15527 * @returns {CanvasRenderingContext2D} context2d instance 15528 */ 15529 acquireContext: function() {}, 15530 15531 /** 15532 * Called at chart destruction time, releases any resources associated to the context 15533 * previously returned by the acquireContext() method. 15534 * @param {CanvasRenderingContext2D} context - The context2d instance 15535 * @returns {Boolean} true if the method succeeded, else false 15536 */ 15537 releaseContext: function() {}, 15538 15539 /** 15540 * Registers the specified listener on the given chart. 15541 * @param {Chart} chart - Chart from which to listen for event 15542 * @param {String} type - The ({@link IEvent}) type to listen for 15543 * @param {Function} listener - Receives a notification (an object that implements 15544 * the {@link IEvent} interface) when an event of the specified type occurs. 15545 */ 15546 addEventListener: function() {}, 15547 15548 /** 15549 * Removes the specified listener previously registered with addEventListener. 15550 * @param {Chart} chart -Chart from which to remove the listener 15551 * @param {String} type - The ({@link IEvent}) type to remove 15552 * @param {Function} listener - The listener function to remove from the event target. 15553 */ 15554 removeEventListener: function() {} 15555 15556}, implementation); 15557 15558/** 15559 * @interface IPlatform 15560 * Allows abstracting platform dependencies away from the chart 15561 * @borrows Chart.platform.acquireContext as acquireContext 15562 * @borrows Chart.platform.releaseContext as releaseContext 15563 * @borrows Chart.platform.addEventListener as addEventListener 15564 * @borrows Chart.platform.removeEventListener as removeEventListener 15565 */ 15566 15567/** 15568 * @interface IEvent 15569 * @prop {String} type - The event type name, possible values are: 15570 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
15571 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' 15572 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') 15573 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) 15574 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) 15575 */ 15576 15577},{"45":45,"46":46,"47":47}],49:[function(require,module,exports){ 15578'use strict'; 15579 15580module.exports = {}; 15581module.exports.filler = require(50); 15582module.exports.legend = require(51); 15583module.exports.title = require(52); 15584 15585},{"50":50,"51":51,"52":52}],50:[function(require,module,exports){ 15586/** 15587 * Plugin based on discussion from the following Chart.js issues: 15588 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 15589 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 15590 */ 15591 15592'use strict'; 15593 15594var defaults = require(25); 15595var elements = require(40); 15596var helpers = require(45); 15597 15598defaults._set('global', { 15599 plugins: { 15600 filler: { 15601 propagate: true 15602 } 15603 } 15604}); 15605 15606var mappers = { 15607 dataset: function(source) { 15608 var index = source.fill; 15609 var chart = source.chart; 15610 var meta = chart.getDatasetMeta(index); 15611 var visible = meta && chart.isDatasetVisible(index); 15612 var points = (visible && meta.dataset._children) || []; 15613 var length = points.length || 0; 15614 15615 return !length ? null : function(point, i) { 15616 return (i < length && points[i]._view) || null; 15617 }; 15618 }, 15619 15620 boundary: function(source) { 15621 var boundary = source.boundary; 15622 var x = boundary ? boundary.x : null; 15623 var y = boundary ? boundary.y : null; 15624 15625 return function(point) { 15626 return { 15627 x: x === null ? point.x : x, 15628 y: y === null ? point.y : y, 15629 }; 15630 }; 15631 } 15632}; 15633 15634// @todo if (fill[0] === '#') 15635function decodeFill(el, index, count) { 15636 var model = el._model || {}; 15637 var fill = model.fill; 15638 var target; 15639 15640 if (fill === undefined) { 15641 fill = !!model.backgroundColor; 15642 } 15643 15644 if (fill === false || fill === null) { 15645 return false; 15646 } 15647 15648 if (fill === true) { 15649 return 'origin'; 15650 } 15651 15652 target = parseFloat(fill, 10); 15653 if (isFinite(target) && Math.floor(target) === target) { 15654 if (fill[0] === '-' || fill[0] === '+') { 15655 target = index + target; 15656 } 15657 15658 if (target === index || target < 0 || target >= count) { 15659 return false; 15660 } 15661 15662 return target; 15663 } 15664 15665 switch (fill) { 15666 // compatibility 15667 case 'bottom': 15668 return 'start'; 15669 case 'top': 15670 return 'end'; 15671 case 'zero': 15672 return 'origin'; 15673 // supported boundaries 15674 case 'origin': 15675 case 'start': 15676 case 'end': 15677 return fill; 15678 // invalid fill values 15679 default: 15680 return false; 15681 } 15682} 15683 15684function computeBoundary(source) { 15685 var model = source.el._model || {}; 15686 var scale = source.el._scale || {}; 15687 var fill = source.fill; 15688 var target = null; 15689 var horizontal; 15690 15691 if (isFinite(fill)) { 15692 return null; 15693 } 15694 15695 // Backward compatibility: until v3, we still need to support boundary values set on 15696 // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and 15697 // controllers might still use it (e.g. the Smith chart). 15698 15699 if (fill === 'start') { 15700 target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; 15701 } else if (fill === 'end') { 15702 target = model.scaleTop === undefined ? scale.top : model.scaleTop; 15703 } else if (model.scaleZero !== undefined) { 15704 target = model.scaleZero; 15705 } else if (scale.getBasePosition) { 15706 target = scale.getBasePosition(); 15707 } else if (scale.getBasePixel) { 15708 target = scale.getBasePixel(); 15709 } 15710 15711 if (target !== undefined && target !== null) { 15712 if (target.x !== undefined && target.y !== undefined) { 15713 return target; 15714 } 15715 15716 if (typeof target === 'number' && isFinite(target)) { 15717 horizontal = scale.isHorizontal(); 15718 return { 15719 x: horizontal ? target : null, 15720 y: horizontal ? null : target 15721 }; 15722 } 15723 } 15724 15725 return null; 15726} 15727 15728function resolveTarget(sources, index, propagate) { 15729 var source = sources[index]; 15730 var fill = source.fill; 15731 var visited = [index]; 15732 var target; 15733 15734 if (!propagate) { 15735 return fill; 15736 } 15737 15738 while (fill !== false && visited.indexOf(fill) === -1) { 15739 if (!isFinite(fill)) { 15740 return fill; 15741 } 15742 15743 target = sources[fill]; 15744 if (!target) { 15745 return false;
15746 } 15747 15748 if (target.visible) { 15749 return fill; 15750 } 15751 15752 visited.push(fill); 15753 fill = target.fill; 15754 } 15755 15756 return false; 15757} 15758 15759function createMapper(source) { 15760 var fill = source.fill; 15761 var type = 'dataset'; 15762 15763 if (fill === false) { 15764 return null; 15765 } 15766 15767 if (!isFinite(fill)) { 15768 type = 'boundary'; 15769 } 15770 15771 return mappers[type](source); 15772} 15773 15774function isDrawable(point) { 15775 return point && !point.skip; 15776} 15777 15778function drawArea(ctx, curve0, curve1, len0, len1) { 15779 var i; 15780 15781 if (!len0 || !len1) { 15782 return; 15783 } 15784 15785 // building first area curve (normal) 15786 ctx.moveTo(curve0[0].x, curve0[0].y); 15787 for (i = 1; i < len0; ++i) { 15788 helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); 15789 } 15790 15791 // joining the two area curves 15792 ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); 15793 15794 // building opposite area curve (reverse) 15795 for (i = len1 - 1; i > 0; --i) { 15796 helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); 15797 } 15798} 15799 15800function doFill(ctx, points, mapper, view, color, loop) { 15801 var count = points.length; 15802 var span = view.spanGaps; 15803 var curve0 = []; 15804 var curve1 = []; 15805 var len0 = 0; 15806 var len1 = 0; 15807 var i, ilen, index, p0, p1, d0, d1; 15808 15809 ctx.beginPath(); 15810 15811 for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { 15812 index = i % count; 15813 p0 = points[index]._view; 15814 p1 = mapper(p0, index, view); 15815 d0 = isDrawable(p0); 15816 d1 = isDrawable(p1); 15817 15818 if (d0 && d1) { 15819 len0 = curve0.push(p0); 15820 len1 = curve1.push(p1); 15821 } else if (len0 && len1) { 15822 if (!span) { 15823 drawArea(ctx, curve0, curve1, len0, len1); 15824 len0 = len1 = 0; 15825 curve0 = []; 15826 curve1 = []; 15827 } else { 15828 if (d0) { 15829 curve0.push(p0); 15830 } 15831 if (d1) { 15832 curve1.push(p1); 15833 } 15834 } 15835 } 15836 } 15837 15838 drawArea(ctx, curve0, curve1, len0, len1); 15839 15840 ctx.closePath(); 15841 ctx.fillStyle = color; 15842 ctx.fill(); 15843} 15844 15845module.exports = { 15846 id: 'filler', 15847 15848 afterDatasetsUpdate: function(chart, options) { 15849 var count = (chart.data.datasets || []).length; 15850 var propagate = options.propagate; 15851 var sources = []; 15852 var meta, i, el, source; 15853 15854 for (i = 0; i < count; ++i) { 15855 meta = chart.getDatasetMeta(i); 15856 el = meta.dataset; 15857 source = null; 15858 15859 if (el && el._model && el instanceof elements.Line) { 15860 source = { 15861 visible: chart.isDatasetVisible(i), 15862 fill: decodeFill(el, i, count), 15863 chart: chart, 15864 el: el 15865 }; 15866 } 15867 15868 meta.$filler = source; 15869 sources.push(source); 15870 } 15871 15872 for (i = 0; i < count; ++i) { 15873 source = sources[i]; 15874 if (!source) { 15875 continue; 15876 } 15877 15878 source.fill = resolveTarget(sources, i, propagate); 15879 source.boundary = computeBoundary(source); 15880 source.mapper = createMapper(source); 15881 } 15882 }, 15883 15884 beforeDatasetDraw: function(chart, args) { 15885 var meta = args.meta.$filler; 15886 if (!meta) { 15887 return; 15888 } 15889 15890 var ctx = chart.ctx; 15891 var el = meta.el; 15892 var view = el._view; 15893 var points = el._children || []; 15894 var mapper = meta.mapper; 15895 var color = view.backgroundColor || defaults.global.defaultColor; 15896 15897 if (mapper && color && points.length) { 15898 helpers.canvas.clipArea(ctx, chart.chartArea); 15899 doFill(ctx, points, mapper, view, color, el._loop); 15900 helpers.canvas.unclipArea(ctx); 15901 } 15902 } 15903}; 15904 15905},{"25":25,"40":40,"45":45}],51:[function(require,module,exports){ 15906'use strict'; 15907 15908var defaults = require(25); 15909var Element = require(26); 15910var helpers = require(45); 15911var layouts = require(30); 15912 15913var noop = helpers.noop; 15914 15915defaults._set('global', { 15916 legend: { 15917 display: true, 15918 position: 'top', 15919 fullWidth: true, 15920 reverse: false, 15921 weight: 1000, 15922 15923 // a callback that will handle 15924 onClick: function(e, legendItem) { 15925 var index = legendItem.datasetIndex; 15926 var ci = this.chart; 15927 var meta = ci.getDatasetMeta(index); 15928 15929 // See controller.isDatasetVisible comment 15930 meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; 15931 15932 // We hid a dataset ... rerender the chart 15933 ci.update(); 15934 }, 15935 15936 onHover: null, 15937 15938 labels: { 15939 boxWidth: 40, 15940 padding: 10, 15941 // Generates labels shown in the legend 15942 // Valid properties to return: 15943 // text : text to display 15944 // fillStyle : fill of coloured box 15945 // strokeStyle: stroke of coloured box 15946 // hidden : if this legend item refers to a hidden item 15947 // lineCap : cap style for line 15948 // lineDash 15949 // lineDashOffset : 15950 // lineJoin : 15951 // lineWidth :
15952 generateLabels: function(chart) { 15953 var data = chart.data; 15954 return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { 15955 return { 15956 text: dataset.label, 15957 fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), 15958 hidden: !chart.isDatasetVisible(i), 15959 lineCap: dataset.borderCapStyle, 15960 lineDash: dataset.borderDash, 15961 lineDashOffset: dataset.borderDashOffset, 15962 lineJoin: dataset.borderJoinStyle, 15963 lineWidth: dataset.borderWidth, 15964 strokeStyle: dataset.borderColor, 15965 pointStyle: dataset.pointStyle, 15966 15967 // Below is extra data used for toggling the datasets 15968 datasetIndex: i 15969 }; 15970 }, this) : []; 15971 } 15972 } 15973 }, 15974 15975 legendCallback: function(chart) { 15976 var text = []; 15977 text.push('<ul class="' + chart.id + '-legend">'); 15978 for (var i = 0; i < chart.data.datasets.length; i++) { 15979 text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>'); 15980 if (chart.data.datasets[i].label) { 15981 text.push(chart.data.datasets[i].label); 15982 } 15983 text.push('</li>'); 15984 } 15985 text.push('</ul>'); 15986 return text.join(''); 15987 } 15988}); 15989 15990/** 15991 * Helper function to get the box width based on the usePointStyle option 15992 * @param labelopts {Object} the label options on the legend 15993 * @param fontSize {Number} the label font size 15994 * @return {Number} width of the color box area 15995 */ 15996function getBoxWidth(labelOpts, fontSize) { 15997 return labelOpts.usePointStyle ? 15998 fontSize * Math.SQRT2 : 15999 labelOpts.boxWidth; 16000} 16001 16002/** 16003 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! 16004 */ 16005var Legend = Element.extend({ 16006 16007 initialize: function(config) { 16008 helpers.extend(this, config); 16009 16010 // Contains hit boxes for each dataset (in dataset order) 16011 this.legendHitBoxes = []; 16012 16013 // Are we in doughnut mode which has a different data type 16014 this.doughnutMode = false; 16015 }, 16016 16017 // These methods are ordered by lifecycle. Utilities then follow. 16018 // Any function defined here is inherited by all legend types. 16019 // Any function can be extended by the legend type 16020 16021 beforeUpdate: noop, 16022 update: function(maxWidth, maxHeight, margins) { 16023 var me = this; 16024 16025 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) 16026 me.beforeUpdate(); 16027 16028 // Absorb the master measurements 16029 me.maxWidth = maxWidth; 16030 me.maxHeight = maxHeight; 16031 me.margins = margins; 16032 16033 // Dimensions 16034 me.beforeSetDimensions(); 16035 me.setDimensions(); 16036 me.afterSetDimensions(); 16037 // Labels 16038 me.beforeBuildLabels(); 16039 me.buildLabels(); 16040 me.afterBuildLabels(); 16041 16042 // Fit 16043 me.beforeFit(); 16044 me.fit(); 16045 me.afterFit(); 16046 // 16047 me.afterUpdate(); 16048 16049 return me.minSize; 16050 }, 16051 afterUpdate: noop, 16052 16053 // 16054 16055 beforeSetDimensions: noop, 16056 setDimensions: function() { 16057 var me = this; 16058 // Set the unconstrained dimension before label rotation 16059 if (me.isHorizontal()) { 16060 // Reset position before calculating rotation 16061 me.width = me.maxWidth; 16062 me.left = 0; 16063 me.right = me.width; 16064 } else { 16065 me.height = me.maxHeight; 16066 16067 // Reset position before calculating rotation 16068 me.top = 0; 16069 me.bottom = me.height; 16070 } 16071 16072 // Reset padding 16073 me.paddingLeft = 0; 16074 me.paddingTop = 0; 16075 me.paddingRight = 0; 16076 me.paddingBottom = 0; 16077 16078 // Reset minSize 16079 me.minSize = { 16080 width: 0, 16081 height: 0 16082 }; 16083 }, 16084 afterSetDimensions: noop, 16085 16086 // 16087 16088 beforeBuildLabels: noop, 16089 buildLabels: function() { 16090 var me = this; 16091 var labelOpts = me.options.labels || {}; 16092 var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; 16093 16094 if (labelOpts.filter) { 16095 legendItems = legendItems.filter(function(item) { 16096 return labelOpts.filter(item, me.chart.data); 16097 }); 16098 } 16099 16100 if (me.options.reverse) { 16101 legendItems.reverse(); 16102 } 16103 16104 me.legendItems = legendItems; 16105 }, 16106 afterBuildLabels: noop, 16107 16108 // 16109 16110 beforeFit: noop, 16111 fit: function() { 16112 var me = this; 16113 var opts = me.options; 16114 var labelOpts = opts.labels; 16115 var display = opts.display; 16116 16117 var ctx = me.ctx; 16118 16119 var globalDefault = defaults.global; 16120 var valueOrDefault = helpers.valueOrDefault; 16121 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); 16122 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); 16123 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); 16124 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16125 16126 // Reset hit boxes 16127 var hitboxes = me.legendHitBoxes = []; 16128 16129 var minSize = me.minSize; 16130 var isHorizontal = me.isHorizontal(); 16131 16132 if (isHorizontal) { 16133 minSize.width = me.maxWidth; // fill all the width 16134 minSize.height = display ? 10 : 0; 16135 } else { 16136 minSize.width = display ? 10 : 0; 16137 minSize.height = me.maxHeight; // fill all the height 16138 } 16139 16140 // Increase sizes here 16141 if (display) { 16142 ctx.font = labelFont; 16143 16144 if (isHorizontal) { 16145 // Labels 16146
16147 // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one 16148 var lineWidths = me.lineWidths = [0]; 16149 var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; 16150 16151 ctx.textAlign = 'left'; 16152 ctx.textBaseline = 'top'; 16153 16154 helpers.each(me.legendItems, function(legendItem, i) { 16155 var boxWidth = getBoxWidth(labelOpts, fontSize); 16156 var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; 16157 16158 if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { 16159 totalHeight += fontSize + (labelOpts.padding); 16160 lineWidths[lineWidths.length] = me.left; 16161 } 16162 16163 // Store the hitbox width and height here. Final position will be updated in `draw` 16164 hitboxes[i] = { 16165 left: 0, 16166 top: 0, 16167 width: width, 16168 height: fontSize 16169 }; 16170 16171 lineWidths[lineWidths.length - 1] += width + labelOpts.padding; 16172 }); 16173 16174 minSize.height += totalHeight; 16175 16176 } else { 16177 var vPadding = labelOpts.padding; 16178 var columnWidths = me.columnWidths = []; 16179 var totalWidth = labelOpts.padding; 16180 var currentColWidth = 0; 16181 var currentColHeight = 0; 16182 var itemHeight = fontSize + vPadding; 16183 16184 helpers.each(me.legendItems, function(legendItem, i) { 16185 var boxWidth = getBoxWidth(labelOpts, fontSize); 16186 var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; 16187 16188 // If too tall, go to new column 16189 if (currentColHeight + itemHeight > minSize.height) { 16190 totalWidth += currentColWidth + labelOpts.padding; 16191 columnWidths.push(currentColWidth); // previous column width 16192 16193 currentColWidth = 0; 16194 currentColHeight = 0; 16195 } 16196 16197 // Get max width 16198 currentColWidth = Math.max(currentColWidth, itemWidth); 16199 currentColHeight += itemHeight; 16200 16201 // Store the hitbox width and height here. Final position will be updated in `draw` 16202 hitboxes[i] = { 16203 left: 0, 16204 top: 0, 16205 width: itemWidth, 16206 height: fontSize 16207 }; 16208 }); 16209 16210 totalWidth += currentColWidth; 16211 columnWidths.push(currentColWidth); 16212 minSize.width += totalWidth; 16213 } 16214 } 16215 16216 me.width = minSize.width; 16217 me.height = minSize.height; 16218 }, 16219 afterFit: noop, 16220 16221 // Shared Methods 16222 isHorizontal: function() { 16223 return this.options.position === 'top' || this.options.position === 'bottom'; 16224 }, 16225 16226 // Actually draw the legend on the canvas 16227 draw: function() { 16228 var me = this; 16229 var opts = me.options; 16230 var labelOpts = opts.labels; 16231 var globalDefault = defaults.global; 16232 var lineDefault = globalDefault.elements.line; 16233 var legendWidth = me.width; 16234 var lineWidths = me.lineWidths; 16235 16236 if (opts.display) { 16237 var ctx = me.ctx; 16238 var valueOrDefault = helpers.valueOrDefault; 16239 var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); 16240 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); 16241 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); 16242 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); 16243 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16244 var cursor; 16245 16246 // Canvas setup 16247 ctx.textAlign = 'left'; 16248 ctx.textBaseline = 'middle'; 16249 ctx.lineWidth = 0.5; 16250 ctx.strokeStyle = fontColor; // for strikethrough effect 16251 ctx.fillStyle = fontColor; // render in correct colour 16252 ctx.font = labelFont; 16253 16254 var boxWidth = getBoxWidth(labelOpts, fontSize); 16255 var hitboxes = me.legendHitBoxes; 16256 16257 // current position 16258 var drawLegendBox = function(x, y, legendItem) { 16259 if (isNaN(boxWidth) || boxWidth <= 0) { 16260 return; 16261 } 16262 16263 // Set the ctx for the box 16264 ctx.save(); 16265 16266 ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); 16267 ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); 16268 ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); 16269 ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); 16270 ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth); 16271 ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); 16272 var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); 16273 16274 if (ctx.setLineDash) { 16275 // IE 9 and 10 do not support line dash 16276 ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); 16277 } 16278 16279 if (opts.labels && opts.labels.usePointStyle) { 16280 // Recalculate x and y for drawPoint() because its expecting 16281 // x and y to be center of figure (instead of top left) 16282 var radius = fontSize * Math.SQRT2 / 2; 16283 var offSet = radius / Math.SQRT2; 16284 var centerX = x + offSet; 16285 var centerY = y + offSet; 16286 16287 // Draw pointStyle as legend symbol 16288 helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); 16289 } else { 16290 // Draw box as legend symbol
16291 if (!isLineWidthZero) { 16292 ctx.strokeRect(x, y, boxWidth, fontSize); 16293 } 16294 ctx.fillRect(x, y, boxWidth, fontSize); 16295 } 16296 16297 ctx.restore(); 16298 }; 16299 var fillText = function(x, y, legendItem, textWidth) { 16300 var halfFontSize = fontSize / 2; 16301 var xLeft = boxWidth + halfFontSize + x; 16302 var yMiddle = y + halfFontSize; 16303 16304 ctx.fillText(legendItem.text, xLeft, yMiddle); 16305 16306 if (legendItem.hidden) { 16307 // Strikethrough the text if hidden 16308 ctx.beginPath(); 16309 ctx.lineWidth = 2; 16310 ctx.moveTo(xLeft, yMiddle); 16311 ctx.lineTo(xLeft + textWidth, yMiddle); 16312 ctx.stroke(); 16313 } 16314 }; 16315 16316 // Horizontal 16317 var isHorizontal = me.isHorizontal(); 16318 if (isHorizontal) { 16319 cursor = { 16320 x: me.left + ((legendWidth - lineWidths[0]) / 2), 16321 y: me.top + labelOpts.padding, 16322 line: 0 16323 }; 16324 } else { 16325 cursor = { 16326 x: me.left + labelOpts.padding, 16327 y: me.top + labelOpts.padding, 16328 line: 0 16329 }; 16330 } 16331 16332 var itemHeight = fontSize + labelOpts.padding; 16333 helpers.each(me.legendItems, function(legendItem, i) { 16334 var textWidth = ctx.measureText(legendItem.text).width; 16335 var width = boxWidth + (fontSize / 2) + textWidth; 16336 var x = cursor.x; 16337 var y = cursor.y; 16338 16339 if (isHorizontal) { 16340 if (x + width >= legendWidth) { 16341 y = cursor.y += itemHeight; 16342 cursor.line++; 16343 x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); 16344 } 16345 } else if (y + itemHeight > me.bottom) { 16346 x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; 16347 y = cursor.y = me.top + labelOpts.padding; 16348 cursor.line++; 16349 } 16350 16351 drawLegendBox(x, y, legendItem); 16352 16353 hitboxes[i].left = x; 16354 hitboxes[i].top = y; 16355 16356 // Fill the actual label 16357 fillText(x, y, legendItem, textWidth); 16358 16359 if (isHorizontal) { 16360 cursor.x += width + (labelOpts.padding); 16361 } else { 16362 cursor.y += itemHeight; 16363 } 16364 16365 }); 16366 } 16367 }, 16368 16369 /** 16370 * Handle an event 16371 * @private 16372 * @param {IEvent} event - The event to handle 16373 * @return {Boolean} true if a change occured 16374 */ 16375 handleEvent: function(e) { 16376 var me = this; 16377 var opts = me.options; 16378 var type = e.type === 'mouseup' ? 'click' : e.type; 16379 var changed = false; 16380 16381 if (type === 'mousemove') { 16382 if (!opts.onHover) { 16383 return; 16384 } 16385 } else if (type === 'click') { 16386 if (!opts.onClick) { 16387 return; 16388 } 16389 } else { 16390 return; 16391 } 16392 16393 // Chart event already has relative position in it 16394 var x = e.x; 16395 var y = e.y; 16396 16397 if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { 16398 // See if we are touching one of the dataset boxes 16399 var lh = me.legendHitBoxes; 16400 for (var i = 0; i < lh.length; ++i) { 16401 var hitBox = lh[i]; 16402 16403 if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { 16404 // Touching an element 16405 if (type === 'click') { 16406 // use e.native for backwards compatibility 16407 opts.onClick.call(me, e.native, me.legendItems[i]); 16408 changed = true; 16409 break; 16410 } else if (type === 'mousemove') { 16411 // use e.native for backwards compatibility 16412 opts.onHover.call(me, e.native, me.legendItems[i]); 16413 changed = true; 16414 break; 16415 } 16416 } 16417 } 16418 } 16419 16420 return changed; 16421 } 16422}); 16423 16424function createNewLegendAndAttach(chart, legendOpts) { 16425 var legend = new Legend({ 16426 ctx: chart.ctx, 16427 options: legendOpts, 16428 chart: chart 16429 }); 16430 16431 layouts.configure(chart, legend, legendOpts); 16432 layouts.addBox(chart, legend); 16433 chart.legend = legend; 16434} 16435 16436module.exports = { 16437 id: 'legend', 16438 16439 /** 16440 * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making 16441 * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of 16442 * the plugin, which one will be re-exposed in the chart.js file. 16443 * https://github.com/chartjs/Chart.js/pull/2640 16444 * @private 16445 */ 16446 _element: Legend, 16447 16448 beforeInit: function(chart) { 16449 var legendOpts = chart.options.legend; 16450 16451 if (legendOpts) { 16452 createNewLegendAndAttach(chart, legendOpts); 16453 } 16454 }, 16455 16456 beforeUpdate: function(chart) { 16457 var legendOpts = chart.options.legend; 16458 var legend = chart.legend; 16459 16460 if (legendOpts) { 16461 helpers.mergeIf(legendOpts, defaults.global.legend); 16462 16463 if (legend) { 16464 layouts.configure(chart, legend, legendOpts); 16465 legend.options = legendOpts; 16466 } else { 16467 createNewLegendAndAttach(chart, legendOpts); 16468 } 16469 } else if (legend) { 16470 layouts.removeBox(chart, legend); 16471 delete chart.legend; 16472 } 16473 }, 16474
16475 afterEvent: function(chart, e) { 16476 var legend = chart.legend; 16477 if (legend) { 16478 legend.handleEvent(e); 16479 } 16480 } 16481}; 16482 16483},{"25":25,"26":26,"30":30,"45":45}],52:[function(require,module,exports){ 16484'use strict'; 16485 16486var defaults = require(25); 16487var Element = require(26); 16488var helpers = require(45); 16489var layouts = require(30); 16490 16491var noop = helpers.noop; 16492 16493defaults._set('global', { 16494 title: { 16495 display: false, 16496 fontStyle: 'bold', 16497 fullWidth: true, 16498 lineHeight: 1.2, 16499 padding: 10, 16500 position: 'top', 16501 text: '', 16502 weight: 2000 // by default greater than legend (1000) to be above 16503 } 16504}); 16505 16506/** 16507 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required! 16508 */ 16509var Title = Element.extend({ 16510 initialize: function(config) { 16511 var me = this; 16512 helpers.extend(me, config); 16513 16514 // Contains hit boxes for each dataset (in dataset order) 16515 me.legendHitBoxes = []; 16516 }, 16517 16518 // These methods are ordered by lifecycle. Utilities then follow. 16519 16520 beforeUpdate: noop, 16521 update: function(maxWidth, maxHeight, margins) { 16522 var me = this; 16523 16524 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) 16525 me.beforeUpdate(); 16526 16527 // Absorb the master measurements 16528 me.maxWidth = maxWidth; 16529 me.maxHeight = maxHeight; 16530 me.margins = margins; 16531 16532 // Dimensions 16533 me.beforeSetDimensions(); 16534 me.setDimensions(); 16535 me.afterSetDimensions(); 16536 // Labels 16537 me.beforeBuildLabels(); 16538 me.buildLabels(); 16539 me.afterBuildLabels(); 16540 16541 // Fit 16542 me.beforeFit(); 16543 me.fit(); 16544 me.afterFit(); 16545 // 16546 me.afterUpdate(); 16547 16548 return me.minSize; 16549 16550 }, 16551 afterUpdate: noop, 16552 16553 // 16554 16555 beforeSetDimensions: noop, 16556 setDimensions: function() { 16557 var me = this; 16558 // Set the unconstrained dimension before label rotation 16559 if (me.isHorizontal()) { 16560 // Reset position before calculating rotation 16561 me.width = me.maxWidth; 16562 me.left = 0; 16563 me.right = me.width; 16564 } else { 16565 me.height = me.maxHeight; 16566 16567 // Reset position before calculating rotation 16568 me.top = 0; 16569 me.bottom = me.height; 16570 } 16571 16572 // Reset padding 16573 me.paddingLeft = 0; 16574 me.paddingTop = 0; 16575 me.paddingRight = 0; 16576 me.paddingBottom = 0; 16577 16578 // Reset minSize 16579 me.minSize = { 16580 width: 0, 16581 height: 0 16582 }; 16583 }, 16584 afterSetDimensions: noop, 16585 16586 // 16587 16588 beforeBuildLabels: noop, 16589 buildLabels: noop, 16590 afterBuildLabels: noop, 16591 16592 // 16593 16594 beforeFit: noop, 16595 fit: function() { 16596 var me = this; 16597 var valueOrDefault = helpers.valueOrDefault; 16598 var opts = me.options; 16599 var display = opts.display; 16600 var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); 16601 var minSize = me.minSize; 16602 var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; 16603 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); 16604 var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; 16605 16606 if (me.isHorizontal()) { 16607 minSize.width = me.maxWidth; // fill all the width 16608 minSize.height = textSize; 16609 } else { 16610 minSize.width = textSize; 16611 minSize.height = me.maxHeight; // fill all the height 16612 } 16613 16614 me.width = minSize.width; 16615 me.height = minSize.height; 16616 16617 }, 16618 afterFit: noop, 16619 16620 // Shared Methods 16621 isHorizontal: function() { 16622 var pos = this.options.position; 16623 return pos === 'top' || pos === 'bottom'; 16624 }, 16625 16626 // Actually draw the title block on the canvas 16627 draw: function() { 16628 var me = this; 16629 var ctx = me.ctx; 16630 var valueOrDefault = helpers.valueOrDefault; 16631 var opts = me.options; 16632 var globalDefaults = defaults.global; 16633 16634 if (opts.display) { 16635 var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); 16636 var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); 16637 var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); 16638 var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); 16639 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); 16640 var offset = lineHeight / 2 + opts.padding; 16641 var rotation = 0; 16642 var top = me.top; 16643 var left = me.left; 16644 var bottom = me.bottom; 16645 var right = me.right; 16646 var maxWidth, titleX, titleY; 16647 16648 ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour 16649 ctx.font = titleFont; 16650 16651 // Horizontal 16652 if (me.isHorizontal()) { 16653 titleX = left + ((right - left) / 2); // midpoint of the width 16654 titleY = top + offset; 16655 maxWidth = right - left; 16656 } else { 16657 titleX = opts.position === 'left' ? left + offset : right - offset; 16658 titleY = top + ((bottom - top) / 2); 16659 maxWidth = bottom - top; 16660 rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); 16661 } 16662 16663 ctx.save(); 16664 ctx.translate(titleX, titleY); 16665 ctx.rotate(rotation); 16666 ctx.textAlign = 'center'; 16667 ctx.textBaseline = 'middle'; 16668 16669 var text = opts.text; 16670 if (helpers.isArray(text)) { 16671 var y = 0; 16672 for (var i = 0; i < text.length; ++i) { 16673 ctx.fillText(text[i], 0, y, maxWidth); 16674 y += lineHeight; 16675 } 16676 } else { 16677 ctx.fillText(text, 0, 0, maxWidth); 16678 } 16679
16680 ctx.restore(); 16681 } 16682 } 16683}); 16684 16685function createNewTitleBlockAndAttach(chart, titleOpts) { 16686 var title = new Title({ 16687 ctx: chart.ctx, 16688 options: titleOpts, 16689 chart: chart 16690 }); 16691 16692 layouts.configure(chart, title, titleOpts); 16693 layouts.addBox(chart, title); 16694 chart.titleBlock = title; 16695} 16696 16697module.exports = { 16698 id: 'title', 16699 16700 /** 16701 * Backward compatibility: since 2.1.5, the title is registered as a plugin, making 16702 * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of 16703 * the plugin, which one will be re-exposed in the chart.js file. 16704 * https://github.com/chartjs/Chart.js/pull/2640 16705 * @private 16706 */ 16707 _element: Title, 16708 16709 beforeInit: function(chart) { 16710 var titleOpts = chart.options.title; 16711 16712 if (titleOpts) { 16713 createNewTitleBlockAndAttach(chart, titleOpts); 16714 } 16715 }, 16716 16717 beforeUpdate: function(chart) { 16718 var titleOpts = chart.options.title; 16719 var titleBlock = chart.titleBlock; 16720 16721 if (titleOpts) { 16722 helpers.mergeIf(titleOpts, defaults.global.title); 16723 16724 if (titleBlock) { 16725 layouts.configure(chart, titleBlock, titleOpts); 16726 titleBlock.options = titleOpts; 16727 } else { 16728 createNewTitleBlockAndAttach(chart, titleOpts); 16729 } 16730 } else if (titleBlock) { 16731 layouts.removeBox(chart, titleBlock); 16732 delete chart.titleBlock; 16733 } 16734 } 16735}; 16736 16737},{"25":25,"26":26,"30":30,"45":45}],53:[function(require,module,exports){ 16738'use strict'; 16739 16740module.exports = function(Chart) { 16741 16742 // Default config for a category scale 16743 var defaultConfig = { 16744 position: 'bottom' 16745 }; 16746 16747 var DatasetScale = Chart.Scale.extend({ 16748 /** 16749 * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those 16750 * else fall back to data.labels 16751 * @private 16752 */ 16753 getLabels: function() { 16754 var data = this.chart.data; 16755 return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; 16756 }, 16757 16758 determineDataLimits: function() { 16759 var me = this; 16760 var labels = me.getLabels(); 16761 me.minIndex = 0; 16762 me.maxIndex = labels.length - 1; 16763 var findIndex; 16764 16765 if (me.options.ticks.min !== undefined) { 16766 // user specified min value 16767 findIndex = labels.indexOf(me.options.ticks.min); 16768 me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; 16769 } 16770 16771 if (me.options.ticks.max !== undefined) { 16772 // user specified max value 16773 findIndex = labels.indexOf(me.options.ticks.max); 16774 me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; 16775 } 16776 16777 me.min = labels[me.minIndex]; 16778 me.max = labels[me.maxIndex]; 16779 }, 16780 16781 buildTicks: function() { 16782 var me = this; 16783 var labels = me.getLabels(); 16784 // If we are viewing some subset of labels, slice the original array 16785 me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); 16786 }, 16787 16788 getLabelForIndex: function(index, datasetIndex) { 16789 var me = this; 16790 var data = me.chart.data; 16791 var isHorizontal = me.isHorizontal(); 16792 16793 if (data.yLabels && !isHorizontal) { 16794 return me.getRightValue(data.datasets[datasetIndex].data[index]); 16795 } 16796 return me.ticks[index - me.minIndex]; 16797 }, 16798 16799 // Used to get data value locations. Value can either be an index or a numerical value 16800 getPixelForValue: function(value, index) { 16801 var me = this; 16802 var offset = me.options.offset; 16803 // 1 is added because we need the length but we have the indexes 16804 var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); 16805 16806 // If value is a data object, then index is the index in the data array, 16807 // not the index of the scale. We need to change that. 16808 var valueCategory; 16809 if (value !== undefined && value !== null) { 16810 valueCategory = me.isHorizontal() ? value.x : value.y; 16811 } 16812 if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { 16813 var labels = me.getLabels(); 16814 value = valueCategory || value; 16815 var idx = labels.indexOf(value); 16816 index = idx !== -1 ? idx : index; 16817 } 16818 16819 if (me.isHorizontal()) { 16820 var valueWidth = me.width / offsetAmt; 16821 var widthOffset = (valueWidth * (index - me.minIndex)); 16822 16823 if (offset) { 16824 widthOffset += (valueWidth / 2); 16825 } 16826 16827 return me.left + Math.round(widthOffset); 16828 } 16829 var valueHeight = me.height / offsetAmt; 16830 var heightOffset = (valueHeight * (index - me.minIndex)); 16831 16832 if (offset) { 16833 heightOffset += (valueHeight / 2); 16834 } 16835 16836 return me.top + Math.round(heightOffset); 16837 }, 16838 getPixelForTick: function(index) { 16839 return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); 16840 }, 16841 getValueForPixel: function(pixel) { 16842 var me = this; 16843 var offset = me.options.offset; 16844 var value; 16845 var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); 16846 var horz = me.isHorizontal(); 16847 var valueDimension = (horz ? me.width : me.height) / offsetAmt; 16848 16849 pixel -= horz ? me.left : me.top; 16850 16851 if (offset) { 16852 pixel -= (valueDimension / 2); 16853 } 16854 16855 if (pixel <= 0) { 16856 value = 0; 16857 } else { 16858 value = Math.round(pixel / valueDimension); 16859 } 16860 16861 return value + me.minIndex; 16862 }, 16863 getBasePixel: function() { 16864 return this.bottom; 16865 } 16866 }); 16867 16868 Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); 16869 16870}; 16871 16872},{}],54:[function(require,module,exports){ 16873'use strict'; 16874 16875var defaults = require(25); 16876var helpers = require(45); 16877var Ticks = require(34); 16878 16879module.exports = function(Chart) { 16880 16881 var defaultConfig = { 16882 position: 'left', 16883 ticks: { 16884 callback: Ticks.formatters.linear 16885 } 16886 }; 16887 16888 var LinearScale = Chart.LinearScaleBase.extend({ 16889 16890 determineDataLimits: function() { 16891 var me = this; 16892 var opts = me.options; 16893 var chart = me.chart; 16894 var data = chart.data; 16895 var datasets = data.datasets; 16896 var isHorizontal = me.isHorizontal(); 16897 var DEFAULT_MIN = 0; 16898 var DEFAULT_MAX = 1; 16899 16900 function IDMatches(meta) { 16901 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; 16902 } 16903 16904 // First Calculate the range 16905 me.min = null; 16906 me.max = null; 16907 16908 var hasStacks = opts.stacked; 16909 if (hasStacks === undefined) { 16910 helpers.each(datasets, function(dataset, datasetIndex) { 16911 if (hasStacks) { 16912 return; 16913 } 16914 16915 var meta = chart.getDatasetMeta(datasetIndex); 16916 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && 16917 meta.stack !== undefined) { 16918 hasStacks = true; 16919 } 16920 }); 16921 } 16922 16923 if (opts.stacked || hasStacks) { 16924 var valuesPerStack = {}; 16925 16926 helpers.each(datasets, function(dataset, datasetIndex) { 16927 var meta = chart.getDatasetMeta(datasetIndex); 16928 var key = [ 16929 meta.type, 16930 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined 16931 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), 16932 meta.stack 16933 ].join('.'); 16934 16935 if (valuesPerStack[key] === undefined) { 16936 valuesPerStack[key] = { 16937 positiveValues: [], 16938 negativeValues: [] 16939 }; 16940 } 16941 16942 // Store these per type 16943 var positiveValues = valuesPerStack[key].positiveValues; 16944 var negativeValues = valuesPerStack[key].negativeValues; 16945 16946 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 16947 helpers.each(dataset.data, function(rawValue, index) { 16948 var value = +me.getRightValue(rawValue); 16949 if (isNaN(value) || meta.data[index].hidden) { 16950 return; 16951 } 16952 16953 positiveValues[index] = positiveValues[index] || 0; 16954 negativeValues[index] = negativeValues[index] || 0; 16955 16956 if (opts.relativePoints) { 16957 positiveValues[index] = 100; 16958 } else if (value < 0) { 16959 negativeValues[index] += value; 16960 } else { 16961 positiveValues[index] += value; 16962 } 16963 }); 16964 } 16965 }); 16966 16967 helpers.each(valuesPerStack, function(valuesForType) { 16968 var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); 16969 var minVal = helpers.min(values); 16970 var maxVal = helpers.max(values); 16971 me.min = me.min === null ? minVal : Math.min(me.min, minVal); 16972 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); 16973 }); 16974 16975 } else { 16976 helpers.each(datasets, function(dataset, datasetIndex) { 16977 var meta = chart.getDatasetMeta(datasetIndex); 16978 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 16979 helpers.each(dataset.data, function(rawValue, index) { 16980 var value = +me.getRightValue(rawValue); 16981 if (isNaN(value) || meta.data[index].hidden) { 16982 return; 16983 } 16984 16985 if (me.min === null) { 16986 me.min = value; 16987 } else if (value < me.min) { 16988 me.min = value; 16989 } 16990 16991 if (me.max === null) { 16992 me.max = value; 16993 } else if (value > me.max) { 16994 me.max = value; 16995 } 16996 }); 16997 } 16998 }); 16999 } 17000 17001 me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; 17002 me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; 17003 17004 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero 17005 this.handleTickRangeOptions(); 17006 }, 17007 getTickLimit: function() { 17008 var maxTicks; 17009 var me = this; 17010 var tickOpts = me.options.ticks; 17011 17012 if (me.isHorizontal()) { 17013 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); 17014 } else { 17015 // The factor of 2 used to scale the font size has been experimentally determined.
17016 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); 17017 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); 17018 } 17019 17020 return maxTicks; 17021 }, 17022 // Called after the ticks are built. We need 17023 handleDirectionalChanges: function() { 17024 if (!this.isHorizontal()) { 17025 // We are in a vertical orientation. The top value is the highest. So reverse the array 17026 this.ticks.reverse(); 17027 } 17028 }, 17029 getLabelForIndex: function(index, datasetIndex) { 17030 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17031 }, 17032 // Utils 17033 getPixelForValue: function(value) { 17034 // This must be called after fit has been run so that 17035 // this.left, this.top, this.right, and this.bottom have been defined 17036 var me = this; 17037 var start = me.start; 17038 17039 var rightValue = +me.getRightValue(value); 17040 var pixel; 17041 var range = me.end - start; 17042 17043 if (me.isHorizontal()) { 17044 pixel = me.left + (me.width / range * (rightValue - start)); 17045 } else { 17046 pixel = me.bottom - (me.height / range * (rightValue - start)); 17047 } 17048 return pixel; 17049 }, 17050 getValueForPixel: function(pixel) { 17051 var me = this; 17052 var isHorizontal = me.isHorizontal(); 17053 var innerDimension = isHorizontal ? me.width : me.height; 17054 var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; 17055 return me.start + ((me.end - me.start) * offset); 17056 }, 17057 getPixelForTick: function(index) { 17058 return this.getPixelForValue(this.ticksAsNumbers[index]); 17059 } 17060 }); 17061 Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); 17062 17063}; 17064 17065},{"25":25,"34":34,"45":45}],55:[function(require,module,exports){ 17066'use strict'; 17067 17068var helpers = require(45); 17069 17070/** 17071 * Generate a set of linear ticks 17072 * @param generationOptions the options used to generate the ticks 17073 * @param dataRange the range of the data 17074 * @returns {Array<Number>} array of tick values 17075 */ 17076function generateTicks(generationOptions, dataRange) { 17077 var ticks = []; 17078 // To get a "nice" value for the tick spacing, we will use the appropriately named 17079 // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks 17080 // for details. 17081 17082 var spacing; 17083 if (generationOptions.stepSize && generationOptions.stepSize > 0) { 17084 spacing = generationOptions.stepSize; 17085 } else { 17086 var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); 17087 spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); 17088 } 17089 var niceMin = Math.floor(dataRange.min / spacing) * spacing; 17090 var niceMax = Math.ceil(dataRange.max / spacing) * spacing; 17091 17092 // If min, max and stepSize is set and they make an evenly spaced scale use it. 17093 if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { 17094 // If very close to our whole number, use it. 17095 if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { 17096 niceMin = generationOptions.min; 17097 niceMax = generationOptions.max; 17098 } 17099 } 17100 17101 var numSpaces = (niceMax - niceMin) / spacing; 17102 // If very close to our rounded value, use it. 17103 if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { 17104 numSpaces = Math.round(numSpaces); 17105 } else { 17106 numSpaces = Math.ceil(numSpaces); 17107 } 17108 17109 var precision = 1; 17110 if (spacing < 1) { 17111 precision = Math.pow(10, spacing.toString().length - 2); 17112 niceMin = Math.round(niceMin * precision) / precision; 17113 niceMax = Math.round(niceMax * precision) / precision; 17114 } 17115 ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); 17116 for (var j = 1; j < numSpaces; ++j) { 17117 ticks.push(Math.round((niceMin + j * spacing) * precision) / precision); 17118 } 17119 ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); 17120 17121 return ticks; 17122} 17123 17124 17125module.exports = function(Chart) { 17126 17127 var noop = helpers.noop; 17128 17129 Chart.LinearScaleBase = Chart.Scale.extend({ 17130 getRightValue: function(value) { 17131 if (typeof value === 'string') { 17132 return +value; 17133 } 17134 return Chart.Scale.prototype.getRightValue.call(this, value); 17135 }, 17136 17137 handleTickRangeOptions: function() { 17138 var me = this; 17139 var opts = me.options; 17140 var tickOpts = opts.ticks; 17141 17142 // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, 17143 // do nothing since that would make the chart weird. If the user really wants a weird chart 17144 // axis, they can manually override it 17145 if (tickOpts.beginAtZero) { 17146 var minSign = helpers.sign(me.min); 17147 var maxSign = helpers.sign(me.max); 17148 17149 if (minSign < 0 && maxSign < 0) { 17150 // move the top up to 0 17151 me.max = 0; 17152 } else if (minSign > 0 && maxSign > 0) { 17153 // move the bottom down to 0 17154 me.min = 0; 17155 } 17156 } 17157 17158 var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; 17159 var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; 17160 17161 if (tickOpts.min !== undefined) { 17162 me.min = tickOpts.min; 17163 } else if (tickOpts.suggestedMin !== undefined) { 17164 if (me.min === null) { 17165 me.min = tickOpts.suggestedMin; 17166 } else { 17167 me.min = Math.min(me.min, tickOpts.suggestedMin); 17168 } 17169 } 17170 17171 if (tickOpts.max !== undefined) { 17172 me.max = tickOpts.max; 17173 } else if (tickOpts.suggestedMax !== undefined) { 17174 if (me.max === null) { 17175 me.max = tickOpts.suggestedMax; 17176 } else { 17177 me.max = Math.max(me.max, tickOpts.suggestedMax); 17178 } 17179 } 17180 17181 if (setMin !== setMax) { 17182 // We set the min or the max but not both. 17183 // So ensure that our range is good 17184 // Inverted or 0 length range can happen when
17185 // ticks.min is set, and no datasets are visible 17186 if (me.min >= me.max) { 17187 if (setMin) { 17188 me.max = me.min + 1; 17189 } else { 17190 me.min = me.max - 1; 17191 } 17192 } 17193 } 17194 17195 if (me.min === me.max) { 17196 me.max++; 17197 17198 if (!tickOpts.beginAtZero) { 17199 me.min--; 17200 } 17201 } 17202 }, 17203 getTickLimit: noop, 17204 handleDirectionalChanges: noop, 17205 17206 buildTicks: function() { 17207 var me = this; 17208 var opts = me.options; 17209 var tickOpts = opts.ticks; 17210 17211 // Figure out what the max number of ticks we can support it is based on the size of 17212 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 17213 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 17214 // the graph. Make sure we always have at least 2 ticks 17215 var maxTicks = me.getTickLimit(); 17216 maxTicks = Math.max(2, maxTicks); 17217 17218 var numericGeneratorOptions = { 17219 maxTicks: maxTicks, 17220 min: tickOpts.min, 17221 max: tickOpts.max, 17222 stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) 17223 }; 17224 var ticks = me.ticks = generateTicks(numericGeneratorOptions, me); 17225 17226 me.handleDirectionalChanges(); 17227 17228 // At this point, we need to update our max and min given the tick values since we have expanded the 17229 // range of the scale 17230 me.max = helpers.max(ticks); 17231 me.min = helpers.min(ticks); 17232 17233 if (tickOpts.reverse) { 17234 ticks.reverse(); 17235 17236 me.start = me.max; 17237 me.end = me.min; 17238 } else { 17239 me.start = me.min; 17240 me.end = me.max; 17241 } 17242 }, 17243 convertTicksToLabels: function() { 17244 var me = this; 17245 me.ticksAsNumbers = me.ticks.slice(); 17246 me.zeroLineIndex = me.ticks.indexOf(0); 17247 17248 Chart.Scale.prototype.convertTicksToLabels.call(me); 17249 } 17250 }); 17251}; 17252 17253},{"45":45}],56:[function(require,module,exports){ 17254'use strict'; 17255 17256var helpers = require(45); 17257var Ticks = require(34); 17258 17259/** 17260 * Generate a set of logarithmic ticks 17261 * @param generationOptions the options used to generate the ticks 17262 * @param dataRange the range of the data 17263 * @returns {Array<Number>} array of tick values 17264 */ 17265function generateTicks(generationOptions, dataRange) { 17266 var ticks = []; 17267 var valueOrDefault = helpers.valueOrDefault; 17268 17269 // Figure out what the max number of ticks we can support it is based on the size of 17270 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 17271 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on 17272 // the graph 17273 var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); 17274 17275 var endExp = Math.floor(helpers.log10(dataRange.max)); 17276 var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); 17277 var exp, significand; 17278 17279 if (tickVal === 0) { 17280 exp = Math.floor(helpers.log10(dataRange.minNotZero)); 17281 significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); 17282 17283 ticks.push(tickVal); 17284 tickVal = significand * Math.pow(10, exp); 17285 } else { 17286 exp = Math.floor(helpers.log10(tickVal)); 17287 significand = Math.floor(tickVal / Math.pow(10, exp)); 17288 } 17289 var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1; 17290 17291 do { 17292 ticks.push(tickVal); 17293 17294 ++significand; 17295 if (significand === 10) { 17296 significand = 1; 17297 ++exp; 17298 precision = exp >= 0 ? 1 : precision; 17299 } 17300 17301 tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision; 17302 } while (exp < endExp || (exp === endExp && significand < endSignificand)); 17303 17304 var lastTick = valueOrDefault(generationOptions.max, tickVal); 17305 ticks.push(lastTick); 17306 17307 return ticks; 17308} 17309 17310 17311module.exports = function(Chart) { 17312 17313 var defaultConfig = { 17314 position: 'left', 17315 17316 // label settings 17317 ticks: { 17318 callback: Ticks.formatters.logarithmic 17319 } 17320 }; 17321 17322 var LogarithmicScale = Chart.Scale.extend({ 17323 determineDataLimits: function() { 17324 var me = this; 17325 var opts = me.options; 17326 var chart = me.chart; 17327 var data = chart.data; 17328 var datasets = data.datasets; 17329 var isHorizontal = me.isHorizontal(); 17330 function IDMatches(meta) { 17331 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; 17332 } 17333 17334 // Calculate Range 17335 me.min = null; 17336 me.max = null; 17337 me.minNotZero = null; 17338 17339 var hasStacks = opts.stacked; 17340 if (hasStacks === undefined) { 17341 helpers.each(datasets, function(dataset, datasetIndex) { 17342 if (hasStacks) { 17343 return; 17344 } 17345 17346 var meta = chart.getDatasetMeta(datasetIndex); 17347 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && 17348 meta.stack !== undefined) { 17349 hasStacks = true; 17350 } 17351 }); 17352 } 17353 17354 if (opts.stacked || hasStacks) { 17355 var valuesPerStack = {}; 17356 17357 helpers.each(datasets, function(dataset, datasetIndex) { 17358 var meta = chart.getDatasetMeta(datasetIndex); 17359 var key = [ 17360 meta.type, 17361 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined 17362 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), 17363 meta.stack 17364 ].join('.'); 17365 17366 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17367 if (valuesPerStack[key] === undefined) { 17368 valuesPerStack[key] = []; 17369 } 17370 17371 helpers.each(dataset.data, function(rawValue, index) { 17372 var values = valuesPerStack[key]; 17373 var value = +me.getRightValue(rawValue); 17374 // invalid, hidden and negative values are ignored 17375 if (isNaN(value) || meta.data[index].hidden || value < 0) { 17376 return; 17377 } 17378 values[index] = values[index] || 0; 17379 values[index] += value; 17380 }); 17381 } 17382 }); 17383 17384 helpers.each(valuesPerStack, function(valuesForType) { 17385 if (valuesForType.length > 0) { 17386 var minVal = helpers.min(valuesForType); 17387 var maxVal = helpers.max(valuesForType); 17388 me.min = me.min === null ? minVal : Math.min(me.min, minVal); 17389 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); 17390 } 17391 }); 17392 17393 } else { 17394 helpers.each(datasets, function(dataset, datasetIndex) { 17395 var meta = chart.getDatasetMeta(datasetIndex); 17396 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { 17397 helpers.each(dataset.data, function(rawValue, index) { 17398 var value = +me.getRightValue(rawValue); 17399 // invalid, hidden and negative values are ignored 17400 if (isNaN(value) || meta.data[index].hidden || value < 0) { 17401 return; 17402 } 17403 17404 if (me.min === null) { 17405 me.min = value; 17406 } else if (value < me.min) { 17407 me.min = value; 17408 } 17409 17410 if (me.max === null) { 17411 me.max = value; 17412 } else if (value > me.max) { 17413 me.max = value; 17414 } 17415 17416 if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { 17417 me.minNotZero = value; 17418 } 17419 }); 17420 } 17421 }); 17422 } 17423 17424 // Common base implementation to handle ticks.min, ticks.max 17425 this.handleTickRangeOptions(); 17426 }, 17427 handleTickRangeOptions: function() { 17428 var me = this; 17429 var opts = me.options; 17430 var tickOpts = opts.ticks; 17431 var valueOrDefault = helpers.valueOrDefault; 17432 var DEFAULT_MIN = 1; 17433 var DEFAULT_MAX = 10; 17434 17435 me.min = valueOrDefault(tickOpts.min, me.min); 17436 me.max = valueOrDefault(tickOpts.max, me.max); 17437 17438 if (me.min === me.max) { 17439 if (me.min !== 0 && me.min !== null) {
17440 me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); 17441 me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); 17442 } else { 17443 me.min = DEFAULT_MIN; 17444 me.max = DEFAULT_MAX; 17445 } 17446 } 17447 if (me.min === null) { 17448 me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1); 17449 } 17450 if (me.max === null) { 17451 me.max = me.min !== 0 17452 ? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1) 17453 : DEFAULT_MAX; 17454 } 17455 if (me.minNotZero === null) { 17456 if (me.min > 0) { 17457 me.minNotZero = me.min; 17458 } else if (me.max < 1) { 17459 me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max))); 17460 } else { 17461 me.minNotZero = DEFAULT_MIN; 17462 } 17463 } 17464 }, 17465 buildTicks: function() { 17466 var me = this; 17467 var opts = me.options; 17468 var tickOpts = opts.ticks; 17469 var reverse = !me.isHorizontal(); 17470 17471 var generationOptions = { 17472 min: tickOpts.min, 17473 max: tickOpts.max 17474 }; 17475 var ticks = me.ticks = generateTicks(generationOptions, me); 17476 17477 // At this point, we need to update our max and min given the tick values since we have expanded the 17478 // range of the scale 17479 me.max = helpers.max(ticks); 17480 me.min = helpers.min(ticks); 17481 17482 if (tickOpts.reverse) { 17483 reverse = !reverse; 17484 me.start = me.max; 17485 me.end = me.min; 17486 } else { 17487 me.start = me.min; 17488 me.end = me.max; 17489 } 17490 if (reverse) { 17491 ticks.reverse(); 17492 } 17493 }, 17494 convertTicksToLabels: function() { 17495 this.tickValues = this.ticks.slice(); 17496 17497 Chart.Scale.prototype.convertTicksToLabels.call(this); 17498 }, 17499 // Get the correct tooltip label 17500 getLabelForIndex: function(index, datasetIndex) { 17501 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17502 }, 17503 getPixelForTick: function(index) { 17504 return this.getPixelForValue(this.tickValues[index]); 17505 }, 17506 /** 17507 * Returns the value of the first tick. 17508 * @param {Number} value - The minimum not zero value. 17509 * @return {Number} The first tick value. 17510 * @private 17511 */ 17512 _getFirstTickValue: function(value) { 17513 var exp = Math.floor(helpers.log10(value)); 17514 var significand = Math.floor(value / Math.pow(10, exp)); 17515 17516 return significand * Math.pow(10, exp); 17517 }, 17518 getPixelForValue: function(value) { 17519 var me = this; 17520 var reverse = me.options.ticks.reverse; 17521 var log10 = helpers.log10; 17522 var firstTickValue = me._getFirstTickValue(me.minNotZero); 17523 var offset = 0; 17524 var innerDimension, pixel, start, end, sign; 17525 17526 value = +me.getRightValue(value); 17527 if (reverse) { 17528 start = me.end; 17529 end = me.start; 17530 sign = -1; 17531 } else { 17532 start = me.start; 17533 end = me.end; 17534 sign = 1; 17535 } 17536 if (me.isHorizontal()) { 17537 innerDimension = me.width; 17538 pixel = reverse ? me.right : me.left; 17539 } else { 17540 innerDimension = me.height; 17541 sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0) 17542 pixel = reverse ? me.top : me.bottom; 17543 } 17544 if (value !== start) { 17545 if (start === 0) { // include zero tick 17546 offset = helpers.getValueOrDefault( 17547 me.options.ticks.fontSize, 17548 Chart.defaults.global.defaultFontSize 17549 ); 17550 innerDimension -= offset; 17551 start = firstTickValue; 17552 } 17553 if (value !== 0) { 17554 offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start)); 17555 } 17556 pixel += sign * offset; 17557 } 17558 return pixel; 17559 }, 17560 getValueForPixel: function(pixel) { 17561 var me = this; 17562 var reverse = me.options.ticks.reverse; 17563 var log10 = helpers.log10; 17564 var firstTickValue = me._getFirstTickValue(me.minNotZero); 17565 var innerDimension, start, end, value; 17566 17567 if (reverse) { 17568 start = me.end; 17569 end = me.start; 17570 } else { 17571 start = me.start; 17572 end = me.end; 17573 } 17574 if (me.isHorizontal()) { 17575 innerDimension = me.width; 17576 value = reverse ? me.right - pixel : pixel - me.left; 17577 } else { 17578 innerDimension = me.height; 17579 value = reverse ? pixel - me.top : me.bottom - pixel; 17580 } 17581 if (value !== start) { 17582 if (start === 0) { // include zero tick 17583 var offset = helpers.getValueOrDefault( 17584 me.options.ticks.fontSize, 17585 Chart.defaults.global.defaultFontSize 17586 ); 17587 value -= offset; 17588 innerDimension -= offset; 17589 start = firstTickValue; 17590 } 17591 value *= log10(end) - log10(start); 17592 value /= innerDimension; 17593 value = Math.pow(10, log10(start) + value); 17594 } 17595 return value; 17596 } 17597 }); 17598 Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); 17599 17600}; 17601 17602},{"34":34,"45":45}],57:[function(require,module,exports){ 17603'use strict'; 17604 17605var defaults = require(25); 17606var helpers = require(45); 17607var Ticks = require(34); 17608 17609module.exports = function(Chart) { 17610 17611 var globalDefaults = defaults.global; 17612 17613 var defaultConfig = { 17614 display: true, 17615 17616 // Boolean - Whether to animate scaling the chart from the centre 17617 animate: true, 17618 position: 'chartArea', 17619 17620 angleLines: { 17621 display: true, 17622 color: 'rgba(0, 0, 0, 0.1)', 17623 lineWidth: 1 17624 }, 17625 17626 gridLines: { 17627 circular: false 17628 }, 17629 17630 // label settings 17631 ticks: { 17632 // Boolean - Show a backdrop to the scale label 17633 showLabelBackdrop: true, 17634 17635 // String - The colour of the label backdrop 17636 backdropColor: 'rgba(255,255,255,0.75)', 17637 17638 // Number - The backdrop padding above & below the label in pixels 17639 backdropPaddingY: 2, 17640 17641 // Number - The backdrop padding to the side of the label in pixels 17642 backdropPaddingX: 2, 17643 17644 callback: Ticks.formatters.linear 17645 }, 17646 17647 pointLabels: { 17648 // Boolean - if true, show point labels 17649 display: true, 17650 17651 // Number - Point label font size in pixels 17652 fontSize: 10, 17653 17654 // Function - Used to convert point labels 17655 callback: function(label) { 17656 return label; 17657 } 17658 } 17659 }; 17660 17661 function getValueCount(scale) { 17662 var opts = scale.options; 17663 return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; 17664 } 17665 17666 function getPointLabelFontOptions(scale) { 17667 var pointLabelOptions = scale.options.pointLabels; 17668 var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); 17669 var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); 17670 var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); 17671 var font = helpers.fontString(fontSize, fontStyle, fontFamily); 17672 17673 return { 17674 size: fontSize, 17675 style: fontStyle, 17676 family: fontFamily, 17677 font: font 17678 }; 17679 } 17680 17681 function measureLabelSize(ctx, fontSize, label) { 17682 if (helpers.isArray(label)) { 17683 return { 17684 w: helpers.longestText(ctx, ctx.font, label), 17685 h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) 17686 }; 17687 } 17688 17689 return { 17690 w: ctx.measureText(label).width, 17691 h: fontSize 17692 }; 17693 } 17694 17695 function determineLimits(angle, pos, size, min, max) { 17696 if (angle === min || angle === max) { 17697 return { 17698 start: pos - (size / 2), 17699 end: pos + (size / 2) 17700 }; 17701 } else if (angle < min || angle > max) { 17702 return { 17703 start: pos - size - 5, 17704 end: pos 17705 }; 17706 } 17707 17708 return { 17709 start: pos, 17710 end: pos + size + 5 17711 }; 17712 } 17713 17714 /** 17715 * Helper function to fit a radial linear scale with point labels 17716 */ 17717 function fitWithPointLabels(scale) { 17718 /* 17719 * Right, this is really confusing and there is a lot of maths going on here 17720 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 17721 * 17722 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif 17723 * 17724 * Solution: 17725 * 17726 * We assume the radius of the polygon is half the size of the canvas at first 17727 * at each index we check if the text overlaps. 17728 * 17729 * Where it does, we store that angle and that index. 17730 * 17731 * After finding the largest index and angle we calculate how much we need to remove 17732 * from the shape radius to move the point inwards by that x. 17733 * 17734 * We average the left and right distances to get the maximum shape radius that can fit in the box 17735 * along with labels. 17736 * 17737 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion 17738 * on each side, removing that from the size, halving it and adding the left x protrusion width. 17739 * 17740 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels 17741 * and position it in the most space efficient manner 17742 * 17743 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif 17744 */ 17745 17746 var plFont = getPointLabelFontOptions(scale); 17747 17748 // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. 17749 // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points 17750 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); 17751 var furthestLimits = { 17752 r: scale.width, 17753 l: 0, 17754 t: scale.height, 17755 b: 0 17756 }; 17757 var furthestAngles = {}; 17758 var i, textSize, pointPosition; 17759 17760 scale.ctx.font = plFont.font; 17761 scale._pointLabelSizes = []; 17762 17763 var valueCount = getValueCount(scale); 17764 for (i = 0; i < valueCount; i++) { 17765 pointPosition = scale.getPointPosition(i, largestPossibleRadius); 17766 textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); 17767 scale._pointLabelSizes[i] = textSize; 17768 17769 // Add quarter circle to make degree 0 mean top of circle 17770 var angleRadians = scale.getIndexAngle(i); 17771 var angle = helpers.toDegrees(angleRadians) % 360; 17772 var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); 17773 var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); 17774 17775 if (hLimits.start < furthestLimits.l) { 17776 furthestLimits.l = hLimits.start; 17777 furthestAngles.l = angleRadians; 17778 } 17779 17780 if (hLimits.end > furthestLimits.r) { 17781 furthestLimits.r = hLimits.end; 17782 furthestAngles.r = angleRadians; 17783 } 17784 17785 if (vLimits.start < furthestLimits.t) { 17786 furthestLimits.t = vLimits.start; 17787 furthestAngles.t = angleRadians; 17788 } 17789 17790 if (vLimits.end > furthestLimits.b) { 17791 furthestLimits.b = vLimits.end; 17792 furthestAngles.b = angleRadians; 17793 } 17794 } 17795 17796 scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); 17797 } 17798 17799 /** 17800 * Helper function to fit a radial linear scale with no point labels 17801 */ 17802 function fit(scale) { 17803 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); 17804 scale.drawingArea = Math.round(largestPossibleRadius); 17805 scale.setCenterPoint(0, 0, 0, 0); 17806 } 17807 17808 function getTextAlignForAngle(angle) { 17809 if (angle === 0 || angle === 180) { 17810 return 'center'; 17811 } else if (angle < 180) { 17812 return 'left'; 17813 } 17814 17815 return 'right'; 17816 } 17817 17818 function fillText(ctx, text, position, fontSize) { 17819 if (helpers.isArray(text)) { 17820 var y = position.y; 17821 var spacing = 1.5 * fontSize; 17822 17823 for (var i = 0; i < text.length; ++i) { 17824 ctx.fillText(text[i], position.x, y); 17825 y += spacing; 17826 } 17827 } else { 17828 ctx.fillText(text, position.x, position.y); 17829 } 17830 } 17831 17832 function adjustPointPositionForLabelHeight(angle, textSize, position) { 17833 if (angle === 90 || angle === 270) { 17834 position.y -= (textSize.h / 2); 17835 } else if (angle > 270 || angle < 90) { 17836 position.y -= textSize.h; 17837 } 17838 } 17839 17840 function drawPointLabels(scale) { 17841 var ctx = scale.ctx; 17842 var opts = scale.options; 17843 var angleLineOpts = opts.angleLines; 17844 var pointLabelOpts = opts.pointLabels; 17845 17846 ctx.lineWidth = angleLineOpts.lineWidth; 17847 ctx.strokeStyle = angleLineOpts.color; 17848 17849 var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); 17850 17851 // Point Label Font 17852 var plFont = getPointLabelFontOptions(scale); 17853 17854 ctx.textBaseline = 'top'; 17855 17856 for (var i = getValueCount(scale) - 1; i >= 0; i--) { 17857 if (angleLineOpts.display) { 17858 var outerPosition = scale.getPointPosition(i, outerDistance); 17859 ctx.beginPath(); 17860 ctx.moveTo(scale.xCenter, scale.yCenter); 17861 ctx.lineTo(outerPosition.x, outerPosition.y); 17862 ctx.stroke(); 17863 ctx.closePath(); 17864 } 17865 17866 if (pointLabelOpts.display) { 17867 // Extra 3px out for some label spacing 17868 var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); 17869 17870 // Keep this in loop since we may support array properties here 17871 var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor); 17872 ctx.font = plFont.font; 17873 ctx.fillStyle = pointLabelFontColor; 17874 17875 var angleRadians = scale.getIndexAngle(i); 17876 var angle = helpers.toDegrees(angleRadians); 17877 ctx.textAlign = getTextAlignForAngle(angle); 17878 adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); 17879 fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); 17880 } 17881 } 17882 } 17883 17884 function drawRadiusLine(scale, gridLineOpts, radius, index) { 17885 var ctx = scale.ctx; 17886 ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); 17887 ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); 17888 17889 if (scale.options.gridLines.circular) { 17890 // Draw circular arcs between the points 17891 ctx.beginPath(); 17892 ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); 17893 ctx.closePath(); 17894 ctx.stroke(); 17895 } else { 17896 // Draw straight lines connecting each index 17897 var valueCount = getValueCount(scale); 17898
17899 if (valueCount === 0) { 17900 return; 17901 } 17902 17903 ctx.beginPath(); 17904 var pointPosition = scale.getPointPosition(0, radius); 17905 ctx.moveTo(pointPosition.x, pointPosition.y); 17906 17907 for (var i = 1; i < valueCount; i++) { 17908 pointPosition = scale.getPointPosition(i, radius); 17909 ctx.lineTo(pointPosition.x, pointPosition.y); 17910 } 17911 17912 ctx.closePath(); 17913 ctx.stroke(); 17914 } 17915 } 17916 17917 function numberOrZero(param) { 17918 return helpers.isNumber(param) ? param : 0; 17919 } 17920 17921 var LinearRadialScale = Chart.LinearScaleBase.extend({ 17922 setDimensions: function() { 17923 var me = this; 17924 var opts = me.options; 17925 var tickOpts = opts.ticks; 17926 // Set the unconstrained dimension before label rotation 17927 me.width = me.maxWidth; 17928 me.height = me.maxHeight; 17929 me.xCenter = Math.round(me.width / 2); 17930 me.yCenter = Math.round(me.height / 2); 17931 17932 var minSize = helpers.min([me.height, me.width]); 17933 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 17934 me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); 17935 }, 17936 determineDataLimits: function() { 17937 var me = this; 17938 var chart = me.chart; 17939 var min = Number.POSITIVE_INFINITY; 17940 var max = Number.NEGATIVE_INFINITY; 17941 17942 helpers.each(chart.data.datasets, function(dataset, datasetIndex) { 17943 if (chart.isDatasetVisible(datasetIndex)) { 17944 var meta = chart.getDatasetMeta(datasetIndex); 17945 17946 helpers.each(dataset.data, function(rawValue, index) { 17947 var value = +me.getRightValue(rawValue); 17948 if (isNaN(value) || meta.data[index].hidden) { 17949 return; 17950 } 17951 17952 min = Math.min(value, min); 17953 max = Math.max(value, max); 17954 }); 17955 } 17956 }); 17957 17958 me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); 17959 me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); 17960 17961 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero 17962 me.handleTickRangeOptions(); 17963 }, 17964 getTickLimit: function() { 17965 var tickOpts = this.options.ticks; 17966 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 17967 return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); 17968 }, 17969 convertTicksToLabels: function() { 17970 var me = this; 17971 17972 Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); 17973 17974 // Point labels 17975 me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); 17976 }, 17977 getLabelForIndex: function(index, datasetIndex) { 17978 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); 17979 }, 17980 fit: function() { 17981 if (this.options.pointLabels.display) { 17982 fitWithPointLabels(this); 17983 } else { 17984 fit(this); 17985 } 17986 }, 17987 /** 17988 * Set radius reductions and determine new radius and center point 17989 * @private 17990 */ 17991 setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { 17992 var me = this; 17993 var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); 17994 var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); 17995 var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); 17996 var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); 17997 17998 radiusReductionLeft = numberOrZero(radiusReductionLeft); 17999 radiusReductionRight = numberOrZero(radiusReductionRight); 18000 radiusReductionTop = numberOrZero(radiusReductionTop); 18001 radiusReductionBottom = numberOrZero(radiusReductionBottom); 18002 18003 me.drawingArea = Math.min( 18004 Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), 18005 Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); 18006 me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); 18007 }, 18008 setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { 18009 var me = this; 18010 var maxRight = me.width - rightMovement - me.drawingArea; 18011 var maxLeft = leftMovement + me.drawingArea; 18012 var maxTop = topMovement + me.drawingArea; 18013 var maxBottom = me.height - bottomMovement - me.drawingArea; 18014 18015 me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); 18016 me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); 18017 }, 18018 18019 getIndexAngle: function(index) { 18020 var angleMultiplier = (Math.PI * 2) / getValueCount(this); 18021 var startAngle = this.chart.options && this.chart.options.startAngle ? 18022 this.chart.options.startAngle : 18023 0; 18024 18025 var startAngleRadians = startAngle * Math.PI * 2 / 360; 18026 18027 // Start from the top instead of right, so remove a quarter of the circle 18028 return index * angleMultiplier + startAngleRadians; 18029 }, 18030 getDistanceFromCenterForValue: function(value) { 18031 var me = this; 18032 18033 if (value === null) { 18034 return 0; // null always in center 18035 } 18036 18037 // Take into account half font size + the yPadding of the top value 18038 var scalingFactor = me.drawingArea / (me.max - me.min); 18039 if (me.options.ticks.reverse) { 18040 return (me.max - value) * scalingFactor; 18041 } 18042 return (value - me.min) * scalingFactor; 18043 }, 18044 getPointPosition: function(index, distanceFromCenter) { 18045 var me = this; 18046 var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); 18047 return { 18048 x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, 18049 y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter 18050 }; 18051 }, 18052 getPointPositionForValue: function(index, value) { 18053 return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); 18054 }, 18055 18056 getBasePosition: function() { 18057 var me = this; 18058 var min = me.min; 18059 var max = me.max; 18060 18061 return me.getPointPositionForValue(0, 18062 me.beginAtZero ? 0 : 18063 min < 0 && max < 0 ? max : 18064 min > 0 && max > 0 ? min : 18065 0); 18066 }, 18067 18068 draw: function() { 18069 var me = this; 18070 var opts = me.options; 18071 var gridLineOpts = opts.gridLines; 18072 var tickOpts = opts.ticks; 18073 var valueOrDefault = helpers.valueOrDefault; 18074 18075 if (opts.display) { 18076 var ctx = me.ctx; 18077 var startAngle = this.getIndexAngle(0); 18078 18079 // Tick Font
18080 var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); 18081 var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); 18082 var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); 18083 var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); 18084 18085 helpers.each(me.ticks, function(label, index) { 18086 // Don't draw a centre value (if it is minimum) 18087 if (index > 0 || tickOpts.reverse) { 18088 var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); 18089 18090 // Draw circular lines around the scale 18091 if (gridLineOpts.display && index !== 0) { 18092 drawRadiusLine(me, gridLineOpts, yCenterOffset, index); 18093 } 18094 18095 if (tickOpts.display) { 18096 var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); 18097 ctx.font = tickLabelFont; 18098 18099 ctx.save(); 18100 ctx.translate(me.xCenter, me.yCenter); 18101 ctx.rotate(startAngle); 18102 18103 if (tickOpts.showLabelBackdrop) { 18104 var labelWidth = ctx.measureText(label).width; 18105 ctx.fillStyle = tickOpts.backdropColor; 18106 ctx.fillRect( 18107 -labelWidth / 2 - tickOpts.backdropPaddingX, 18108 -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, 18109 labelWidth + tickOpts.backdropPaddingX * 2, 18110 tickFontSize + tickOpts.backdropPaddingY * 2 18111 ); 18112 } 18113 18114 ctx.textAlign = 'center'; 18115 ctx.textBaseline = 'middle'; 18116 ctx.fillStyle = tickFontColor; 18117 ctx.fillText(label, 0, -yCenterOffset); 18118 ctx.restore(); 18119 } 18120 } 18121 }); 18122 18123 if (opts.angleLines.display || opts.pointLabels.display) { 18124 drawPointLabels(me); 18125 } 18126 } 18127 } 18128 }); 18129 Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); 18130 18131}; 18132 18133},{"25":25,"34":34,"45":45}],58:[function(require,module,exports){ 18134/* global window: false */ 18135'use strict'; 18136 18137var moment = require(6); 18138moment = typeof moment === 'function' ? moment : window.moment; 18139 18140var defaults = require(25); 18141var helpers = require(45); 18142 18143// Integer constants are from the ES6 spec. 18144var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; 18145var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; 18146 18147var INTERVALS = { 18148 millisecond: { 18149 common: true, 18150 size: 1, 18151 steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] 18152 }, 18153 second: { 18154 common: true, 18155 size: 1000, 18156 steps: [1, 2, 5, 10, 30] 18157 }, 18158 minute: { 18159 common: true, 18160 size: 60000, 18161 steps: [1, 2, 5, 10, 30] 18162 }, 18163 hour: { 18164 common: true, 18165 size: 3600000, 18166 steps: [1, 2, 3, 6, 12] 18167 }, 18168 day: { 18169 common: true, 18170 size: 86400000, 18171 steps: [1, 2, 5] 18172 }, 18173 week: { 18174 common: false, 18175 size: 604800000, 18176 steps: [1, 2, 3, 4] 18177 }, 18178 month: { 18179 common: true, 18180 size: 2.628e9, 18181 steps: [1, 2, 3] 18182 }, 18183 quarter: { 18184 common: false, 18185 size: 7.884e9, 18186 steps: [1, 2, 3, 4] 18187 }, 18188 year: { 18189 common: true, 18190 size: 3.154e10 18191 } 18192}; 18193 18194var UNITS = Object.keys(INTERVALS); 18195 18196function sorter(a, b) { 18197 return a - b; 18198} 18199 18200function arrayUnique(items) { 18201 var hash = {}; 18202 var out = []; 18203 var i, ilen, item; 18204 18205 for (i = 0, ilen = items.length; i < ilen; ++i) { 18206 item = items[i]; 18207 if (!hash[item]) { 18208 hash[item] = true; 18209 out.push(item); 18210 } 18211 } 18212 18213 return out; 18214} 18215 18216/** 18217 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position 18218 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is 18219 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other 18220 * extremity (left + width or top + height). Note that it would be more optimized to directly 18221 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need 18222 * to create the lookup table. The table ALWAYS contains at least two items: min and max. 18223 * 18224 * @param {Number[]} timestamps - timestamps sorted from lowest to highest. 18225 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min 18226 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. 18227 * If 'series', timestamps will be positioned at the same distance from each other. In this 18228 * case, only timestamps that break the time linearity are registered, meaning that in the 18229 * best case, all timestamps are linear, the table contains only min and max. 18230 */ 18231function buildLookupTable(timestamps, min, max, distribution) { 18232 if (distribution === 'linear' || !timestamps.length) { 18233 return [ 18234 {time: min, pos: 0}, 18235 {time: max, pos: 1} 18236 ]; 18237 } 18238 18239 var table = []; 18240 var items = [min]; 18241 var i, ilen, prev, curr, next; 18242 18243 for (i = 0, ilen = timestamps.length; i < ilen; ++i) { 18244 curr = timestamps[i]; 18245 if (curr > min && curr < max) { 18246 items.push(curr); 18247 } 18248 } 18249 18250 items.push(max); 18251 18252 for (i = 0, ilen = items.length; i < ilen; ++i) { 18253 next = items[i + 1]; 18254 prev = items[i - 1]; 18255 curr = items[i]; 18256 18257 // only add points that breaks the scale linearity 18258 if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { 18259 table.push({time: curr, pos: i / (ilen - 1)}); 18260 } 18261 } 18262 18263 return table; 18264} 18265 18266// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ 18267function lookup(table, key, value) { 18268 var lo = 0; 18269 var hi = table.length - 1; 18270 var mid, i0, i1; 18271 18272 while (lo >= 0 && lo <= hi) { 18273 mid = (lo + hi) >> 1; 18274 i0 = table[mid - 1] || null; 18275 i1 = table[mid]; 18276 18277 if (!i0) { 18278 // given value is outside table (before first item) 18279 return {lo: null, hi: i1}; 18280 } else if (i1[key] < value) { 18281 lo = mid + 1; 18282 } else if (i0[key] > value) {
18283 hi = mid - 1; 18284 } else { 18285 return {lo: i0, hi: i1}; 18286 } 18287 } 18288 18289 // given value is outside table (after last item) 18290 return {lo: i1, hi: null}; 18291} 18292 18293/** 18294 * Linearly interpolates the given source `value` using the table items `skey` values and 18295 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') 18296 * returns the position for a timestamp equal to 42. If value is out of bounds, values at 18297 * index [0, 1] or [n - 1, n] are used for the interpolation. 18298 */ 18299function interpolate(table, skey, sval, tkey) { 18300 var range = lookup(table, skey, sval); 18301 18302 // Note: the lookup table ALWAYS contains at least 2 items (min and max) 18303 var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; 18304 var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; 18305 18306 var span = next[skey] - prev[skey]; 18307 var ratio = span ? (sval - prev[skey]) / span : 0; 18308 var offset = (next[tkey] - prev[tkey]) * ratio; 18309 18310 return prev[tkey] + offset; 18311} 18312 18313/** 18314 * Convert the given value to a moment object using the given time options. 18315 * @see http://momentjs.com/docs/#/parsing/ 18316 */ 18317function momentify(value, options) { 18318 var parser = options.parser; 18319 var format = options.parser || options.format; 18320 18321 if (typeof parser === 'function') { 18322 return parser(value); 18323 } 18324 18325 if (typeof value === 'string' && typeof format === 'string') { 18326 return moment(value, format); 18327 } 18328 18329 if (!(value instanceof moment)) { 18330 value = moment(value); 18331 } 18332 18333 if (value.isValid()) { 18334 return value; 18335 } 18336 18337 // Labels are in an incompatible moment format and no `parser` has been provided. 18338 // The user might still use the deprecated `format` option to convert his inputs. 18339 if (typeof format === 'function') { 18340 return format(value); 18341 } 18342 18343 return value; 18344} 18345 18346function parse(input, scale) { 18347 if (helpers.isNullOrUndef(input)) { 18348 return null; 18349 } 18350 18351 var options = scale.options.time; 18352 var value = momentify(scale.getRightValue(input), options); 18353 if (!value.isValid()) { 18354 return null; 18355 } 18356 18357 if (options.round) { 18358 value.startOf(options.round); 18359 } 18360 18361 return value.valueOf(); 18362} 18363 18364/** 18365 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks 18366 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. 18367 */ 18368function determineStepSize(min, max, unit, capacity) { 18369 var range = max - min; 18370 var interval = INTERVALS[unit]; 18371 var milliseconds = interval.size; 18372 var steps = interval.steps; 18373 var i, ilen, factor; 18374 18375 if (!steps) { 18376 return Math.ceil(range / (capacity * milliseconds)); 18377 } 18378 18379 for (i = 0, ilen = steps.length; i < ilen; ++i) { 18380 factor = steps[i]; 18381 if (Math.ceil(range / (milliseconds * factor)) <= capacity) { 18382 break; 18383 } 18384 } 18385 18386 return factor; 18387} 18388 18389/** 18390 * Figures out what unit results in an appropriate number of auto-generated ticks 18391 */ 18392function determineUnitForAutoTicks(minUnit, min, max, capacity) { 18393 var ilen = UNITS.length; 18394 var i, interval, factor; 18395 18396 for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { 18397 interval = INTERVALS[UNITS[i]]; 18398 factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; 18399 18400 if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { 18401 return UNITS[i]; 18402 } 18403 } 18404 18405 return UNITS[ilen - 1]; 18406} 18407 18408/** 18409 * Figures out what unit to format a set of ticks with 18410 */ 18411function determineUnitForFormatting(ticks, minUnit, min, max) { 18412 var duration = moment.duration(moment(max).diff(moment(min))); 18413 var ilen = UNITS.length; 18414 var i, unit; 18415 18416 for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { 18417 unit = UNITS[i]; 18418 if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) { 18419 return unit; 18420 } 18421 } 18422 18423 return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; 18424} 18425 18426function determineMajorUnit(unit) { 18427 for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { 18428 if (INTERVALS[UNITS[i]].common) { 18429 return UNITS[i]; 18430 } 18431 } 18432} 18433 18434/** 18435 * Generates a maximum of `capacity` timestamps between min and max, rounded to the 18436 * `minor` unit, aligned on the `major` unit and using the given scale time `options`. 18437 * Important: this method can return ticks outside the min and max range, it's the 18438 * responsibility of the calling code to clamp values if needed. 18439 */ 18440function generate(min, max, capacity, options) { 18441 var timeOpts = options.time; 18442 var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); 18443 var major = determineMajorUnit(minor); 18444 var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); 18445 var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
18446 var majorTicksEnabled = options.ticks.major.enabled; 18447 var interval = INTERVALS[minor]; 18448 var first = moment(min); 18449 var last = moment(max); 18450 var ticks = []; 18451 var time; 18452 18453 if (!stepSize) { 18454 stepSize = determineStepSize(min, max, minor, capacity); 18455 } 18456 18457 // For 'week' unit, handle the first day of week option 18458 if (weekday) { 18459 first = first.isoWeekday(weekday); 18460 last = last.isoWeekday(weekday); 18461 } 18462 18463 // Align first/last ticks on unit 18464 first = first.startOf(weekday ? 'day' : minor); 18465 last = last.startOf(weekday ? 'day' : minor); 18466 18467 // Make sure that the last tick include max 18468 if (last < max) { 18469 last.add(1, minor); 18470 } 18471 18472 time = moment(first); 18473 18474 if (majorTicksEnabled && major && !weekday && !timeOpts.round) { 18475 // Align the first tick on the previous `minor` unit aligned on the `major` unit: 18476 // we first aligned time on the previous `major` unit then add the number of full 18477 // stepSize there is between first and the previous major time. 18478 time.startOf(major); 18479 time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); 18480 } 18481 18482 for (; time < last; time.add(stepSize, minor)) { 18483 ticks.push(+time); 18484 } 18485 18486 ticks.push(+time); 18487 18488 return ticks; 18489} 18490 18491/** 18492 * Returns the right and left offsets from edges in the form of {left, right}. 18493 * Offsets are added when the `offset` option is true. 18494 */ 18495function computeOffsets(table, ticks, min, max, options) { 18496 var left = 0; 18497 var right = 0; 18498 var upper, lower; 18499 18500 if (options.offset && ticks.length) { 18501 if (!options.time.min) { 18502 upper = ticks.length > 1 ? ticks[1] : max; 18503 lower = ticks[0]; 18504 left = ( 18505 interpolate(table, 'time', upper, 'pos') - 18506 interpolate(table, 'time', lower, 'pos') 18507 ) / 2; 18508 } 18509 if (!options.time.max) { 18510 upper = ticks[ticks.length - 1]; 18511 lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; 18512 right = ( 18513 interpolate(table, 'time', upper, 'pos') - 18514 interpolate(table, 'time', lower, 'pos') 18515 ) / 2; 18516 } 18517 } 18518 18519 return {left: left, right: right}; 18520} 18521 18522function ticksFromTimestamps(values, majorUnit) { 18523 var ticks = []; 18524 var i, ilen, value, major; 18525 18526 for (i = 0, ilen = values.length; i < ilen; ++i) { 18527 value = values[i]; 18528 major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; 18529 18530 ticks.push({ 18531 value: value, 18532 major: major 18533 }); 18534 } 18535 18536 return ticks; 18537} 18538 18539function determineLabelFormat(data, timeOpts) { 18540 var i, momentDate, hasTime; 18541 var ilen = data.length; 18542 18543 // find the label with the most parts (milliseconds, minutes, etc.) 18544 // format all labels with the same level of detail as the most specific label 18545 for (i = 0; i < ilen; i++) { 18546 momentDate = momentify(data[i], timeOpts); 18547 if (momentDate.millisecond() !== 0) { 18548 return 'MMM D, YYYY h:mm:ss.SSS a'; 18549 } 18550 if (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) { 18551 hasTime = true; 18552 } 18553 } 18554 if (hasTime) { 18555 return 'MMM D, YYYY h:mm:ss a'; 18556 } 18557 return 'MMM D, YYYY'; 18558} 18559 18560module.exports = function(Chart) { 18561 18562 var defaultConfig = { 18563 position: 'bottom', 18564 18565 /** 18566 * Data distribution along the scale: 18567 * - 'linear': data are spread according to their time (distances can vary), 18568 * - 'series': data are spread at the same distance from each other. 18569 * @see https://github.com/chartjs/Chart.js/pull/4507 18570 * @since 2.7.0 18571 */ 18572 distribution: 'linear', 18573 18574 /** 18575 * Scale boundary strategy (bypassed by min/max time options) 18576 * - `data`: make sure data are fully visible, ticks outside are removed 18577 * - `ticks`: make sure ticks are fully visible, data outside are truncated 18578 * @see https://github.com/chartjs/Chart.js/pull/4556 18579 * @since 2.7.0 18580 */ 18581 bounds: 'data', 18582 18583 time: { 18584 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 18585 format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ 18586 unit: false, // false == automatic or override with week, month, year, etc. 18587 round: false, // none, or override with week, month, year, etc. 18588 displayFormat: false, // DEPRECATED 18589 isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ 18590 minUnit: 'millisecond', 18591 18592 // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ 18593 displayFormats: { 18594 millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, 18595 second: 'h:mm:ss a', // 11:20:01 AM 18596 minute: 'h:mm a', // 11:20 AM 18597 hour: 'hA', // 5PM 18598 day: 'MMM D', // Sep 4 18599 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? 18600 month: 'MMM YYYY', // Sept 2015 18601 quarter: '[Q]Q - YYYY', // Q3 18602 year: 'YYYY' // 2015 18603 }, 18604 }, 18605 ticks: { 18606 autoSkip: false, 18607 18608 /** 18609 * Ticks generation input values: 18610 * - 'auto': generates "optimal" ticks based on scale size and time options. 18611 * - 'data': generates ticks from data (including labels from data {t|x|y} objects). 18612 * - 'labels': generates ticks from user given `data.labels` values ONLY. 18613 * @see https://github.com/chartjs/Chart.js/pull/4507 18614 * @since 2.7.0 18615 */ 18616 source: 'auto', 18617 18618 major: { 18619 enabled: false 18620 } 18621 } 18622 }; 18623 18624 var TimeScale = Chart.Scale.extend({ 18625 initialize: function() { 18626 if (!moment) { 18627 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'); 18628 } 18629 18630 this.mergeTicksOptions(); 18631 18632 Chart.Scale.prototype.initialize.call(this); 18633 }, 18634 18635 update: function() { 18636 var me = this; 18637 var options = me.options; 18638 18639 // DEPRECATIONS: output a message only one time per update 18640 if (options.time && options.time.format) { 18641 console.warn('options.time.format is deprecated and replaced by options.time.parser.'); 18642 } 18643 18644 return Chart.Scale.prototype.update.apply(me, arguments); 18645 }, 18646 18647 /** 18648 * Allows data to be referenced via 't' attribute 18649 */ 18650 getRightValue: function(rawValue) { 18651 if (rawValue && rawValue.t !== undefined) { 18652 rawValue = rawValue.t; 18653 } 18654 return Chart.Scale.prototype.getRightValue.call(this, rawValue); 18655 }, 18656 18657 determineDataLimits: function() { 18658 var me = this; 18659 var chart = me.chart; 18660 var timeOpts = me.options.time; 18661 var unit = timeOpts.unit || 'day'; 18662 var min = MAX_INTEGER; 18663 var max = MIN_INTEGER; 18664 var timestamps = []; 18665 var datasets = []; 18666 var labels = []; 18667 var i, j, ilen, jlen, data, timestamp; 18668 18669 // Convert labels to timestamps 18670 for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { 18671 labels.push(parse(chart.data.labels[i], me)); 18672 } 18673 18674 // Convert data to timestamps 18675 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { 18676 if (chart.isDatasetVisible(i)) { 18677 data = chart.data.datasets[i].data; 18678 18679 // Let's consider that all data have the same format. 18680 if (helpers.isObject(data[0])) { 18681 datasets[i] = []; 18682 18683 for (j = 0, jlen = data.length; j < jlen; ++j) { 18684 timestamp = parse(data[j], me); 18685 timestamps.push(timestamp); 18686 datasets[i][j] = timestamp; 18687 } 18688 } else { 18689 timestamps.push.apply(timestamps, labels); 18690 datasets[i] = labels.slice(0); 18691 } 18692 } else { 18693 datasets[i] = []; 18694 } 18695 } 18696 18697 if (labels.length) { 18698 // Sort labels **after** data have been converted 18699 labels = arrayUnique(labels).sort(sorter); 18700 min = Math.min(min, labels[0]); 18701 max = Math.max(max, labels[labels.length - 1]); 18702 } 18703 18704 if (timestamps.length) { 18705 timestamps = arrayUnique(timestamps).sort(sorter); 18706 min = Math.min(min, timestamps[0]); 18707 max = Math.max(max, timestamps[timestamps.length - 1]); 18708 } 18709 18710 min = parse(timeOpts.min, me) || min; 18711 max = parse(timeOpts.max, me) || max; 18712 18713 // In case there is no valid min/max, set limits based on unit time option 18714 min = min === MAX_INTEGER ? +moment().startOf(unit) : min; 18715 max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max; 18716 18717 // Make sure that max is strictly higher than min (required by the lookup table) 18718 me.min = Math.min(min, max); 18719 me.max = Math.max(min + 1, max); 18720 18721 // PRIVATE 18722 me._horizontal = me.isHorizontal(); 18723 me._table = []; 18724 me._timestamps = { 18725 data: timestamps, 18726 datasets: datasets, 18727 labels: labels 18728 }; 18729 }, 18730 18731 buildTicks: function() { 18732 var me = this; 18733 var min = me.min; 18734 var max = me.max; 18735 var options = me.options; 18736 var timeOpts = options.time; 18737 var timestamps = []; 18738 var ticks = []; 18739 var i, ilen, timestamp; 18740 18741 switch (options.ticks.source) { 18742 case 'data': 18743 timestamps = me._timestamps.data; 18744 break; 18745 case 'labels': 18746 timestamps = me._timestamps.labels; 18747 break; 18748 case 'auto': 18749 default: 18750 timestamps = generate(min, max, me.getLabelCapacity(min), options); 18751 } 18752 18753 if (options.bounds === 'ticks' && timestamps.length) { 18754 min = timestamps[0]; 18755 max = timestamps[timestamps.length - 1]; 18756 } 18757 18758 // Enforce limits with user min/max options 18759 min = parse(timeOpts.min, me) || min; 18760 max = parse(timeOpts.max, me) || max; 18761 18762 // Remove ticks outside the min/max range 18763 for (i = 0, ilen = timestamps.length; i < ilen; ++i) { 18764 timestamp = timestamps[i]; 18765 if (timestamp >= min && timestamp <= max) { 18766 ticks.push(timestamp); 18767 } 18768 } 18769 18770 me.min = min; 18771 me.max = max; 18772 18773 // PRIVATE 18774 me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); 18775 me._majorUnit = determineMajorUnit(me._unit); 18776 me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); 18777 me._offsets = computeOffsets(me._table, ticks, min, max, options); 18778 me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts); 18779 18780 return ticksFromTimestamps(ticks, me._majorUnit); 18781 }, 18782 18783 getLabelForIndex: function(index, datasetIndex) { 18784 var me = this; 18785 var data = me.chart.data; 18786 var timeOpts = me.options.time; 18787 var label = data.labels && index < data.labels.length ? data.labels[index] : ''; 18788 var value = data.datasets[datasetIndex].data[index]; 18789 18790 if (helpers.isObject(value)) { 18791 label = me.getRightValue(value); 18792 } 18793 if (timeOpts.tooltipFormat) { 18794 return momentify(label, timeOpts).format(timeOpts.tooltipFormat); 18795 } 18796 if (typeof label === 'string') { 18797 return label; 18798 } 18799 18800 return momentify(label, timeOpts).format(me._labelFormat); 18801 }, 18802 18803 /** 18804 * Function to format an individual tick mark 18805 * @private 18806 */ 18807 tickFormatFunction: function(tick, index, ticks, formatOverride) { 18808 var me = this; 18809 var options = me.options; 18810 var time = tick.valueOf(); 18811 var formats = options.time.displayFormats; 18812 var minorFormat = formats[me._unit]; 18813 var majorUnit = me._majorUnit; 18814 var majorFormat = formats[majorUnit]; 18815 var majorTime = tick.clone().startOf(majorUnit).valueOf(); 18816 var majorTickOpts = options.ticks.major; 18817 var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; 18818 var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat); 18819 var tickOpts = major ? majorTickOpts : options.ticks.minor; 18820 var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); 18821 18822 return formatter ? formatter(label, index, ticks) : label; 18823 }, 18824 18825 convertTicksToLabels: function(ticks) { 18826 var labels = []; 18827 var i, ilen; 18828 18829 for (i = 0, ilen = ticks.length; i < ilen; ++i) { 18830 labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); 18831 } 18832 18833 return labels; 18834 }, 18835 18836 /** 18837 * @private 18838 */ 18839 getPixelForOffset: function(time) { 18840 var me = this; 18841 var size = me._horizontal ? me.width : me.height; 18842 var start = me._horizontal ? me.left : me.top; 18843 var pos = interpolate(me._table, 'time', time, 'pos'); 18844 18845 return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); 18846 }, 18847 18848 getPixelForValue: function(value, index, datasetIndex) { 18849 var me = this; 18850 var time = null; 18851 18852 if (index !== undefined && datasetIndex !== undefined) { 18853 time = me._timestamps.datasets[datasetIndex][index]; 18854 } 18855 18856 if (time === null) { 18857 time = parse(value, me); 18858 } 18859 18860 if (time !== null) { 18861 return me.getPixelForOffset(time); 18862 } 18863 }, 18864 18865 getPixelForTick: function(index) { 18866 var ticks = this.getTicks(); 18867 return index >= 0 && index < ticks.length ? 18868 this.getPixelForOffset(ticks[index].value) : 18869 null; 18870 }, 18871 18872 getValueForPixel: function(pixel) { 18873 var me = this; 18874 var size = me._horizontal ? me.width : me.height; 18875 var start = me._horizontal ? me.left : me.top; 18876 var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; 18877 var time = interpolate(me._table, 'pos', pos, 'time'); 18878 18879 return moment(time); 18880 }, 18881 18882 /** 18883 * Crude approximation of what the label width might be 18884 * @private 18885 */ 18886 getLabelWidth: function(label) { 18887 var me = this; 18888 var ticksOpts = me.options.ticks; 18889 var tickLabelWidth = me.ctx.measureText(label).width; 18890 var angle = helpers.toRadians(ticksOpts.maxRotation); 18891 var cosRotation = Math.cos(angle); 18892 var sinRotation = Math.sin(angle);
18893 var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); 18894 18895 return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); 18896 }, 18897 18898 /** 18899 * @private 18900 */ 18901 getLabelCapacity: function(exampleTime) { 18902 var me = this; 18903 18904 var formatOverride = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation 18905 18906 var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride); 18907 var tickLabelWidth = me.getLabelWidth(exampleLabel); 18908 var innerWidth = me.isHorizontal() ? me.width : me.height; 18909 18910 var capacity = Math.floor(innerWidth / tickLabelWidth); 18911 return capacity > 0 ? capacity : 1; 18912 } 18913 }); 18914 18915 Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); 18916}; 18917 18918},{"25":25,"45":45,"6":6}]},{},[7])(7) 18919});
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.