PluginProbe
Wp-Insert / 2.6.0
Wp-Insert v2.6.0
trunk 1.0 1.1 1.2 1.2.1 1.2.2 1.3.0 1.3.1 1.3.3 1.4 1.5.0 1.5.1 1.5.2 1.5.3 1.6.0 1.6.1 1.6.2 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 2.0 All 63 releases
wp-insert / includes / assets / js / uncompressed / jQuery-Chart / Chart.bundle.js

Chart.bundle.js in Wp-Insert 2.6.0, at includes/assets/js/uncompressed/jQuery-Chart/Chart.bundle.js

18,494 lines 510.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * Chart.js
3 * http://chartjs.org/
4 * Version: 2.7.0
5 *
6 * Copyright 2017 Nick Downie
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 e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
11 /* MIT license */
12 var colorNames = require(5);
13
14 module.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
33 function 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
93 function 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
109 function 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
125 function getRgb(string) {
126 var rgba = getRgba(string);
127 return rgba && rgba.slice(0, 3);
128 }
129
130 function getHsl(string) {
131 var hsla = getHsla(string);
132 return hsla && hsla.slice(0, 3);
133 }
134
135 function 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
149 function hexString(rgb) {
150 return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
151 + hexDouble(rgb[2]);
152 }
153
154 function rgbString(rgba, alpha) {
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
161 function 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
169 function 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
180 function 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
187 function 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
194 function 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)
204 function 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
212 function keyword(rgb) {
213 return reverseNames[rgb.slice(0, 3)];
214 }
215
216 // helpers
217 function scale(num, min, max) {
218 return Math.min(Math.max(min, num), max);
219 }
220
221 function 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
228 var reverseNames = {};
229 for (var name in colorNames) {
230 reverseNames[colorNames[name]] = name;
231 }
232
233 },{"5":5}],2:[function(require,module,exports){
234 /* MIT license */
235 var convert = require(4);
236 var string = require(1);
237
238 var 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
283 Color.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
591 Color.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
599 Color.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
607 Color.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
623 Color.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
680 Color.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
697 Color.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
714 if (typeof window !== 'undefined') {
715 window.Color = Color;
716 }
717
718 module.exports = Color;
719
720 },{"1":1,"4":4}],3:[function(require,module,exports){
721 /* MIT license */
722
723 module.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
779 function 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
814 function 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
847 function 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
858 function 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
871 function rgb2keyword(rgb) {
872 return reverseKeywords[JSON.stringify(rgb)];
873 }
874
875 function 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
892 function 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
914 function rgb2lch(args) {
915 return lab2lch(rgb2lab(args));
916 }
917
918 function 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
956 function 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
975 function hsl2hwb(args) {
976 return rgb2hwb(hsl2rgb(args));
977 }
978
979 function hsl2cmyk(args) {
980 return rgb2cmyk(hsl2rgb(args));
981 }
982
983 function hsl2keyword(args) {
984 return rgb2keyword(hsl2rgb(args));
985 }
986
987
988 function 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
1016 function 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
1030 function hsv2hwb(args) {
1031 return rgb2hwb(hsv2rgb(args))
1032 }
1033
1034 function hsv2cmyk(args) {
1035 return rgb2cmyk(hsv2rgb(args));
1036 }
1037
1038 function hsv2keyword(args) {
1039 return rgb2keyword(hsv2rgb(args));
1040 }
1041
1042 // http://dev.w3.org/csswg/css-color/#hwb-to-rgb
1043 function 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
1078 function hwb2hsl(args) {
1079 return rgb2hsl(hwb2rgb(args));
1080 }
1081
1082 function hwb2hsv(args) {
1083 return rgb2hsv(hwb2rgb(args));
1084 }
1085
1086 function hwb2cmyk(args) {
1087 return rgb2cmyk(hwb2rgb(args));
1088 }
1089
1090 function hwb2keyword(args) {
1091 return rgb2keyword(hwb2rgb(args));
1092 }
1093
1094 function 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
1107 function cmyk2hsl(args) {
1108 return rgb2hsl(cmyk2rgb(args));
1109 }
1110
1111 function cmyk2hsv(args) {
1112 return rgb2hsv(cmyk2rgb(args));
1113 }
1114
1115 function cmyk2hwb(args) {
1116 return rgb2hwb(cmyk2rgb(args));
1117 }
1118
1119 function cmyk2keyword(args) {
1120 return rgb2keyword(cmyk2rgb(args));
1121 }
1122
1123
1124 function 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
1151 function 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
1172 function xyz2lch(args) {
1173 return lab2lch(xyz2lab(args));
1174 }
1175
1176 function 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
1197 function 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
1212 function lab2rgb(args) {
1213 return xyz2rgb(lab2xyz(args));
1214 }
1215
1216 function 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
1228 function lch2xyz(args) {
1229 return lab2xyz(lch2lab(args));
1230 }
1231
1232 function lch2rgb(args) {
1233 return lab2rgb(lch2lab(args));
1234 }
1235
1236 function keyword2rgb(keyword) {
1237 return cssKeywords[keyword];
1238 }
1239
1240 function keyword2hsl(args) {
1241 return rgb2hsl(keyword2rgb(args));
1242 }
1243
1244 function keyword2hsv(args) {
1245 return rgb2hsv(keyword2rgb(args));
1246 }
1247
1248 function keyword2hwb(args) {
1249 return rgb2hwb(keyword2rgb(args));
1250 }
1251
1252 function keyword2cmyk(args) {
1253 return rgb2cmyk(keyword2rgb(args));
1254 }
1255
1256 function keyword2lab(args) {
1257 return rgb2lab(keyword2rgb(args));
1258 }
1259
1260 function keyword2xyz(args) {
1261 return rgb2xyz(keyword2rgb(args));
1262 }
1263
1264 var 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
1415 var reverseKeywords = {};
1416 for (var key in cssKeywords) {
1417 reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
1418 }
1419
1420 },{}],4:[function(require,module,exports){
1421 var conversions = require(3);
1422
1423 var convert = function() {
1424 return new Converter();
1425 }
1426
1427 for (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 */
1463 var 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 */
1469 Converter.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 */
1484 Converter.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 */
1494 Converter.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
1512 module.exports = convert;
1513 },{"3":3}],5:[function(require,module,exports){
1514 'use strict'
1515
1516 module.exports = {
1517 "aliceblue": [240, 248, 255],
1518 "antiquewhite": [250, 235, 215],
1519 "aqua": [0, 255, 255],
1520 "aquamarine": [127, 255, 212],
1521 "azure": [240, 255, 255],
1522 "beige": [245, 245, 220],
1523 "bisque": [255, 228, 196],
1524 "black": [0, 0, 0],
1525 "blanchedalmond": [255, 235, 205],
1526 "blue": [0, 0, 255],
1527 "blueviolet": [138, 43, 226],
1528 "brown": [165, 42, 42],
1529 "burlywood": [222, 184, 135],
1530 "cadetblue": [95, 158, 160],
1531 "chartreuse": [127, 255, 0],
1532 "chocolate": [210, 105, 30],
1533 "coral": [255, 127, 80],
1534 "cornflowerblue": [100, 149, 237],
1535 "cornsilk": [255, 248, 220],
1536 "crimson": [220, 20, 60],
1537 "cyan": [0, 255, 255],
1538 "darkblue": [0, 0, 139],
1539 "darkcyan": [0, 139, 139],
1540 "darkgoldenrod": [184, 134, 11],
1541 "darkgray": [169, 169, 169],
1542 "darkgreen": [0, 100, 0],
1543 "darkgrey": [169, 169, 169],
1544 "darkkhaki": [189, 183, 107],
1545 "darkmagenta": [139, 0, 139],
1546 "darkolivegreen": [85, 107, 47],
1547 "darkorange": [255, 140, 0],
1548 "darkorchid": [153, 50, 204],
1549 "darkred": [139, 0, 0],
1550 "darksalmon": [233, 150, 122],
1551 "darkseagreen": [143, 188, 143],
1552 "darkslateblue": [72, 61, 139],
1553 "darkslategray": [47, 79, 79],
1554 "darkslategrey": [47, 79, 79],
1555 "darkturquoise": [0, 206, 209],
1556 "darkviolet": [148, 0, 211],
1557 "deeppink": [255, 20, 147],
1558 "deepskyblue": [0, 191, 255],
1559 "dimgray": [105, 105, 105],
1560 "dimgrey": [105, 105, 105],
1561 "dodgerblue": [30, 144, 255],
1562 "firebrick": [178, 34, 34],
1563 "floralwhite": [255, 250, 240],
1564 "forestgreen": [34, 139, 34],
1565 "fuchsia": [255, 0, 255],
1566 "gainsboro": [220, 220, 220],
1567 "ghostwhite": [248, 248, 255],
1568 "gold": [255, 215, 0],
1569 "goldenrod": [218, 165, 32],
1570 "gray": [128, 128, 128],
1571 "green": [0, 128, 0],
1572 "greenyellow": [173, 255, 47],
1573 "grey": [128, 128, 128],
1574 "honeydew": [240, 255, 240],
1575 "hotpink": [255, 105, 180],
1576 "indianred": [205, 92, 92],
1577 "indigo": [75, 0, 130],
1578 "ivory": [255, 255, 240],
1579 "khaki": [240, 230, 140],
1580 "lavender": [230, 230, 250],
1581 "lavenderblush": [255, 240, 245],
1582 "lawngreen": [124, 252, 0],
1583 "lemonchiffon": [255, 250, 205],
1584 "lightblue": [173, 216, 230],
1585 "lightcoral": [240, 128, 128],
1586 "lightcyan": [224, 255, 255],
1587 "lightgoldenrodyellow": [250, 250, 210],
1588 "lightgray": [211, 211, 211],
1589 "lightgreen": [144, 238, 144],
1590 "lightgrey": [211, 211, 211],
1591 "lightpink": [255, 182, 193],
1592 "lightsalmon": [255, 160, 122],
1593 "lightseagreen": [32, 178, 170],
1594 "lightskyblue": [135, 206, 250],
1595 "lightslategray": [119, 136, 153],
1596 "lightslategrey": [119, 136, 153],
1597 "lightsteelblue": [176, 196, 222],
1598 "lightyellow": [255, 255, 224],
1599 "lime": [0, 255, 0],
1600 "limegreen": [50, 205, 50],
1601 "linen": [250, 240, 230],
1602 "magenta": [255, 0, 255],
1603 "maroon": [128, 0, 0],
1604 "mediumaquamarine": [102, 205, 170],
1605 "mediumblue": [0, 0, 205],
1606 "mediumorchid": [186, 85, 211],
1607 "mediumpurple": [147, 112, 219],
1608 "mediumseagreen": [60, 179, 113],
1609 "mediumslateblue": [123, 104, 238],
1610 "mediumspringgreen": [0, 250, 154],
1611 "mediumturquoise": [72, 209, 204],
1612 "mediumvioletred": [199, 21, 133],
1613 "midnightblue": [25, 25, 112],
1614 "mintcream": [245, 255, 250],
1615 "mistyrose": [255, 228, 225],
1616 "moccasin": [255, 228, 181],
1617 "navajowhite": [255, 222, 173],
1618 "navy": [0, 0, 128],
1619 "oldlace": [253, 245, 230],
1620 "olive": [128, 128, 0],
1621 "olivedrab": [107, 142, 35],
1622 "orange": [255, 165, 0],
1623 "orangered": [255, 69, 0],
1624 "orchid": [218, 112, 214],
1625 "palegoldenrod": [238, 232, 170],
1626 "palegreen": [152, 251, 152],
1627 "paleturquoise": [175, 238, 238],
1628 "palevioletred": [219, 112, 147],
1629 "papayawhip": [255, 239, 213],
1630 "peachpuff": [255, 218, 185],
1631 "peru": [205, 133, 63],
1632 "pink": [255, 192, 203],
1633 "plum": [221, 160, 221],
1634 "powderblue": [176, 224, 230],
1635 "purple": [128, 0, 128],
1636 "rebeccapurple": [102, 51, 153],
1637 "red": [255, 0, 0],
1638 "rosybrown": [188, 143, 143],
1639 "royalblue": [65, 105, 225],
1640 "saddlebrown": [139, 69, 19],
1641 "salmon": [250, 128, 114],
1642 "sandybrown": [244, 164, 96],
1643 "seagreen": [46, 139, 87],
1644 "seashell": [255, 245, 238],
1645 "sienna": [160, 82, 45],
1646 "silver": [192, 192, 192],
1647 "skyblue": [135, 206, 235],
1648 "slateblue": [106, 90, 205],
1649 "slategray": [112, 128, 144],
1650 "slategrey": [112, 128, 144],
1651 "snow": [255, 250, 250],
1652 "springgreen": [0, 255, 127],
1653 "steelblue": [70, 130, 180],
1654 "tan": [210, 180, 140],
1655 "teal": [0, 128, 128],
1656 "thistle": [216, 191, 216],
1657 "tomato": [255, 99, 71],
1658 "turquoise": [64, 224, 208],
1659 "violet": [238, 130, 238],
1660 "wheat": [245, 222, 179],
1661 "white": [255, 255, 255],
1662 "whitesmoke": [245, 245, 245],
1663 "yellow": [255, 255, 0],
1664 "yellowgreen": [154, 205, 50]
1665 };
1666
1667 },{}],6:[function(require,module,exports){
1668 //! moment.js
1669 //! version : 2.18.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
1680 var hookCallback;
1681
1682 function 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.
1688 function setHookCallback (callback) {
1689 hookCallback = callback;
1690 }
1691
1692 function isArray(input) {
1693 return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
1694 }
1695
1696 function 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
1702 function isObjectEmpty(obj) {
1703 var k;
1704 for (k in obj) {
1705 // even if its not own property I'd still call it non-empty
1706 return false;
1707 }
1708 return true;
1709 }
1710
1711 function isUndefined(input) {
1712 return input === void 0;
1713 }
1714
1715 function isNumber(input) {
1716 return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
1717 }
1718
1719 function isDate(input) {
1720 return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
1721 }
1722
1723 function map(arr, fn) {
1724 var res = [], i;
1725 for (i = 0; i < arr.length; ++i) {
1726 res.push(fn(arr[i], i));
1727 }
1728 return res;
1729 }
1730
1731 function hasOwnProp(a, b) {
1732 return Object.prototype.hasOwnProperty.call(a, b);
1733 }
1734
1735 function extend(a, b) {
1736 for (var i in b) {
1737 if (hasOwnProp(b, i)) {
1738 a[i] = b[i];
1739 }
1740 }
1741
1742 if (hasOwnProp(b, 'toString')) {
1743 a.toString = b.toString;
1744 }
1745
1746 if (hasOwnProp(b, 'valueOf')) {
1747 a.valueOf = b.valueOf;
1748 }
1749
1750 return a;
1751 }
1752
1753 function createUTC (input, format, locale, strict) {
1754 return createLocalOrUTC(input, format, locale, strict, true).utc();
1755 }
1756
1757 function defaultParsingFlags() {
1758 // We need to deep clone this object.
1759 return {
1760 empty : false,
1761 unusedTokens : [],
1762 unusedInput : [],
1763 overflow : -2,
1764 charsLeftOver : 0,
1765 nullInput : false,
1766 invalidMonth : null,
1767 invalidFormat : false,
1768 userInvalidated : false,
1769 iso : false,
1770 parsedDateParts : [],
1771 meridiem : null,
1772 rfc2822 : false,
1773 weekdayMismatch : false
1774 };
1775 }
1776
1777 function getParsingFlags(m) {
1778 if (m._pf == null) {
1779 m._pf = defaultParsingFlags();
1780 }
1781 return m._pf;
1782 }
1783
1784 var some;
1785 if (Array.prototype.some) {
1786 some = Array.prototype.some;
1787 } else {
1788 some = function (fun) {
1789 var t = Object(this);
1790 var len = t.length >>> 0;
1791
1792 for (var i = 0; i < len; i++) {
1793 if (i in t && fun.call(this, t[i], i, t)) {
1794 return true;
1795 }
1796 }
1797
1798 return false;
1799 };
1800 }
1801
1802 var some$1 = some;
1803
1804 function isValid(m) {
1805 if (m._isValid == null) {
1806 var flags = getParsingFlags(m);
1807 var parsedParts = some$1.call(flags.parsedDateParts, function (i) {
1808 return i != null;
1809 });
1810 var isNowValid = !isNaN(m._d.getTime()) &&
1811 flags.overflow < 0 &&
1812 !flags.empty &&
1813 !flags.invalidMonth &&
1814 !flags.invalidWeekday &&
1815 !flags.nullInput &&
1816 !flags.invalidFormat &&
1817 !flags.userInvalidated &&
1818 (!flags.meridiem || (flags.meridiem && parsedParts));
1819
1820 if (m._strict) {
1821 isNowValid = isNowValid &&
1822 flags.charsLeftOver === 0 &&
1823 flags.unusedTokens.length === 0 &&
1824 flags.bigHour === undefined;
1825 }
1826
1827 if (Object.isFrozen == null || !Object.isFrozen(m)) {
1828 m._isValid = isNowValid;
1829 }
1830 else {
1831 return isNowValid;
1832 }
1833 }
1834 return m._isValid;
1835 }
1836
1837 function createInvalid (flags) {
1838 var m = createUTC(NaN);
1839 if (flags != null) {
1840 extend(getParsingFlags(m), flags);
1841 }
1842 else {
1843 getParsingFlags(m).userInvalidated = true;
1844 }
1845
1846 return m;
1847 }
1848
1849 // Plugins that add properties should also add the key here (null value),
1850 // so we can properly clone ourselves.
1851 var momentProperties = hooks.momentProperties = [];
1852
1853 function copyConfig(to, from) {
1854 var i, prop, val;
1855
1856 if (!isUndefined(from._isAMomentObject)) {
1857 to._isAMomentObject = from._isAMomentObject;
1858 }
1859 if (!isUndefined(from._i)) {
1860 to._i = from._i;
1861 }
1862 if (!isUndefined(from._f)) {
1863 to._f = from._f;
1864 }
1865 if (!isUndefined(from._l)) {
1866 to._l = from._l;
1867 }
1868 if (!isUndefined(from._strict)) {
1869 to._strict = from._strict;
1870 }
1871 if (!isUndefined(from._tzm)) {
1872 to._tzm = from._tzm;
1873 }
1874 if (!isUndefined(from._isUTC)) {
1875 to._isUTC = from._isUTC;
1876 }
1877 if (!isUndefined(from._offset)) {
1878 to._offset = from._offset;
1879 }
1880 if (!isUndefined(from._pf)) {
1881 to._pf = getParsingFlags(from);
1882 }
1883 if (!isUndefined(from._locale)) {
1884 to._locale = from._locale;
1885 }
1886
1887 if (momentProperties.length > 0) {
1888 for (i = 0; i < momentProperties.length; i++) {
1889 prop = momentProperties[i];
1890 val = from[prop];
1891 if (!isUndefined(val)) {
1892 to[prop] = val;
1893 }
1894 }
1895 }
1896
1897 return to;
1898 }
1899
1900 var updateInProgress = false;
1901
1902 // Moment prototype object
1903 function Moment(config) {
1904 copyConfig(this, config);
1905 this._d = new Date(config._d != null ? config._d.getTime() : NaN);
1906 if (!this.isValid()) {
1907 this._d = new Date(NaN);
1908 }
1909 // Prevent infinite loop in case updateOffset creates new moment
1910 // objects.
1911 if (updateInProgress === false) {
1912 updateInProgress = true;
1913 hooks.updateOffset(this);
1914 updateInProgress = false;
1915 }
1916 }
1917
1918 function isMoment (obj) {
1919 return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
1920 }
1921
1922 function absFloor (number) {
1923 if (number < 0) {
1924 // -0 -> 0
1925 return Math.ceil(number) || 0;
1926 } else {
1927 return Math.floor(number);
1928 }
1929 }
1930
1931 function toInt(argumentForCoercion) {
1932 var coercedNumber = +argumentForCoercion,
1933 value = 0;
1934
1935 if (coercedNumber !== 0 && isFinite(coercedNumber)) {
1936 value = absFloor(coercedNumber);
1937 }
1938
1939 return value;
1940 }
1941
1942 // compare two arrays, return the number of differences
1943 function compareArrays(array1, array2, dontConvert) {
1944 var len = Math.min(array1.length, array2.length),
1945 lengthDiff = Math.abs(array1.length - array2.length),
1946 diffs = 0,
1947 i;
1948 for (i = 0; i < len; i++) {
1949 if ((dontConvert && array1[i] !== array2[i]) ||
1950 (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
1951 diffs++;
1952 }
1953 }
1954 return diffs + lengthDiff;
1955 }
1956
1957 function warn(msg) {
1958 if (hooks.suppressDeprecationWarnings === false &&
1959 (typeof console !== 'undefined') && console.warn) {
1960 console.warn('Deprecation warning: ' + msg);
1961 }
1962 }
1963
1964 function deprecate(msg, fn) {
1965 var firstTime = true;
1966
1967 return extend(function () {
1968 if (hooks.deprecationHandler != null) {
1969 hooks.deprecationHandler(null, msg);
1970 }
1971 if (firstTime) {
1972 var args = [];
1973 var arg;
1974 for (var i = 0; i < arguments.length; i++) {
1975 arg = '';
1976 if (typeof arguments[i] === 'object') {
1977 arg += '\n[' + i + '] ';
1978 for (var key in arguments[0]) {
1979 arg += key + ': ' + arguments[0][key] + ', ';
1980 }
1981 arg = arg.slice(0, -2); // Remove trailing comma and space
1982 } else {
1983 arg = arguments[i];
1984 }
1985 args.push(arg);
1986 }
1987 warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
1988 firstTime = false;
1989 }
1990 return fn.apply(this, arguments);
1991 }, fn);
1992 }
1993
1994 var deprecations = {};
1995
1996 function deprecateSimple(name, msg) {
1997 if (hooks.deprecationHandler != null) {
1998 hooks.deprecationHandler(name, msg);
1999 }
2000 if (!deprecations[name]) {
2001 warn(msg);
2002 deprecations[name] = true;
2003 }
2004 }
2005
2006 hooks.suppressDeprecationWarnings = false;
2007 hooks.deprecationHandler = null;
2008
2009 function isFunction(input) {
2010 return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
2011 }
2012
2013 function set (config) {
2014 var prop, i;
2015 for (i in config) {
2016 prop = config[i];
2017 if (isFunction(prop)) {
2018 this[i] = prop;
2019 } else {
2020 this['_' + i] = prop;
2021 }
2022 }
2023 this._config = config;
2024 // Lenient ordinal parsing accepts just a number in addition to
2025 // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
2026 // TODO: Remove "ordinalParse" fallback in next major release.
2027 this._dayOfMonthOrdinalParseLenient = new RegExp(
2028 (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) +
2029 '|' + (/\d{1,2}/).source);
2030 }
2031
2032 function mergeConfigs(parentConfig, childConfig) {
2033 var res = extend({}, parentConfig), prop;
2034 for (prop in childConfig) {
2035 if (hasOwnProp(childConfig, prop)) {
2036 if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
2037 res[prop] = {};
2038 extend(res[prop], parentConfig[prop]);
2039 extend(res[prop], childConfig[prop]);
2040 } else if (childConfig[prop] != null) {
2041 res[prop] = childConfig[prop];
2042 } else {
2043 delete res[prop];
2044 }
2045 }
2046 }
2047 for (prop in parentConfig) {
2048 if (hasOwnProp(parentConfig, prop) &&
2049 !hasOwnProp(childConfig, prop) &&
2050 isObject(parentConfig[prop])) {
2051 // make sure changes to properties don't modify parent config
2052 res[prop] = extend({}, res[prop]);
2053 }
2054 }
2055 return res;
2056 }
2057
2058 function Locale(config) {
2059 if (config != null) {
2060 this.set(config);
2061 }
2062 }
2063
2064 var keys;
2065
2066 if (Object.keys) {
2067 keys = Object.keys;
2068 } else {
2069 keys = function (obj) {
2070 var i, res = [];
2071 for (i in obj) {
2072 if (hasOwnProp(obj, i)) {
2073 res.push(i);
2074 }
2075 }
2076 return res;
2077 };
2078 }
2079
2080 var keys$1 = keys;
2081
2082 var defaultCalendar = {
2083 sameDay : '[Today at] LT',
2084 nextDay : '[Tomorrow at] LT',
2085 nextWeek : 'dddd [at] LT',
2086 lastDay : '[Yesterday at] LT',
2087 lastWeek : '[Last] dddd [at] LT',
2088 sameElse : 'L'
2089 };
2090
2091 function calendar (key, mom, now) {
2092 var output = this._calendar[key] || this._calendar['sameElse'];
2093 return isFunction(output) ? output.call(mom, now) : output;
2094 }
2095
2096 var defaultLongDateFormat = {
2097 LTS : 'h:mm:ss A',
2098 LT : 'h:mm A',
2099 L : 'MM/DD/YYYY',
2100 LL : 'MMMM D, YYYY',
2101 LLL : 'MMMM D, YYYY h:mm A',
2102 LLLL : 'dddd, MMMM D, YYYY h:mm A'
2103 };
2104
2105 function longDateFormat (key) {
2106 var format = this._longDateFormat[key],
2107 formatUpper = this._longDateFormat[key.toUpperCase()];
2108
2109 if (format || !formatUpper) {
2110 return format;
2111 }
2112
2113 this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
2114 return val.slice(1);
2115 });
2116
2117 return this._longDateFormat[key];
2118 }
2119
2120 var defaultInvalidDate = 'Invalid date';
2121
2122 function invalidDate () {
2123 return this._invalidDate;
2124 }
2125
2126 var defaultOrdinal = '%d';
2127 var defaultDayOfMonthOrdinalParse = /\d{1,2}/;
2128
2129 function ordinal (number) {
2130 return this._ordinal.replace('%d', number);
2131 }
2132
2133 var defaultRelativeTime = {
2134 future : 'in %s',
2135 past : '%s ago',
2136 s : 'a few seconds',
2137 ss : '%d seconds',
2138 m : 'a minute',
2139 mm : '%d minutes',
2140 h : 'an hour',
2141 hh : '%d hours',
2142 d : 'a day',
2143 dd : '%d days',
2144 M : 'a month',
2145 MM : '%d months',
2146 y : 'a year',
2147 yy : '%d years'
2148 };
2149
2150 function relativeTime (number, withoutSuffix, string, isFuture) {
2151 var output = this._relativeTime[string];
2152 return (isFunction(output)) ?
2153 output(number, withoutSuffix, string, isFuture) :
2154 output.replace(/%d/i, number);
2155 }
2156
2157 function pastFuture (diff, output) {
2158 var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
2159 return isFunction(format) ? format(output) : format.replace(/%s/i, output);
2160 }
2161
2162 var aliases = {};
2163
2164 function addUnitAlias (unit, shorthand) {
2165 var lowerCase = unit.toLowerCase();
2166 aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
2167 }
2168
2169 function normalizeUnits(units) {
2170 return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
2171 }
2172
2173 function normalizeObjectUnits(inputObject) {
2174 var normalizedInput = {},
2175 normalizedProp,
2176 prop;
2177
2178 for (prop in inputObject) {
2179 if (hasOwnProp(inputObject, prop)) {
2180 normalizedProp = normalizeUnits(prop);
2181 if (normalizedProp) {
2182 normalizedInput[normalizedProp] = inputObject[prop];
2183 }
2184 }
2185 }
2186
2187 return normalizedInput;
2188 }
2189
2190 var priorities = {};
2191
2192 function addUnitPriority(unit, priority) {
2193 priorities[unit] = priority;
2194 }
2195
2196 function getPrioritizedUnits(unitsObj) {
2197 var units = [];
2198 for (var u in unitsObj) {
2199 units.push({unit: u, priority: priorities[u]});
2200 }
2201 units.sort(function (a, b) {
2202 return a.priority - b.priority;
2203 });
2204 return units;
2205 }
2206
2207 function makeGetSet (unit, keepTime) {
2208 return function (value) {
2209 if (value != null) {
2210 set$1(this, unit, value);
2211 hooks.updateOffset(this, keepTime);
2212 return this;
2213 } else {
2214 return get(this, unit);
2215 }
2216 };
2217 }
2218
2219 function get (mom, unit) {
2220 return mom.isValid() ?
2221 mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
2222 }
2223
2224 function set$1 (mom, unit, value) {
2225 if (mom.isValid()) {
2226 mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
2227 }
2228 }
2229
2230 // MOMENTS
2231
2232 function stringGet (units) {
2233 units = normalizeUnits(units);
2234 if (isFunction(this[units])) {
2235 return this[units]();
2236 }
2237 return this;
2238 }
2239
2240
2241 function stringSet (units, value) {
2242 if (typeof units === 'object') {
2243 units = normalizeObjectUnits(units);
2244 var prioritized = getPrioritizedUnits(units);
2245 for (var i = 0; i < prioritized.length; i++) {
2246 this[prioritized[i].unit](units[prioritized[i].unit]);
2247 }
2248 } else {
2249 units = normalizeUnits(units);
2250 if (isFunction(this[units])) {
2251 return this[units](value);
2252 }
2253 }
2254 return this;
2255 }
2256
2257 function zeroFill(number, targetLength, forceSign) {
2258 var absNumber = '' + Math.abs(number),
2259 zerosToFill = targetLength - absNumber.length,
2260 sign = number >= 0;
2261 return (sign ? (forceSign ? '+' : '') : '-') +
2262 Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
2263 }
2264
2265 var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
2266
2267 var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
2268
2269 var formatFunctions = {};
2270
2271 var formatTokenFunctions = {};
2272
2273 // token: 'M'
2274 // padded: ['MM', 2]
2275 // ordinal: 'Mo'
2276 // callback: function () { this.month() + 1 }
2277 function addFormatToken (token, padded, ordinal, callback) {
2278 var func = callback;
2279 if (typeof callback === 'string') {
2280 func = function () {
2281 return this[callback]();
2282 };
2283 }
2284 if (token) {
2285 formatTokenFunctions[token] = func;
2286 }
2287 if (padded) {
2288 formatTokenFunctions[padded[0]] = function () {
2289 return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
2290 };
2291 }
2292 if (ordinal) {
2293 formatTokenFunctions[ordinal] = function () {
2294 return this.localeData().ordinal(func.apply(this, arguments), token);
2295 };
2296 }
2297 }
2298
2299 function removeFormattingTokens(input) {
2300 if (input.match(/\[[\s\S]/)) {
2301 return input.replace(/^\[|\]$/g, '');
2302 }
2303 return input.replace(/\\/g, '');
2304 }
2305
2306 function makeFormatFunction(format) {
2307 var array = format.match(formattingTokens), i, length;
2308
2309 for (i = 0, length = array.length; i < length; i++) {
2310 if (formatTokenFunctions[array[i]]) {
2311 array[i] = formatTokenFunctions[array[i]];
2312 } else {
2313 array[i] = removeFormattingTokens(array[i]);
2314 }
2315 }
2316
2317 return function (mom) {
2318 var output = '', i;
2319 for (i = 0; i < length; i++) {
2320 output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
2321 }
2322 return output;
2323 };
2324 }
2325
2326 // format date using native date object
2327 function formatMoment(m, format) {
2328 if (!m.isValid()) {
2329 return m.localeData().invalidDate();
2330 }
2331
2332 format = expandFormat(format, m.localeData());
2333 formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
2334
2335 return formatFunctions[format](m);
2336 }
2337
2338 function expandFormat(format, locale) {
2339 var i = 5;
2340
2341 function replaceLongDateFormatTokens(input) {
2342 return locale.longDateFormat(input) || input;
2343 }
2344
2345 localFormattingTokens.lastIndex = 0;
2346 while (i >= 0 && localFormattingTokens.test(format)) {
2347 format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
2348 localFormattingTokens.lastIndex = 0;
2349 i -= 1;
2350 }
2351
2352 return format;
2353 }
2354
2355 var match1 = /\d/; // 0 - 9
2356 var match2 = /\d\d/; // 00 - 99
2357 var match3 = /\d{3}/; // 000 - 999
2358 var match4 = /\d{4}/; // 0000 - 9999
2359 var match6 = /[+-]?\d{6}/; // -999999 - 999999
2360 var match1to2 = /\d\d?/; // 0 - 99
2361 var match3to4 = /\d\d\d\d?/; // 999 - 9999
2362 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
2363 var match1to3 = /\d{1,3}/; // 0 - 999
2364 var match1to4 = /\d{1,4}/; // 0 - 9999
2365 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
2366
2367 var matchUnsigned = /\d+/; // 0 - inf
2368 var matchSigned = /[+-]?\d+/; // -inf - inf
2369
2370 var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
2371 var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
2372
2373 var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
2374
2375 // any word (or two) characters or numbers including two/three word month in arabic.
2376 // includes scottish gaelic two word and hyphenated months
2377 var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
2378
2379
2380 var regexes = {};
2381
2382 function addRegexToken (token, regex, strictRegex) {
2383 regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
2384 return (isStrict && strictRegex) ? strictRegex : regex;
2385 };
2386 }
2387
2388 function getParseRegexForToken (token, config) {
2389 if (!hasOwnProp(regexes, token)) {
2390 return new RegExp(unescapeFormat(token));
2391 }
2392
2393 return regexes[token](config._strict, config._locale);
2394 }
2395
2396 // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
2397 function unescapeFormat(s) {
2398 return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
2399 return p1 || p2 || p3 || p4;
2400 }));
2401 }
2402
2403 function regexEscape(s) {
2404 return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
2405 }
2406
2407 var tokens = {};
2408
2409 function addParseToken (token, callback) {
2410 var i, func = callback;
2411 if (typeof token === 'string') {
2412 token = [token];
2413 }
2414 if (isNumber(callback)) {
2415 func = function (input, array) {
2416 array[callback] = toInt(input);
2417 };
2418 }
2419 for (i = 0; i < token.length; i++) {
2420 tokens[token[i]] = func;
2421 }
2422 }
2423
2424 function addWeekParseToken (token, callback) {
2425 addParseToken(token, function (input, array, config, token) {
2426 config._w = config._w || {};
2427 callback(input, config._w, config, token);
2428 });
2429 }
2430
2431 function addTimeToArrayFromToken(token, input, config) {
2432 if (input != null && hasOwnProp(tokens, token)) {
2433 tokens[token](input, config._a, config, token);
2434 }
2435 }
2436
2437 var YEAR = 0;
2438 var MONTH = 1;
2439 var DATE = 2;
2440 var HOUR = 3;
2441 var MINUTE = 4;
2442 var SECOND = 5;
2443 var MILLISECOND = 6;
2444 var WEEK = 7;
2445 var WEEKDAY = 8;
2446
2447 var indexOf;
2448
2449 if (Array.prototype.indexOf) {
2450 indexOf = Array.prototype.indexOf;
2451 } else {
2452 indexOf = function (o) {
2453 // I know
2454 var i;
2455 for (i = 0; i < this.length; ++i) {
2456 if (this[i] === o) {
2457 return i;
2458 }
2459 }
2460 return -1;
2461 };
2462 }
2463
2464 var indexOf$1 = indexOf;
2465
2466 function daysInMonth(year, month) {
2467 return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
2468 }
2469
2470 // FORMATTING
2471
2472 addFormatToken('M', ['MM', 2], 'Mo', function () {
2473 return this.month() + 1;
2474 });
2475
2476 addFormatToken('MMM', 0, 0, function (format) {
2477 return this.localeData().monthsShort(this, format);
2478 });
2479
2480 addFormatToken('MMMM', 0, 0, function (format) {
2481 return this.localeData().months(this, format);
2482 });
2483
2484 // ALIASES
2485
2486 addUnitAlias('month', 'M');
2487
2488 // PRIORITY
2489
2490 addUnitPriority('month', 8);
2491
2492 // PARSING
2493
2494 addRegexToken('M', match1to2);
2495 addRegexToken('MM', match1to2, match2);
2496 addRegexToken('MMM', function (isStrict, locale) {
2497 return locale.monthsShortRegex(isStrict);
2498 });
2499 addRegexToken('MMMM', function (isStrict, locale) {
2500 return locale.monthsRegex(isStrict);
2501 });
2502
2503 addParseToken(['M', 'MM'], function (input, array) {
2504 array[MONTH] = toInt(input) - 1;
2505 });
2506
2507 addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
2508 var month = config._locale.monthsParse(input, token, config._strict);
2509 // if we didn't find a month name, mark the date as invalid.
2510 if (month != null) {
2511 array[MONTH] = month;
2512 } else {
2513 getParsingFlags(config).invalidMonth = input;
2514 }
2515 });
2516
2517 // LOCALES
2518
2519 var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
2520 var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
2521 function localeMonths (m, format) {
2522 if (!m) {
2523 return isArray(this._months) ? this._months :
2524 this._months['standalone'];
2525 }
2526 return isArray(this._months) ? this._months[m.month()] :
2527 this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
2528 }
2529
2530 var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
2531 function localeMonthsShort (m, format) {
2532 if (!m) {
2533 return isArray(this._monthsShort) ? this._monthsShort :
2534 this._monthsShort['standalone'];
2535 }
2536 return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
2537 this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
2538 }
2539
2540 function handleStrictParse(monthName, format, strict) {
2541 var i, ii, mom, llc = monthName.toLocaleLowerCase();
2542 if (!this._monthsParse) {
2543 // this is not used
2544 this._monthsParse = [];
2545 this._longMonthsParse = [];
2546 this._shortMonthsParse = [];
2547 for (i = 0; i < 12; ++i) {
2548 mom = createUTC([2000, i]);
2549 this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
2550 this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
2551 }
2552 }
2553
2554 if (strict) {
2555 if (format === 'MMM') {
2556 ii = indexOf$1.call(this._shortMonthsParse, llc);
2557 return ii !== -1 ? ii : null;
2558 } else {
2559 ii = indexOf$1.call(this._longMonthsParse, llc);
2560 return ii !== -1 ? ii : null;
2561 }
2562 } else {
2563 if (format === 'MMM') {
2564 ii = indexOf$1.call(this._shortMonthsParse, llc);
2565 if (ii !== -1) {
2566 return ii;
2567 }
2568 ii = indexOf$1.call(this._longMonthsParse, llc);
2569 return ii !== -1 ? ii : null;
2570 } else {
2571 ii = indexOf$1.call(this._longMonthsParse, llc);
2572 if (ii !== -1) {
2573 return ii;
2574 }
2575 ii = indexOf$1.call(this._shortMonthsParse, llc);
2576 return ii !== -1 ? ii : null;
2577 }
2578 }
2579 }
2580
2581 function localeMonthsParse (monthName, format, strict) {
2582 var i, mom, regex;
2583
2584 if (this._monthsParseExact) {
2585 return handleStrictParse.call(this, monthName, format, strict);
2586 }
2587
2588 if (!this._monthsParse) {
2589 this._monthsParse = [];
2590 this._longMonthsParse = [];
2591 this._shortMonthsParse = [];
2592 }
2593
2594 // TODO: add sorting
2595 // Sorting makes sure if one month (or abbr) is a prefix of another
2596 // see sorting in computeMonthsParse
2597 for (i = 0; i < 12; i++) {
2598 // make the regex if we don't have it already
2599 mom = createUTC([2000, i]);
2600 if (strict && !this._longMonthsParse[i]) {
2601 this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
2602 this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
2603 }
2604 if (!strict && !this._monthsParse[i]) {
2605 regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
2606 this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
2607 }
2608 // test the regex
2609 if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
2610 return i;
2611 } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
2612 return i;
2613 } else if (!strict && this._monthsParse[i].test(monthName)) {
2614 return i;
2615 }
2616 }
2617 }
2618
2619 // MOMENTS
2620
2621 function setMonth (mom, value) {
2622 var dayOfMonth;
2623
2624 if (!mom.isValid()) {
2625 // No op
2626 return mom;
2627 }
2628
2629 if (typeof value === 'string') {
2630 if (/^\d+$/.test(value)) {
2631 value = toInt(value);
2632 } else {
2633 value = mom.localeData().monthsParse(value);
2634 // TODO: Another silent failure?
2635 if (!isNumber(value)) {
2636 return mom;
2637 }
2638 }
2639 }
2640
2641 dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
2642 mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
2643 return mom;
2644 }
2645
2646 function getSetMonth (value) {
2647 if (value != null) {
2648 setMonth(this, value);
2649 hooks.updateOffset(this, true);
2650 return this;
2651 } else {
2652 return get(this, 'Month');
2653 }
2654 }
2655
2656 function getDaysInMonth () {
2657 return daysInMonth(this.year(), this.month());
2658 }
2659
2660 var defaultMonthsShortRegex = matchWord;
2661 function monthsShortRegex (isStrict) {
2662 if (this._monthsParseExact) {
2663 if (!hasOwnProp(this, '_monthsRegex')) {
2664 computeMonthsParse.call(this);
2665 }
2666 if (isStrict) {
2667 return this._monthsShortStrictRegex;
2668 } else {
2669 return this._monthsShortRegex;
2670 }
2671 } else {
2672 if (!hasOwnProp(this, '_monthsShortRegex')) {
2673 this._monthsShortRegex = defaultMonthsShortRegex;
2674 }
2675 return this._monthsShortStrictRegex && isStrict ?
2676 this._monthsShortStrictRegex : this._monthsShortRegex;
2677 }
2678 }
2679
2680 var defaultMonthsRegex = matchWord;
2681 function monthsRegex (isStrict) {
2682 if (this._monthsParseExact) {
2683 if (!hasOwnProp(this, '_monthsRegex')) {
2684 computeMonthsParse.call(this);
2685 }
2686 if (isStrict) {
2687 return this._monthsStrictRegex;
2688 } else {
2689 return this._monthsRegex;
2690 }
2691 } else {
2692 if (!hasOwnProp(this, '_monthsRegex')) {
2693 this._monthsRegex = defaultMonthsRegex;
2694 }
2695 return this._monthsStrictRegex && isStrict ?
2696 this._monthsStrictRegex : this._monthsRegex;
2697 }
2698 }
2699
2700 function computeMonthsParse () {
2701 function cmpLenRev(a, b) {
2702 return b.length - a.length;
2703 }
2704
2705 var shortPieces = [], longPieces = [], mixedPieces = [],
2706 i, mom;
2707 for (i = 0; i < 12; i++) {
2708 // make the regex if we don't have it already
2709 mom = createUTC([2000, i]);
2710 shortPieces.push(this.monthsShort(mom, ''));
2711 longPieces.push(this.months(mom, ''));
2712 mixedPieces.push(this.months(mom, ''));
2713 mixedPieces.push(this.monthsShort(mom, ''));
2714 }
2715 // Sorting makes sure if one month (or abbr) is a prefix of another it
2716 // will match the longer piece.
2717 shortPieces.sort(cmpLenRev);
2718 longPieces.sort(cmpLenRev);
2719 mixedPieces.sort(cmpLenRev);
2720 for (i = 0; i < 12; i++) {
2721 shortPieces[i] = regexEscape(shortPieces[i]);
2722 longPieces[i] = regexEscape(longPieces[i]);
2723 }
2724 for (i = 0; i < 24; i++) {
2725 mixedPieces[i] = regexEscape(mixedPieces[i]);
2726 }
2727
2728 this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
2729 this._monthsShortRegex = this._monthsRegex;
2730 this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
2731 this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
2732 }
2733
2734 // FORMATTING
2735
2736 addFormatToken('Y', 0, 0, function () {
2737 var y = this.year();
2738 return y <= 9999 ? '' + y : '+' + y;
2739 });
2740
2741 addFormatToken(0, ['YY', 2], 0, function () {
2742 return this.year() % 100;
2743 });
2744
2745 addFormatToken(0, ['YYYY', 4], 0, 'year');
2746 addFormatToken(0, ['YYYYY', 5], 0, 'year');
2747 addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
2748
2749 // ALIASES
2750
2751 addUnitAlias('year', 'y');
2752
2753 // PRIORITIES
2754
2755 addUnitPriority('year', 1);
2756
2757 // PARSING
2758
2759 addRegexToken('Y', matchSigned);
2760 addRegexToken('YY', match1to2, match2);
2761 addRegexToken('YYYY', match1to4, match4);
2762 addRegexToken('YYYYY', match1to6, match6);
2763 addRegexToken('YYYYYY', match1to6, match6);
2764
2765 addParseToken(['YYYYY', 'YYYYYY'], YEAR);
2766 addParseToken('YYYY', function (input, array) {
2767 array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
2768 });
2769 addParseToken('YY', function (input, array) {
2770 array[YEAR] = hooks.parseTwoDigitYear(input);
2771 });
2772 addParseToken('Y', function (input, array) {
2773 array[YEAR] = parseInt(input, 10);
2774 });
2775
2776 // HELPERS
2777
2778 function daysInYear(year) {
2779 return isLeapYear(year) ? 366 : 365;
2780 }
2781
2782 function isLeapYear(year) {
2783 return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
2784 }
2785
2786 // HOOKS
2787
2788 hooks.parseTwoDigitYear = function (input) {
2789 return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
2790 };
2791
2792 // MOMENTS
2793
2794 var getSetYear = makeGetSet('FullYear', true);
2795
2796 function getIsLeapYear () {
2797 return isLeapYear(this.year());
2798 }
2799
2800 function createDate (y, m, d, h, M, s, ms) {
2801 // can't just apply() to create a date:
2802 // https://stackoverflow.com/q/181348
2803 var date = new Date(y, m, d, h, M, s, ms);
2804
2805 // the date constructor remaps years 0-99 to 1900-1999
2806 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
2807 date.setFullYear(y);
2808 }
2809 return date;
2810 }
2811
2812 function createUTCDate (y) {
2813 var date = new Date(Date.UTC.apply(null, arguments));
2814
2815 // the Date.UTC function remaps years 0-99 to 1900-1999
2816 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
2817 date.setUTCFullYear(y);
2818 }
2819 return date;
2820 }
2821
2822 // start-of-first-week - start-of-year
2823 function firstWeekOffset(year, dow, doy) {
2824 var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
2825 fwd = 7 + dow - doy,
2826 // first-week day local weekday -- which local weekday is fwd
2827 fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
2828
2829 return -fwdlw + fwd - 1;
2830 }
2831
2832 // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
2833 function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
2834 var localWeekday = (7 + weekday - dow) % 7,
2835 weekOffset = firstWeekOffset(year, dow, doy),
2836 dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
2837 resYear, resDayOfYear;
2838
2839 if (dayOfYear <= 0) {
2840 resYear = year - 1;
2841 resDayOfYear = daysInYear(resYear) + dayOfYear;
2842 } else if (dayOfYear > daysInYear(year)) {
2843 resYear = year + 1;
2844 resDayOfYear = dayOfYear - daysInYear(year);
2845 } else {
2846 resYear = year;
2847 resDayOfYear = dayOfYear;
2848 }
2849
2850 return {
2851 year: resYear,
2852 dayOfYear: resDayOfYear
2853 };
2854 }
2855
2856 function weekOfYear(mom, dow, doy) {
2857 var weekOffset = firstWeekOffset(mom.year(), dow, doy),
2858 week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
2859 resWeek, resYear;
2860
2861 if (week < 1) {
2862 resYear = mom.year() - 1;
2863 resWeek = week + weeksInYear(resYear, dow, doy);
2864 } else if (week > weeksInYear(mom.year(), dow, doy)) {
2865 resWeek = week - weeksInYear(mom.year(), dow, doy);
2866 resYear = mom.year() + 1;
2867 } else {
2868 resYear = mom.year();
2869 resWeek = week;
2870 }
2871
2872 return {
2873 week: resWeek,
2874 year: resYear
2875 };
2876 }
2877
2878 function weeksInYear(year, dow, doy) {
2879 var weekOffset = firstWeekOffset(year, dow, doy),
2880 weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
2881 return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
2882 }
2883
2884 // FORMATTING
2885
2886 addFormatToken('w', ['ww', 2], 'wo', 'week');
2887 addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
2888
2889 // ALIASES
2890
2891 addUnitAlias('week', 'w');
2892 addUnitAlias('isoWeek', 'W');
2893
2894 // PRIORITIES
2895
2896 addUnitPriority('week', 5);
2897 addUnitPriority('isoWeek', 5);
2898
2899 // PARSING
2900
2901 addRegexToken('w', match1to2);
2902 addRegexToken('ww', match1to2, match2);
2903 addRegexToken('W', match1to2);
2904 addRegexToken('WW', match1to2, match2);
2905
2906 addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
2907 week[token.substr(0, 1)] = toInt(input);
2908 });
2909
2910 // HELPERS
2911
2912 // LOCALES
2913
2914 function localeWeek (mom) {
2915 return weekOfYear(mom, this._week.dow, this._week.doy).week;
2916 }
2917
2918 var defaultLocaleWeek = {
2919 dow : 0, // Sunday is the first day of the week.
2920 doy : 6 // The week that contains Jan 1st is the first week of the year.
2921 };
2922
2923 function localeFirstDayOfWeek () {
2924 return this._week.dow;
2925 }
2926
2927 function localeFirstDayOfYear () {
2928 return this._week.doy;
2929 }
2930
2931 // MOMENTS
2932
2933 function getSetWeek (input) {
2934 var week = this.localeData().week(this);
2935 return input == null ? week : this.add((input - week) * 7, 'd');
2936 }
2937
2938 function getSetISOWeek (input) {
2939 var week = weekOfYear(this, 1, 4).week;
2940 return input == null ? week : this.add((input - week) * 7, 'd');
2941 }
2942
2943 // FORMATTING
2944
2945 addFormatToken('d', 0, 'do', 'day');
2946
2947 addFormatToken('dd', 0, 0, function (format) {
2948 return this.localeData().weekdaysMin(this, format);
2949 });
2950
2951 addFormatToken('ddd', 0, 0, function (format) {
2952 return this.localeData().weekdaysShort(this, format);
2953 });
2954
2955 addFormatToken('dddd', 0, 0, function (format) {
2956 return this.localeData().weekdays(this, format);
2957 });
2958
2959 addFormatToken('e', 0, 0, 'weekday');
2960 addFormatToken('E', 0, 0, 'isoWeekday');
2961
2962 // ALIASES
2963
2964 addUnitAlias('day', 'd');
2965 addUnitAlias('weekday', 'e');
2966 addUnitAlias('isoWeekday', 'E');
2967
2968 // PRIORITY
2969 addUnitPriority('day', 11);
2970 addUnitPriority('weekday', 11);
2971 addUnitPriority('isoWeekday', 11);
2972
2973 // PARSING
2974
2975 addRegexToken('d', match1to2);
2976 addRegexToken('e', match1to2);
2977 addRegexToken('E', match1to2);
2978 addRegexToken('dd', function (isStrict, locale) {
2979 return locale.weekdaysMinRegex(isStrict);
2980 });
2981 addRegexToken('ddd', function (isStrict, locale) {
2982 return locale.weekdaysShortRegex(isStrict);
2983 });
2984 addRegexToken('dddd', function (isStrict, locale) {
2985 return locale.weekdaysRegex(isStrict);
2986 });
2987
2988 addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
2989 var weekday = config._locale.weekdaysParse(input, token, config._strict);
2990 // if we didn't get a weekday name, mark the date as invalid
2991 if (weekday != null) {
2992 week.d = weekday;
2993 } else {
2994 getParsingFlags(config).invalidWeekday = input;
2995 }
2996 });
2997
2998 addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
2999 week[token] = toInt(input);
3000 });
3001
3002 // HELPERS
3003
3004 function parseWeekday(input, locale) {
3005 if (typeof input !== 'string') {
3006 return input;
3007 }
3008
3009 if (!isNaN(input)) {
3010 return parseInt(input, 10);
3011 }
3012
3013 input = locale.weekdaysParse(input);
3014 if (typeof input === 'number') {
3015 return input;
3016 }
3017
3018 return null;
3019 }
3020
3021 function parseIsoWeekday(input, locale) {
3022 if (typeof input === 'string') {
3023 return locale.weekdaysParse(input) % 7 || 7;
3024 }
3025 return isNaN(input) ? null : input;
3026 }
3027
3028 // LOCALES
3029
3030 var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
3031 function localeWeekdays (m, format) {
3032 if (!m) {
3033 return isArray(this._weekdays) ? this._weekdays :
3034 this._weekdays['standalone'];
3035 }
3036 return isArray(this._weekdays) ? this._weekdays[m.day()] :
3037 this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
3038 }
3039
3040 var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
3041 function localeWeekdaysShort (m) {
3042 return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
3043 }
3044
3045 var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
3046 function localeWeekdaysMin (m) {
3047 return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
3048 }
3049
3050 function handleStrictParse$1(weekdayName, format, strict) {
3051 var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
3052 if (!this._weekdaysParse) {
3053 this._weekdaysParse = [];
3054 this._shortWeekdaysParse = [];
3055 this._minWeekdaysParse = [];
3056
3057 for (i = 0; i < 7; ++i) {
3058 mom = createUTC([2000, 1]).day(i);
3059 this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
3060 this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
3061 this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
3062 }
3063 }
3064
3065 if (strict) {
3066 if (format === 'dddd') {
3067 ii = indexOf$1.call(this._weekdaysParse, llc);
3068 return ii !== -1 ? ii : null;
3069 } else if (format === 'ddd') {
3070 ii = indexOf$1.call(this._shortWeekdaysParse, llc);
3071 return ii !== -1 ? ii : null;
3072 } else {
3073 ii = indexOf$1.call(this._minWeekdaysParse, llc);
3074 return ii !== -1 ? ii : null;
3075 }
3076 } else {
3077 if (format === 'dddd') {
3078 ii = indexOf$1.call(this._weekdaysParse, llc);
3079 if (ii !== -1) {
3080 return ii;
3081 }
3082 ii = indexOf$1.call(this._shortWeekdaysParse, llc);
3083 if (ii !== -1) {
3084 return ii;
3085 }
3086 ii = indexOf$1.call(this._minWeekdaysParse, llc);
3087 return ii !== -1 ? ii : null;
3088 } else if (format === 'ddd') {
3089 ii = indexOf$1.call(this._shortWeekdaysParse, llc);
3090 if (ii !== -1) {
3091 return ii;
3092 }
3093 ii = indexOf$1.call(this._weekdaysParse, llc);
3094 if (ii !== -1) {
3095 return ii;
3096 }
3097 ii = indexOf$1.call(this._minWeekdaysParse, llc);
3098 return ii !== -1 ? ii : null;
3099 } else {
3100 ii = indexOf$1.call(this._minWeekdaysParse, llc);
3101 if (ii !== -1) {
3102 return ii;
3103 }
3104 ii = indexOf$1.call(this._weekdaysParse, llc);
3105 if (ii !== -1) {
3106 return ii;
3107 }
3108 ii = indexOf$1.call(this._shortWeekdaysParse, llc);
3109 return ii !== -1 ? ii : null;
3110 }
3111 }
3112 }
3113
3114 function localeWeekdaysParse (weekdayName, format, strict) {
3115 var i, mom, regex;
3116
3117 if (this._weekdaysParseExact) {
3118 return handleStrictParse$1.call(this, weekdayName, format, strict);
3119 }
3120
3121 if (!this._weekdaysParse) {
3122 this._weekdaysParse = [];
3123 this._minWeekdaysParse = [];
3124 this._shortWeekdaysParse = [];
3125 this._fullWeekdaysParse = [];
3126 }
3127
3128 for (i = 0; i < 7; i++) {
3129 // make the regex if we don't have it already
3130
3131 mom = createUTC([2000, 1]).day(i);
3132 if (strict && !this._fullWeekdaysParse[i]) {
3133 this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
3134 this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
3135 this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
3136 }
3137 if (!this._weekdaysParse[i]) {
3138 regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
3139 this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
3140 }
3141 // test the regex
3142 if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
3143 return i;
3144 } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
3145 return i;
3146 } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
3147 return i;
3148 } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
3149 return i;
3150 }
3151 }
3152 }
3153
3154 // MOMENTS
3155
3156 function getSetDayOfWeek (input) {
3157 if (!this.isValid()) {
3158 return input != null ? this : NaN;
3159 }
3160 var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
3161 if (input != null) {
3162 input = parseWeekday(input, this.localeData());
3163 return this.add(input - day, 'd');
3164 } else {
3165 return day;
3166 }
3167 }
3168
3169 function getSetLocaleDayOfWeek (input) {
3170 if (!this.isValid()) {
3171 return input != null ? this : NaN;
3172 }
3173 var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
3174 return input == null ? weekday : this.add(input - weekday, 'd');
3175 }
3176
3177 function getSetISODayOfWeek (input) {
3178 if (!this.isValid()) {
3179 return input != null ? this : NaN;
3180 }
3181
3182 // behaves the same as moment#day except
3183 // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
3184 // as a setter, sunday should belong to the previous week.
3185
3186 if (input != null) {
3187 var weekday = parseIsoWeekday(input, this.localeData());
3188 return this.day(this.day() % 7 ? weekday : weekday - 7);
3189 } else {
3190 return this.day() || 7;
3191 }
3192 }
3193
3194 var defaultWeekdaysRegex = matchWord;
3195 function weekdaysRegex (isStrict) {
3196 if (this._weekdaysParseExact) {
3197 if (!hasOwnProp(this, '_weekdaysRegex')) {
3198 computeWeekdaysParse.call(this);
3199 }
3200 if (isStrict) {
3201 return this._weekdaysStrictRegex;
3202 } else {
3203 return this._weekdaysRegex;
3204 }
3205 } else {
3206 if (!hasOwnProp(this, '_weekdaysRegex')) {
3207 this._weekdaysRegex = defaultWeekdaysRegex;
3208 }
3209 return this._weekdaysStrictRegex && isStrict ?
3210 this._weekdaysStrictRegex : this._weekdaysRegex;
3211 }
3212 }
3213
3214 var defaultWeekdaysShortRegex = matchWord;
3215 function weekdaysShortRegex (isStrict) {
3216 if (this._weekdaysParseExact) {
3217 if (!hasOwnProp(this, '_weekdaysRegex')) {
3218 computeWeekdaysParse.call(this);
3219 }
3220 if (isStrict) {
3221 return this._weekdaysShortStrictRegex;
3222 } else {
3223 return this._weekdaysShortRegex;
3224 }
3225 } else {
3226 if (!hasOwnProp(this, '_weekdaysShortRegex')) {
3227 this._weekdaysShortRegex = defaultWeekdaysShortRegex;
3228 }
3229 return this._weekdaysShortStrictRegex && isStrict ?
3230 this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
3231 }
3232 }
3233
3234 var defaultWeekdaysMinRegex = matchWord;
3235 function weekdaysMinRegex (isStrict) {
3236 if (this._weekdaysParseExact) {
3237 if (!hasOwnProp(this, '_weekdaysRegex')) {
3238 computeWeekdaysParse.call(this);
3239 }
3240 if (isStrict) {
3241 return this._weekdaysMinStrictRegex;
3242 } else {
3243 return this._weekdaysMinRegex;
3244 }
3245 } else {
3246 if (!hasOwnProp(this, '_weekdaysMinRegex')) {
3247 this._weekdaysMinRegex = defaultWeekdaysMinRegex;
3248 }
3249 return this._weekdaysMinStrictRegex && isStrict ?
3250 this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
3251 }
3252 }
3253
3254
3255 function computeWeekdaysParse () {
3256 function cmpLenRev(a, b) {
3257 return b.length - a.length;
3258 }
3259
3260 var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
3261 i, mom, minp, shortp, longp;
3262 for (i = 0; i < 7; i++) {
3263 // make the regex if we don't have it already
3264 mom = createUTC([2000, 1]).day(i);
3265 minp = this.weekdaysMin(mom, '');
3266 shortp = this.weekdaysShort(mom, '');
3267 longp = this.weekdays(mom, '');
3268 minPieces.push(minp);
3269 shortPieces.push(shortp);
3270 longPieces.push(longp);
3271 mixedPieces.push(minp);
3272 mixedPieces.push(shortp);
3273 mixedPieces.push(longp);
3274 }
3275 // Sorting makes sure if one weekday (or abbr) is a prefix of another it
3276 // will match the longer piece.
3277 minPieces.sort(cmpLenRev);
3278 shortPieces.sort(cmpLenRev);
3279 longPieces.sort(cmpLenRev);
3280 mixedPieces.sort(cmpLenRev);
3281 for (i = 0; i < 7; i++) {
3282 shortPieces[i] = regexEscape(shortPieces[i]);
3283 longPieces[i] = regexEscape(longPieces[i]);
3284 mixedPieces[i] = regexEscape(mixedPieces[i]);
3285 }
3286
3287 this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
3288 this._weekdaysShortRegex = this._weekdaysRegex;
3289 this._weekdaysMinRegex = this._weekdaysRegex;
3290
3291 this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
3292 this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
3293 this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
3294 }
3295
3296 // FORMATTING
3297
3298 function hFormat() {
3299 return this.hours() % 12 || 12;
3300 }
3301
3302 function kFormat() {
3303 return this.hours() || 24;
3304 }
3305
3306 addFormatToken('H', ['HH', 2], 0, 'hour');
3307 addFormatToken('h', ['hh', 2], 0, hFormat);
3308 addFormatToken('k', ['kk', 2], 0, kFormat);
3309
3310 addFormatToken('hmm', 0, 0, function () {
3311 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
3312 });
3313
3314 addFormatToken('hmmss', 0, 0, function () {
3315 return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
3316 zeroFill(this.seconds(), 2);
3317 });
3318
3319 addFormatToken('Hmm', 0, 0, function () {
3320 return '' + this.hours() + zeroFill(this.minutes(), 2);
3321 });
3322
3323 addFormatToken('Hmmss', 0, 0, function () {
3324 return '' + this.hours() + zeroFill(this.minutes(), 2) +
3325 zeroFill(this.seconds(), 2);
3326 });
3327
3328 function meridiem (token, lowercase) {
3329 addFormatToken(token, 0, 0, function () {
3330 return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
3331 });
3332 }
3333
3334 meridiem('a', true);
3335 meridiem('A', false);
3336
3337 // ALIASES
3338
3339 addUnitAlias('hour', 'h');
3340
3341 // PRIORITY
3342 addUnitPriority('hour', 13);
3343
3344 // PARSING
3345
3346 function matchMeridiem (isStrict, locale) {
3347 return locale._meridiemParse;
3348 }
3349
3350 addRegexToken('a', matchMeridiem);
3351 addRegexToken('A', matchMeridiem);
3352 addRegexToken('H', match1to2);
3353 addRegexToken('h', match1to2);
3354 addRegexToken('k', match1to2);
3355 addRegexToken('HH', match1to2, match2);
3356 addRegexToken('hh', match1to2, match2);
3357 addRegexToken('kk', match1to2, match2);
3358
3359 addRegexToken('hmm', match3to4);
3360 addRegexToken('hmmss', match5to6);
3361 addRegexToken('Hmm', match3to4);
3362 addRegexToken('Hmmss', match5to6);
3363
3364 addParseToken(['H', 'HH'], HOUR);
3365 addParseToken(['k', 'kk'], function (input, array, config) {
3366 var kInput = toInt(input);
3367 array[HOUR] = kInput === 24 ? 0 : kInput;
3368 });
3369 addParseToken(['a', 'A'], function (input, array, config) {
3370 config._isPm = config._locale.isPM(input);
3371 config._meridiem = input;
3372 });
3373 addParseToken(['h', 'hh'], function (input, array, config) {
3374 array[HOUR] = toInt(input);
3375 getParsingFlags(config).bigHour = true;
3376 });
3377 addParseToken('hmm', function (input, array, config) {
3378 var pos = input.length - 2;
3379 array[HOUR] = toInt(input.substr(0, pos));
3380 array[MINUTE] = toInt(input.substr(pos));
3381 getParsingFlags(config).bigHour = true;
3382 });
3383 addParseToken('hmmss', function (input, array, config) {
3384 var pos1 = input.length - 4;
3385 var pos2 = input.length - 2;
3386 array[HOUR] = toInt(input.substr(0, pos1));
3387 array[MINUTE] = toInt(input.substr(pos1, 2));
3388 array[SECOND] = toInt(input.substr(pos2));
3389 getParsingFlags(config).bigHour = true;
3390 });
3391 addParseToken('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 });
3396 addParseToken('Hmmss', function (input, array, config) {
3397 var pos1 = input.length - 4;
3398 var pos2 = input.length - 2;
3399 array[HOUR] = toInt(input.substr(0, pos1));
3400 array[MINUTE] = toInt(input.substr(pos1, 2));
3401 array[SECOND] = toInt(input.substr(pos2));
3402 });
3403
3404 // LOCALES
3405
3406 function localeIsPM (input) {
3407 // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
3408 // Using charAt should be more compatible.
3409 return ((input + '').toLowerCase().charAt(0) === 'p');
3410 }
3411
3412 var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
3413 function localeMeridiem (hours, minutes, isLower) {
3414 if (hours > 11) {
3415 return isLower ? 'pm' : 'PM';
3416 } else {
3417 return isLower ? 'am' : 'AM';
3418 }
3419 }
3420
3421
3422 // MOMENTS
3423
3424 // Setting the hour should keep the time, because the user explicitly
3425 // specified which hour he wants. So trying to maintain the same hour (in
3426 // a new timezone) makes sense. Adding/subtracting hours does not follow
3427 // this rule.
3428 var getSetHour = makeGetSet('Hours', true);
3429
3430 // months
3431 // week
3432 // weekdays
3433 // meridiem
3434 var baseConfig = {
3435 calendar: defaultCalendar,
3436 longDateFormat: defaultLongDateFormat,
3437 invalidDate: defaultInvalidDate,
3438 ordinal: defaultOrdinal,
3439 dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
3440 relativeTime: defaultRelativeTime,
3441
3442 months: defaultLocaleMonths,
3443 monthsShort: defaultLocaleMonthsShort,
3444
3445 week: defaultLocaleWeek,
3446
3447 weekdays: defaultLocaleWeekdays,
3448 weekdaysMin: defaultLocaleWeekdaysMin,
3449 weekdaysShort: defaultLocaleWeekdaysShort,
3450
3451 meridiemParse: defaultLocaleMeridiemParse
3452 };
3453
3454 // internal storage for locale config files
3455 var locales = {};
3456 var localeFamilies = {};
3457 var globalLocale;
3458
3459 function normalizeLocale(key) {
3460 return key ? key.toLowerCase().replace('_', '-') : key;
3461 }
3462
3463 // pick the locale from the array
3464 // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
3465 // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
3466 function chooseLocale(names) {
3467 var i = 0, j, next, locale, split;
3468
3469 while (i < names.length) {
3470 split = normalizeLocale(names[i]).split('-');
3471 j = split.length;
3472 next = normalizeLocale(names[i + 1]);
3473 next = next ? next.split('-') : null;
3474 while (j > 0) {
3475 locale = loadLocale(split.slice(0, j).join('-'));
3476 if (locale) {
3477 return locale;
3478 }
3479 if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
3480 //the next array item is better than a shallower substring of this one
3481 break;
3482 }
3483 j--;
3484 }
3485 i++;
3486 }
3487 return null;
3488 }
3489
3490 function loadLocale(name) {
3491 var oldLocale = null;
3492 // TODO: Find a better way to register and load all the locales in Node
3493 if (!locales[name] && (typeof module !== 'undefined') &&
3494 module && module.exports) {
3495 try {
3496 oldLocale = globalLocale._abbr;
3497 require('./locale/' + name);
3498 // because defineLocale currently also sets the global locale, we
3499 // want to undo that for lazy loaded locales
3500 getSetGlobalLocale(oldLocale);
3501 } catch (e) { }
3502 }
3503 return locales[name];
3504 }
3505
3506 // This function will load locale and then set the global locale. If
3507 // no arguments are passed in, it will simply return the current global
3508 // locale key.
3509 function getSetGlobalLocale (key, values) {
3510 var data;
3511 if (key) {
3512 if (isUndefined(values)) {
3513 data = getLocale(key);
3514 }
3515 else {
3516 data = defineLocale(key, values);
3517 }
3518
3519 if (data) {
3520 // moment.duration._locale = moment._locale = data;
3521 globalLocale = data;
3522 }
3523 }
3524
3525 return globalLocale._abbr;
3526 }
3527
3528 function defineLocale (name, config) {
3529 if (config !== null) {
3530 var parentConfig = baseConfig;
3531 config.abbr = name;
3532 if (locales[name] != null) {
3533 deprecateSimple('defineLocaleOverride',
3534 'use moment.updateLocale(localeName, config) to change ' +
3535 'an existing locale. moment.defineLocale(localeName, ' +
3536 'config) should only be used for creating a new locale ' +
3537 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
3538 parentConfig = locales[name]._config;
3539 } else if (config.parentLocale != null) {
3540 if (locales[config.parentLocale] != null) {
3541 parentConfig = locales[config.parentLocale]._config;
3542 } else {
3543 if (!localeFamilies[config.parentLocale]) {
3544 localeFamilies[config.parentLocale] = [];
3545 }
3546 localeFamilies[config.parentLocale].push({
3547 name: name,
3548 config: config
3549 });
3550 return null;
3551 }
3552 }
3553 locales[name] = new Locale(mergeConfigs(parentConfig, config));
3554
3555 if (localeFamilies[name]) {
3556 localeFamilies[name].forEach(function (x) {
3557 defineLocale(x.name, x.config);
3558 });
3559 }
3560
3561 // backwards compat for now: also set the locale
3562 // make sure we set the locale AFTER all child locales have been
3563 // created, so we won't end up with the child locale set.
3564 getSetGlobalLocale(name);
3565
3566
3567 return locales[name];
3568 } else {
3569 // useful for testing
3570 delete locales[name];
3571 return null;
3572 }
3573 }
3574
3575 function updateLocale(name, config) {
3576 if (config != null) {
3577 var locale, parentConfig = baseConfig;
3578 // MERGE
3579 if (locales[name] != null) {
3580 parentConfig = locales[name]._config;
3581 }
3582 config = mergeConfigs(parentConfig, config);
3583 locale = new Locale(config);
3584 locale.parentLocale = locales[name];
3585 locales[name] = locale;
3586
3587 // backwards compat for now: also set the locale
3588 getSetGlobalLocale(name);
3589 } else {
3590 // pass null for config to unupdate, useful for tests
3591 if (locales[name] != null) {
3592 if (locales[name].parentLocale != null) {
3593 locales[name] = locales[name].parentLocale;
3594 } else if (locales[name] != null) {
3595 delete locales[name];
3596 }
3597 }
3598 }
3599 return locales[name];
3600 }
3601
3602 // returns locale data
3603 function getLocale (key) {
3604 var locale;
3605
3606 if (key && key._locale && key._locale._abbr) {
3607 key = key._locale._abbr;
3608 }
3609
3610 if (!key) {
3611 return globalLocale;
3612 }
3613
3614 if (!isArray(key)) {
3615 //short-circuit everything else
3616 locale = loadLocale(key);
3617 if (locale) {
3618 return locale;
3619 }
3620 key = [key];
3621 }
3622
3623 return chooseLocale(key);
3624 }
3625
3626 function listLocales() {
3627 return keys$1(locales);
3628 }
3629
3630 function checkOverflow (m) {
3631 var overflow;
3632 var a = m._a;
3633
3634 if (a && getParsingFlags(m).overflow === -2) {
3635 overflow =
3636 a[MONTH] < 0 || a[MONTH] > 11 ? MONTH :
3637 a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
3638 a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
3639 a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE :
3640 a[SECOND] < 0 || a[SECOND] > 59 ? SECOND :
3641 a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
3642 -1;
3643
3644 if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
3645 overflow = DATE;
3646 }
3647 if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
3648 overflow = WEEK;
3649 }
3650 if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
3651 overflow = WEEKDAY;
3652 }
3653
3654 getParsingFlags(m).overflow = overflow;
3655 }
3656
3657 return m;
3658 }
3659
3660 // iso 8601 regex
3661 // 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)
3662 var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
3663 var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
3664
3665 var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
3666
3667 var isoDates = [
3668 ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
3669 ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
3670 ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
3671 ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
3672 ['YYYY-DDD', /\d{4}-\d{3}/],
3673 ['YYYY-MM', /\d{4}-\d\d/, false],
3674 ['YYYYYYMMDD', /[+-]\d{10}/],
3675 ['YYYYMMDD', /\d{8}/],
3676 // YYYYMM is NOT allowed by the standard
3677 ['GGGG[W]WWE', /\d{4}W\d{3}/],
3678 ['GGGG[W]WW', /\d{4}W\d{2}/, false],
3679 ['YYYYDDD', /\d{7}/]
3680 ];
3681
3682 // iso time formats and regexes
3683 var isoTimes = [
3684 ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
3685 ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
3686 ['HH:mm:ss', /\d\d:\d\d:\d\d/],
3687 ['HH:mm', /\d\d:\d\d/],
3688 ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
3689 ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
3690 ['HHmmss', /\d\d\d\d\d\d/],
3691 ['HHmm', /\d\d\d\d/],
3692 ['HH', /\d\d/]
3693 ];
3694
3695 var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
3696
3697 // date from iso format
3698 function configFromISO(config) {
3699 var i, l,
3700 string = config._i,
3701 match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
3702 allowTime, dateFormat, timeFormat, tzFormat;
3703
3704 if (match) {
3705 getParsingFlags(config).iso = true;
3706
3707 for (i = 0, l = isoDates.length; i < l; i++) {
3708 if (isoDates[i][1].exec(match[1])) {
3709 dateFormat = isoDates[i][0];
3710 allowTime = isoDates[i][2] !== false;
3711 break;
3712 }
3713 }
3714 if (dateFormat == null) {
3715 config._isValid = false;
3716 return;
3717 }
3718 if (match[3]) {
3719 for (i = 0, l = isoTimes.length; i < l; i++) {
3720 if (isoTimes[i][1].exec(match[3])) {
3721 // match[2] should be 'T' or space
3722 timeFormat = (match[2] || ' ') + isoTimes[i][0];
3723 break;
3724 }
3725 }
3726 if (timeFormat == null) {
3727 config._isValid = false;
3728 return;
3729 }
3730 }
3731 if (!allowTime && timeFormat != null) {
3732 config._isValid = false;
3733 return;
3734 }
3735 if (match[4]) {
3736 if (tzRegex.exec(match[4])) {
3737 tzFormat = 'Z';
3738 } else {
3739 config._isValid = false;
3740 return;
3741 }
3742 }
3743 config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
3744 configFromStringAndFormat(config);
3745 } else {
3746 config._isValid = false;
3747 }
3748 }
3749
3750 // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
3751 var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;
3752
3753 // date and time from ref 2822 format
3754 function configFromRFC2822(config) {
3755 var string, match, dayFormat,
3756 dateFormat, timeFormat, tzFormat;
3757 var timezones = {
3758 ' GMT': ' +0000',
3759 ' EDT': ' -0400',
3760 ' EST': ' -0500',
3761 ' CDT': ' -0500',
3762 ' CST': ' -0600',
3763 ' MDT': ' -0600',
3764 ' MST': ' -0700',
3765 ' PDT': ' -0700',
3766 ' PST': ' -0800'
3767 };
3768 var military = 'YXWVUTSRQPONZABCDEFGHIKLM';
3769 var timezone, timezoneIndex;
3770
3771 string = config._i
3772 .replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace
3773 .replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space
3774 .replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces
3775 match = basicRfcRegex.exec(string);
3776
3777 if (match) {
3778 dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : '';
3779 dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY ');
3780 timeFormat = 'HH:mm' + (match[4] ? ':ss' : '');
3781
3782 // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
3783 if (match[1]) { // day of week given
3784 var momentDate = new Date(match[2]);
3785 var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()];
3786
3787 if (match[1].substr(0,3) !== momentDay) {
3788 getParsingFlags(config).weekdayMismatch = true;
3789 config._isValid = false;
3790 return;
3791 }
3792 }
3793
3794 switch (match[5].length) {
3795 case 2: // military
3796 if (timezoneIndex === 0) {
3797 timezone = ' +0000';
3798 } else {
3799 timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12;
3800 timezone = ((timezoneIndex < 0) ? ' -' : ' +') +
3801 (('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00';
3802 }
3803 break;
3804 case 4: // Zone
3805 timezone = timezones[match[5]];
3806 break;
3807 default: // UT or +/-9999
3808 timezone = timezones[' GMT'];
3809 }
3810 match[5] = timezone;
3811 config._i = match.splice(1).join('');
3812 tzFormat = ' ZZ';
3813 config._f = dayFormat + dateFormat + timeFormat + tzFormat;
3814 configFromStringAndFormat(config);
3815 getParsingFlags(config).rfc2822 = true;
3816 } else {
3817 config._isValid = false;
3818 }
3819 }
3820
3821 // date from iso format or fallback
3822 function configFromString(config) {
3823 var matched = aspNetJsonRegex.exec(config._i);
3824
3825 if (matched !== null) {
3826 config._d = new Date(+matched[1]);
3827 return;
3828 }
3829
3830 configFromISO(config);
3831 if (config._isValid === false) {
3832 delete config._isValid;
3833 } else {
3834 return;
3835 }
3836
3837 configFromRFC2822(config);
3838 if (config._isValid === false) {
3839 delete config._isValid;
3840 } else {
3841 return;
3842 }
3843
3844 // Final attempt, use Input Fallback
3845 hooks.createFromInputFallback(config);
3846 }
3847
3848 hooks.createFromInputFallback = deprecate(
3849 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' +
3850 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' +
3851 'discouraged and will be removed in an upcoming major release. Please refer to ' +
3852 'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
3853 function (config) {
3854 config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
3855 }
3856 );
3857
3858 // Pick the first defined of two or three arguments.
3859 function defaults(a, b, c) {
3860 if (a != null) {
3861 return a;
3862 }
3863 if (b != null) {
3864 return b;
3865 }
3866 return c;
3867 }
3868
3869 function currentDateArray(config) {
3870 // hooks is actually the exported moment object
3871 var nowValue = new Date(hooks.now());
3872 if (config._useUTC) {
3873 return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
3874 }
3875 return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
3876 }
3877
3878 // convert an array to a date.
3879 // the array should mirror the parameters below
3880 // note: all values past the year are optional and will default to the lowest possible value.
3881 // [year, month, day , hour, minute, second, millisecond]
3882 function configFromArray (config) {
3883 var i, date, input = [], currentDate, yearToUse;
3884
3885 if (config._d) {
3886 return;
3887 }
3888
3889 currentDate = currentDateArray(config);
3890
3891 //compute day of the year from weeks and weekdays
3892 if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
3893 dayOfYearFromWeekInfo(config);
3894 }
3895
3896 //if the day of the year is set, figure out what it is
3897 if (config._dayOfYear != null) {
3898 yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
3899
3900 if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
3901 getParsingFlags(config)._overflowDayOfYear = true;
3902 }
3903
3904 date = createUTCDate(yearToUse, 0, config._dayOfYear);
3905 config._a[MONTH] = date.getUTCMonth();
3906 config._a[DATE] = date.getUTCDate();
3907 }
3908
3909 // Default to current date.
3910 // * if no year, month, day of month are given, default to today
3911 // * if day of month is given, default month and year
3912 // * if month is given, default only year
3913 // * if year is given, don't default anything
3914 for (i = 0; i < 3 && config._a[i] == null; ++i) {
3915 config._a[i] = input[i] = currentDate[i];
3916 }
3917
3918 // Zero out whatever was not defaulted, including time
3919 for (; i < 7; i++) {
3920 config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
3921 }
3922
3923 // Check for 24:00:00.000
3924 if (config._a[HOUR] === 24 &&
3925 config._a[MINUTE] === 0 &&
3926 config._a[SECOND] === 0 &&
3927 config._a[MILLISECOND] === 0) {
3928 config._nextDay = true;
3929 config._a[HOUR] = 0;
3930 }
3931
3932 config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
3933 // Apply timezone offset from input. The actual utcOffset can be changed
3934 // with parseZone.
3935 if (config._tzm != null) {
3936 config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
3937 }
3938
3939 if (config._nextDay) {
3940 config._a[HOUR] = 24;
3941 }
3942 }
3943
3944 function dayOfYearFromWeekInfo(config) {
3945 var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
3946
3947 w = config._w;
3948 if (w.GG != null || w.W != null || w.E != null) {
3949 dow = 1;
3950 doy = 4;
3951
3952 // TODO: We need to take the current isoWeekYear, but that depends on
3953 // how we interpret now (local, utc, fixed offset). So create
3954 // a now version of current config (take local/utc/offset flags, and
3955 // create now).
3956 weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
3957 week = defaults(w.W, 1);
3958 weekday = defaults(w.E, 1);
3959 if (weekday < 1 || weekday > 7) {
3960 weekdayOverflow = true;
3961 }
3962 } else {
3963 dow = config._locale._week.dow;
3964 doy = config._locale._week.doy;
3965
3966 var curWeek = weekOfYear(createLocal(), dow, doy);
3967
3968 weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);
3969
3970 // Default to current week.
3971 week = defaults(w.w, curWeek.week);
3972
3973 if (w.d != null) {
3974 // weekday -- low day numbers are considered next week
3975 weekday = w.d;
3976 if (weekday < 0 || weekday > 6) {
3977 weekdayOverflow = true;
3978 }
3979 } else if (w.e != null) {
3980 // local weekday -- counting starts from begining of week
3981 weekday = w.e + dow;
3982 if (w.e < 0 || w.e > 6) {
3983 weekdayOverflow = true;
3984 }
3985 } else {
3986 // default to begining of week
3987 weekday = dow;
3988 }
3989 }
3990 if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
3991 getParsingFlags(config)._overflowWeeks = true;
3992 } else if (weekdayOverflow != null) {
3993 getParsingFlags(config)._overflowWeekday = true;
3994 } else {
3995 temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
3996 config._a[YEAR] = temp.year;
3997 config._dayOfYear = temp.dayOfYear;
3998 }
3999 }
4000
4001 // constant that refers to the ISO standard
4002 hooks.ISO_8601 = function () {};
4003
4004 // constant that refers to the RFC 2822 form
4005 hooks.RFC_2822 = function () {};
4006
4007 // date from string and format string
4008 function configFromStringAndFormat(config) {
4009 // TODO: Move this to another part of the creation flow to prevent circular deps
4010 if (config._f === hooks.ISO_8601) {
4011 configFromISO(config);
4012 return;
4013 }
4014 if (config._f === hooks.RFC_2822) {
4015 configFromRFC2822(config);
4016 return;
4017 }
4018 config._a = [];
4019 getParsingFlags(config).empty = true;
4020
4021 // This array is used to make a Date, either with `new Date` or `Date.UTC`
4022 var string = '' + config._i,
4023 i, parsedInput, tokens, token, skipped,
4024 stringLength = string.length,
4025 totalParsedInputLength = 0;
4026
4027 tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
4028
4029 for (i = 0; i < tokens.length; i++) {
4030 token = tokens[i];
4031 parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
4032 // console.log('token', token, 'parsedInput', parsedInput,
4033 // 'regex', getParseRegexForToken(token, config));
4034 if (parsedInput) {
4035 skipped = string.substr(0, string.indexOf(parsedInput));
4036 if (skipped.length > 0) {
4037 getParsingFlags(config).unusedInput.push(skipped);
4038 }
4039 string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
4040 totalParsedInputLength += parsedInput.length;
4041 }
4042 // don't parse if it's not a known token
4043 if (formatTokenFunctions[token]) {
4044 if (parsedInput) {
4045 getParsingFlags(config).empty = false;
4046 }
4047 else {
4048 getParsingFlags(config).unusedTokens.push(token);
4049 }
4050 addTimeToArrayFromToken(token, parsedInput, config);
4051 }
4052 else if (config._strict && !parsedInput) {
4053 getParsingFlags(config).unusedTokens.push(token);
4054 }
4055 }
4056
4057 // add remaining unparsed input length to the string
4058 getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
4059 if (string.length > 0) {
4060 getParsingFlags(config).unusedInput.push(string);
4061 }
4062
4063 // clear _12h flag if hour is <= 12
4064 if (config._a[HOUR] <= 12 &&
4065 getParsingFlags(config).bigHour === true &&
4066 config._a[HOUR] > 0) {
4067 getParsingFlags(config).bigHour = undefined;
4068 }
4069
4070 getParsingFlags(config).parsedDateParts = config._a.slice(0);
4071 getParsingFlags(config).meridiem = config._meridiem;
4072 // handle meridiem
4073 config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
4074
4075 configFromArray(config);
4076 checkOverflow(config);
4077 }
4078
4079
4080 function meridiemFixWrap (locale, hour, meridiem) {
4081 var isPm;
4082
4083 if (meridiem == null) {
4084 // nothing to do
4085 return hour;
4086 }
4087 if (locale.meridiemHour != null) {
4088 return locale.meridiemHour(hour, meridiem);
4089 } else if (locale.isPM != null) {
4090 // Fallback
4091 isPm = locale.isPM(meridiem);
4092 if (isPm && hour < 12) {
4093 hour += 12;
4094 }
4095 if (!isPm && hour === 12) {
4096 hour = 0;
4097 }
4098 return hour;
4099 } else {
4100 // this is not supposed to happen
4101 return hour;
4102 }
4103 }
4104
4105 // date from string and array of format strings
4106 function configFromStringAndArray(config) {
4107 var tempConfig,
4108 bestMoment,
4109
4110 scoreToBeat,
4111 i,
4112 currentScore;
4113
4114 if (config._f.length === 0) {
4115 getParsingFlags(config).invalidFormat = true;
4116 config._d = new Date(NaN);
4117 return;
4118 }
4119
4120 for (i = 0; i < config._f.length; i++) {
4121 currentScore = 0;
4122 tempConfig = copyConfig({}, config);
4123 if (config._useUTC != null) {
4124 tempConfig._useUTC = config._useUTC;
4125 }
4126 tempConfig._f = config._f[i];
4127 configFromStringAndFormat(tempConfig);
4128
4129 if (!isValid(tempConfig)) {
4130 continue;
4131 }
4132
4133 // if there is any input that was not parsed add a penalty for that format
4134 currentScore += getParsingFlags(tempConfig).charsLeftOver;
4135
4136 //or tokens
4137 currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
4138
4139 getParsingFlags(tempConfig).score = currentScore;
4140
4141 if (scoreToBeat == null || currentScore < scoreToBeat) {
4142 scoreToBeat = currentScore;
4143 bestMoment = tempConfig;
4144 }
4145 }
4146
4147 extend(config, bestMoment || tempConfig);
4148 }
4149
4150 function configFromObject(config) {
4151 if (config._d) {
4152 return;
4153 }
4154
4155 var i = normalizeObjectUnits(config._i);
4156 config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
4157 return obj && parseInt(obj, 10);
4158 });
4159
4160 configFromArray(config);
4161 }
4162
4163 function createFromConfig (config) {
4164 var res = new Moment(checkOverflow(prepareConfig(config)));
4165 if (res._nextDay) {
4166 // Adding is smart enough around DST
4167 res.add(1, 'd');
4168 res._nextDay = undefined;
4169 }
4170
4171 return res;
4172 }
4173
4174 function prepareConfig (config) {
4175 var input = config._i,
4176 format = config._f;
4177
4178 config._locale = config._locale || getLocale(config._l);
4179
4180 if (input === null || (format === undefined && input === '')) {
4181 return createInvalid({nullInput: true});
4182 }
4183
4184 if (typeof input === 'string') {
4185 config._i = input = config._locale.preparse(input);
4186 }
4187
4188 if (isMoment(input)) {
4189 return new Moment(checkOverflow(input));
4190 } else if (isDate(input)) {
4191 config._d = input;
4192 } else if (isArray(format)) {
4193 configFromStringAndArray(config);
4194 } else if (format) {
4195 configFromStringAndFormat(config);
4196 } else {
4197 configFromInput(config);
4198 }
4199
4200 if (!isValid(config)) {
4201 config._d = null;
4202 }
4203
4204 return config;
4205 }
4206
4207 function configFromInput(config) {
4208 var input = config._i;
4209 if (isUndefined(input)) {
4210 config._d = new Date(hooks.now());
4211 } else if (isDate(input)) {
4212 config._d = new Date(input.valueOf());
4213 } else if (typeof input === 'string') {
4214 configFromString(config);
4215 } else if (isArray(input)) {
4216 config._a = map(input.slice(0), function (obj) {
4217 return parseInt(obj, 10);
4218 });
4219 configFromArray(config);
4220 } else if (isObject(input)) {
4221 configFromObject(config);
4222 } else if (isNumber(input)) {
4223 // from milliseconds
4224 config._d = new Date(input);
4225 } else {
4226 hooks.createFromInputFallback(config);
4227 }
4228 }
4229
4230 function createLocalOrUTC (input, format, locale, strict, isUTC) {
4231 var c = {};
4232
4233 if (locale === true || locale === false) {
4234 strict = locale;
4235 locale = undefined;
4236 }
4237
4238 if ((isObject(input) && isObjectEmpty(input)) ||
4239 (isArray(input) && input.length === 0)) {
4240 input = undefined;
4241 }
4242 // object construction must be done this way.
4243 // https://github.com/moment/moment/issues/1423
4244 c._isAMomentObject = true;
4245 c._useUTC = c._isUTC = isUTC;
4246 c._l = locale;
4247 c._i = input;
4248 c._f = format;
4249 c._strict = strict;
4250
4251 return createFromConfig(c);
4252 }
4253
4254 function createLocal (input, format, locale, strict) {
4255 return createLocalOrUTC(input, format, locale, strict, false);
4256 }
4257
4258 var prototypeMin = deprecate(
4259 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
4260 function () {
4261 var other = createLocal.apply(null, arguments);
4262 if (this.isValid() && other.isValid()) {
4263 return other < this ? this : other;
4264 } else {
4265 return createInvalid();
4266 }
4267 }
4268 );
4269
4270 var prototypeMax = deprecate(
4271 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
4272 function () {
4273 var other = createLocal.apply(null, arguments);
4274 if (this.isValid() && other.isValid()) {
4275 return other > this ? this : other;
4276 } else {
4277 return createInvalid();
4278 }
4279 }
4280 );
4281
4282 // Pick a moment m from moments so that m[fn](other) is true for all
4283 // other. This relies on the function fn to be transitive.
4284 //
4285 // moments should either be an array of moment objects or an array, whose
4286 // first element is an array of moment objects.
4287 function pickBy(fn, moments) {
4288 var res, i;
4289 if (moments.length === 1 && isArray(moments[0])) {
4290 moments = moments[0];
4291 }
4292 if (!moments.length) {
4293 return createLocal();
4294 }
4295 res = moments[0];
4296 for (i = 1; i < moments.length; ++i) {
4297 if (!moments[i].isValid() || moments[i][fn](res)) {
4298 res = moments[i];
4299 }
4300 }
4301 return res;
4302 }
4303
4304 // TODO: Use [].sort instead?
4305 function min () {
4306 var args = [].slice.call(arguments, 0);
4307
4308 return pickBy('isBefore', args);
4309 }
4310
4311 function max () {
4312 var args = [].slice.call(arguments, 0);
4313
4314 return pickBy('isAfter', args);
4315 }
4316
4317 var now = function () {
4318 return Date.now ? Date.now() : +(new Date());
4319 };
4320
4321 var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];
4322
4323 function isDurationValid(m) {
4324 for (var key in m) {
4325 if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
4326 return false;
4327 }
4328 }
4329
4330 var unitHasDecimal = false;
4331 for (var i = 0; i < ordering.length; ++i) {
4332 if (m[ordering[i]]) {
4333 if (unitHasDecimal) {
4334 return false; // only allow non-integers for smallest unit
4335 }
4336 if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
4337 unitHasDecimal = true;
4338 }
4339 }
4340 }
4341
4342 return true;
4343 }
4344
4345 function isValid$1() {
4346 return this._isValid;
4347 }
4348
4349 function createInvalid$1() {
4350 return createDuration(NaN);
4351 }
4352
4353 function Duration (duration) {
4354 var normalizedInput = normalizeObjectUnits(duration),
4355 years = normalizedInput.year || 0,
4356 quarters = normalizedInput.quarter || 0,
4357 months = normalizedInput.month || 0,
4358 weeks = normalizedInput.week || 0,
4359 days = normalizedInput.day || 0,
4360 hours = normalizedInput.hour || 0,
4361 minutes = normalizedInput.minute || 0,
4362 seconds = normalizedInput.second || 0,
4363 milliseconds = normalizedInput.millisecond || 0;
4364
4365 this._isValid = isDurationValid(normalizedInput);
4366
4367 // representation for dateAddRemove
4368 this._milliseconds = +milliseconds +
4369 seconds * 1e3 + // 1000
4370 minutes * 6e4 + // 1000 * 60
4371 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
4372 // Because of dateAddRemove treats 24 hours as different from a
4373 // day when working around DST, we need to store them separately
4374 this._days = +days +
4375 weeks * 7;
4376 // It is impossible translate months into days without knowing
4377 // which months you are are talking about, so we have to store
4378 // it separately.
4379 this._months = +months +
4380 quarters * 3 +
4381 years * 12;
4382
4383 this._data = {};
4384
4385 this._locale = getLocale();
4386
4387 this._bubble();
4388 }
4389
4390 function isDuration (obj) {
4391 return obj instanceof Duration;
4392 }
4393
4394 function absRound (number) {
4395 if (number < 0) {
4396 return Math.round(-1 * number) * -1;
4397 } else {
4398 return Math.round(number);
4399 }
4400 }
4401
4402 // FORMATTING
4403
4404 function offset (token, separator) {
4405 addFormatToken(token, 0, 0, function () {
4406 var offset = this.utcOffset();
4407 var sign = '+';
4408 if (offset < 0) {
4409 offset = -offset;
4410 sign = '-';
4411 }
4412 return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
4413 });
4414 }
4415
4416 offset('Z', ':');
4417 offset('ZZ', '');
4418
4419 // PARSING
4420
4421 addRegexToken('Z', matchShortOffset);
4422 addRegexToken('ZZ', matchShortOffset);
4423 addParseToken(['Z', 'ZZ'], function (input, array, config) {
4424 config._useUTC = true;
4425 config._tzm = offsetFromString(matchShortOffset, input);
4426 });
4427
4428 // HELPERS
4429
4430 // timezone chunker
4431 // '+10:00' > ['10', '00']
4432 // '-1530' > ['-15', '30']
4433 var chunkOffset = /([\+\-]|\d\d)/gi;
4434
4435 function offsetFromString(matcher, string) {
4436 var matches = (string || '').match(matcher);
4437
4438 if (matches === null) {
4439 return null;
4440 }
4441
4442 var chunk = matches[matches.length - 1] || [];
4443 var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
4444 var minutes = +(parts[1] * 60) + toInt(parts[2]);
4445
4446 return minutes === 0 ?
4447 0 :
4448 parts[0] === '+' ? minutes : -minutes;
4449 }
4450
4451 // Return a moment from input, that is local/utc/zone equivalent to model.
4452 function cloneWithOffset(input, model) {
4453 var res, diff;
4454 if (model._isUTC) {
4455 res = model.clone();
4456 diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
4457 // Use low-level api, because this fn is low-level api.
4458 res._d.setTime(res._d.valueOf() + diff);
4459 hooks.updateOffset(res, false);
4460 return res;
4461 } else {
4462 return createLocal(input).local();
4463 }
4464 }
4465
4466 function getDateOffset (m) {
4467 // On Firefox.24 Date#getTimezoneOffset returns a floating point.
4468 // https://github.com/moment/moment/pull/1871
4469 return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
4470 }
4471
4472 // HOOKS
4473
4474 // This function will be called whenever a moment is mutated.
4475 // It is intended to keep the offset in sync with the timezone.
4476 hooks.updateOffset = function () {};
4477
4478 // MOMENTS
4479
4480 // keepLocalTime = true means only change the timezone, without
4481 // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
4482 // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
4483 // +0200, so we adjust the time as needed, to be valid.
4484 //
4485 // Keeping the time actually adds/subtracts (one hour)
4486 // from the actual represented time. That is why we call updateOffset
4487 // a second time. In case it wants us to change the offset again
4488 // _changeInProgress == true case, then we have to adjust, because
4489 // there is no such time in the given timezone.
4490 function getSetOffset (input, keepLocalTime, keepMinutes) {
4491 var offset = this._offset || 0,
4492 localAdjust;
4493 if (!this.isValid()) {
4494 return input != null ? this : NaN;
4495 }
4496 if (input != null) {
4497 if (typeof input === 'string') {
4498 input = offsetFromString(matchShortOffset, input);
4499 if (input === null) {
4500 return this;
4501 }
4502 } else if (Math.abs(input) < 16 && !keepMinutes) {
4503 input = input * 60;
4504 }
4505 if (!this._isUTC && keepLocalTime) {
4506 localAdjust = getDateOffset(this);
4507 }
4508 this._offset = input;
4509 this._isUTC = true;
4510 if (localAdjust != null) {
4511 this.add(localAdjust, 'm');
4512 }
4513 if (offset !== input) {
4514 if (!keepLocalTime || this._changeInProgress) {
4515 addSubtract(this, createDuration(input - offset, 'm'), 1, false);
4516 } else if (!this._changeInProgress) {
4517 this._changeInProgress = true;
4518 hooks.updateOffset(this, true);
4519 this._changeInProgress = null;
4520 }
4521 }
4522 return this;
4523 } else {
4524 return this._isUTC ? offset : getDateOffset(this);
4525 }
4526 }
4527
4528 function getSetZone (input, keepLocalTime) {
4529 if (input != null) {
4530 if (typeof input !== 'string') {
4531 input = -input;
4532 }
4533
4534 this.utcOffset(input, keepLocalTime);
4535
4536 return this;
4537 } else {
4538 return -this.utcOffset();
4539 }
4540 }
4541
4542 function setOffsetToUTC (keepLocalTime) {
4543 return this.utcOffset(0, keepLocalTime);
4544 }
4545
4546 function setOffsetToLocal (keepLocalTime) {
4547 if (this._isUTC) {
4548 this.utcOffset(0, keepLocalTime);
4549 this._isUTC = false;
4550
4551 if (keepLocalTime) {
4552 this.subtract(getDateOffset(this), 'm');
4553 }
4554 }
4555 return this;
4556 }
4557
4558 function setOffsetToParsedOffset () {
4559 if (this._tzm != null) {
4560 this.utcOffset(this._tzm, false, true);
4561 } else if (typeof this._i === 'string') {
4562 var tZone = offsetFromString(matchOffset, this._i);
4563 if (tZone != null) {
4564 this.utcOffset(tZone);
4565 }
4566 else {
4567 this.utcOffset(0, true);
4568 }
4569 }
4570 return this;
4571 }
4572
4573 function hasAlignedHourOffset (input) {
4574 if (!this.isValid()) {
4575 return false;
4576 }
4577 input = input ? createLocal(input).utcOffset() : 0;
4578
4579 return (this.utcOffset() - input) % 60 === 0;
4580 }
4581
4582 function isDaylightSavingTime () {
4583 return (
4584 this.utcOffset() > this.clone().month(0).utcOffset() ||
4585 this.utcOffset() > this.clone().month(5).utcOffset()
4586 );
4587 }
4588
4589 function isDaylightSavingTimeShifted () {
4590 if (!isUndefined(this._isDSTShifted)) {
4591 return this._isDSTShifted;
4592 }
4593
4594 var c = {};
4595
4596 copyConfig(c, this);
4597 c = prepareConfig(c);
4598
4599 if (c._a) {
4600 var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
4601 this._isDSTShifted = this.isValid() &&
4602 compareArrays(c._a, other.toArray()) > 0;
4603 } else {
4604 this._isDSTShifted = false;
4605 }
4606
4607 return this._isDSTShifted;
4608 }
4609
4610 function isLocal () {
4611 return this.isValid() ? !this._isUTC : false;
4612 }
4613
4614 function isUtcOffset () {
4615 return this.isValid() ? this._isUTC : false;
4616 }
4617
4618 function isUtc () {
4619 return this.isValid() ? this._isUTC && this._offset === 0 : false;
4620 }
4621
4622 // ASP.NET json date format regex
4623 var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;
4624
4625 // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
4626 // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
4627 // and further modified to allow for strings containing both week and day
4628 var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;
4629
4630 function createDuration (input, key) {
4631 var duration = input,
4632 // matching against regexp is expensive, do it on demand
4633 match = null,
4634 sign,
4635 ret,
4636 diffRes;
4637
4638 if (isDuration(input)) {
4639 duration = {
4640 ms : input._milliseconds,
4641 d : input._days,
4642 M : input._months
4643 };
4644 } else if (isNumber(input)) {
4645 duration = {};
4646 if (key) {
4647 duration[key] = input;
4648 } else {
4649 duration.milliseconds = input;
4650 }
4651 } else if (!!(match = aspNetRegex.exec(input))) {
4652 sign = (match[1] === '-') ? -1 : 1;
4653 duration = {
4654 y : 0,
4655 d : toInt(match[DATE]) * sign,
4656 h : toInt(match[HOUR]) * sign,
4657 m : toInt(match[MINUTE]) * sign,
4658 s : toInt(match[SECOND]) * sign,
4659 ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
4660 };
4661 } else if (!!(match = isoRegex.exec(input))) {
4662 sign = (match[1] === '-') ? -1 : 1;
4663 duration = {
4664 y : parseIso(match[2], sign),
4665 M : parseIso(match[3], sign),
4666 w : parseIso(match[4], sign),
4667 d : parseIso(match[5], sign),
4668 h : parseIso(match[6], sign),
4669 m : parseIso(match[7], sign),
4670 s : parseIso(match[8], sign)
4671 };
4672 } else if (duration == null) {// checks for null or undefined
4673 duration = {};
4674 } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
4675 diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
4676
4677 duration = {};
4678 duration.ms = diffRes.milliseconds;
4679 duration.M = diffRes.months;
4680 }
4681
4682 ret = new Duration(duration);
4683
4684 if (isDuration(input) && hasOwnProp(input, '_locale')) {
4685 ret._locale = input._locale;
4686 }
4687
4688 return ret;
4689 }
4690
4691 createDuration.fn = Duration.prototype;
4692 createDuration.invalid = createInvalid$1;
4693
4694 function parseIso (inp, sign) {
4695 // We'd normally use ~~inp for this, but unfortunately it also
4696 // converts floats to ints.
4697 // inp may be undefined, so careful calling replace on it.
4698 var res = inp && parseFloat(inp.replace(',', '.'));
4699 // apply sign while we're at it
4700 return (isNaN(res) ? 0 : res) * sign;
4701 }
4702
4703 function positiveMomentsDifference(base, other) {
4704 var res = {milliseconds: 0, months: 0};
4705
4706 res.months = other.month() - base.month() +
4707 (other.year() - base.year()) * 12;
4708 if (base.clone().add(res.months, 'M').isAfter(other)) {
4709 --res.months;
4710 }
4711
4712 res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
4713
4714 return res;
4715 }
4716
4717 function momentsDifference(base, other) {
4718 var res;
4719 if (!(base.isValid() && other.isValid())) {
4720 return {milliseconds: 0, months: 0};
4721 }
4722
4723 other = cloneWithOffset(other, base);
4724 if (base.isBefore(other)) {
4725 res = positiveMomentsDifference(base, other);
4726 } else {
4727 res = positiveMomentsDifference(other, base);
4728 res.milliseconds = -res.milliseconds;
4729 res.months = -res.months;
4730 }
4731
4732 return res;
4733 }
4734
4735 // TODO: remove 'name' arg after deprecation is removed
4736 function createAdder(direction, name) {
4737 return function (val, period) {
4738 var dur, tmp;
4739 //invert the arguments, but complain about it
4740 if (period !== null && !isNaN(+period)) {
4741 deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
4742 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
4743 tmp = val; val = period; period = tmp;
4744 }
4745
4746 val = typeof val === 'string' ? +val : val;
4747 dur = createDuration(val, period);
4748 addSubtract(this, dur, direction);
4749 return this;
4750 };
4751 }
4752
4753 function addSubtract (mom, duration, isAdding, updateOffset) {
4754 var milliseconds = duration._milliseconds,
4755 days = absRound(duration._days),
4756 months = absRound(duration._months);
4757
4758 if (!mom.isValid()) {
4759 // No op
4760 return;
4761 }
4762
4763 updateOffset = updateOffset == null ? true : updateOffset;
4764
4765 if (milliseconds) {
4766 mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
4767 }
4768 if (days) {
4769 set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
4770 }
4771 if (months) {
4772 setMonth(mom, get(mom, 'Month') + months * isAdding);
4773 }
4774 if (updateOffset) {
4775 hooks.updateOffset(mom, days || months);
4776 }
4777 }
4778
4779 var add = createAdder(1, 'add');
4780 var subtract = createAdder(-1, 'subtract');
4781
4782 function getCalendarFormat(myMoment, now) {
4783 var diff = myMoment.diff(now, 'days', true);
4784 return diff < -6 ? 'sameElse' :
4785 diff < -1 ? 'lastWeek' :
4786 diff < 0 ? 'lastDay' :
4787 diff < 1 ? 'sameDay' :
4788 diff < 2 ? 'nextDay' :
4789 diff < 7 ? 'nextWeek' : 'sameElse';
4790 }
4791
4792 function calendar$1 (time, formats) {
4793 // We want to compare the start of today, vs this.
4794 // Getting start-of-today depends on whether we're local/utc/offset or not.
4795 var now = time || createLocal(),
4796 sod = cloneWithOffset(now, this).startOf('day'),
4797 format = hooks.calendarFormat(this, sod) || 'sameElse';
4798
4799 var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
4800
4801 return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
4802 }
4803
4804 function clone () {
4805 return new Moment(this);
4806 }
4807
4808 function isAfter (input, units) {
4809 var localInput = isMoment(input) ? input : createLocal(input);
4810 if (!(this.isValid() && localInput.isValid())) {
4811 return false;
4812 }
4813 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
4814 if (units === 'millisecond') {
4815 return this.valueOf() > localInput.valueOf();
4816 } else {
4817 return localInput.valueOf() < this.clone().startOf(units).valueOf();
4818 }
4819 }
4820
4821 function isBefore (input, units) {
4822 var localInput = isMoment(input) ? input : createLocal(input);
4823 if (!(this.isValid() && localInput.isValid())) {
4824 return false;
4825 }
4826 units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
4827 if (units === 'millisecond') {
4828 return this.valueOf() < localInput.valueOf();
4829 } else {
4830 return this.clone().endOf(units).valueOf() < localInput.valueOf();
4831 }
4832 }
4833
4834 function isBetween (from, to, units, inclusivity) {
4835 inclusivity = inclusivity || '()';
4836 return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
4837 (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
4838 }
4839
4840 function isSame (input, units) {
4841 var localInput = isMoment(input) ? input : createLocal(input),
4842 inputMs;
4843 if (!(this.isValid() && localInput.isValid())) {
4844 return false;
4845 }
4846 units = normalizeUnits(units || 'millisecond');
4847 if (units === 'millisecond') {
4848 return this.valueOf() === localInput.valueOf();
4849 } else {
4850 inputMs = localInput.valueOf();
4851 return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
4852 }
4853 }
4854
4855 function isSameOrAfter (input, units) {
4856 return this.isSame(input, units) || this.isAfter(input,units);
4857 }
4858
4859 function isSameOrBefore (input, units) {
4860 return this.isSame(input, units) || this.isBefore(input,units);
4861 }
4862
4863 function diff (input, units, asFloat) {
4864 var that,
4865 zoneDelta,
4866 delta, output;
4867
4868 if (!this.isValid()) {
4869 return NaN;
4870 }
4871
4872 that = cloneWithOffset(input, this);
4873
4874 if (!that.isValid()) {
4875 return NaN;
4876 }
4877
4878 zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
4879
4880 units = normalizeUnits(units);
4881
4882 if (units === 'year' || units === 'month' || units === 'quarter') {
4883 output = monthDiff(this, that);
4884 if (units === 'quarter') {
4885 output = output / 3;
4886 } else if (units === 'year') {
4887 output = output / 12;
4888 }
4889 } else {
4890 delta = this - that;
4891 output = units === 'second' ? delta / 1e3 : // 1000
4892 units === 'minute' ? delta / 6e4 : // 1000 * 60
4893 units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
4894 units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
4895 units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
4896 delta;
4897 }
4898 return asFloat ? output : absFloor(output);
4899 }
4900
4901 function monthDiff (a, b) {
4902 // difference in months
4903 var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
4904 // b is in (anchor - 1 month, anchor + 1 month)
4905 anchor = a.clone().add(wholeMonthDiff, 'months'),
4906 anchor2, adjust;
4907
4908 if (b - anchor < 0) {
4909 anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
4910 // linear across the month
4911 adjust = (b - anchor) / (anchor - anchor2);
4912 } else {
4913 anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
4914 // linear across the month
4915 adjust = (b - anchor) / (anchor2 - anchor);
4916 }
4917
4918 //check for negative zero, return zero if negative zero
4919 return -(wholeMonthDiff + adjust) || 0;
4920 }
4921
4922 hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
4923 hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
4924
4925 function toString () {
4926 return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
4927 }
4928
4929 function toISOString() {
4930 if (!this.isValid()) {
4931 return null;
4932 }
4933 var m = this.clone().utc();
4934 if (m.year() < 0 || m.year() > 9999) {
4935 return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
4936 }
4937 if (isFunction(Date.prototype.toISOString)) {
4938 // native implementation is ~50x faster, use it when we can
4939 return this.toDate().toISOString();
4940 }
4941 return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
4942 }
4943
4944 /**
4945 * Return a human readable representation of a moment that can
4946 * also be evaluated to get a new moment which is the same
4947 *
4948 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
4949 */
4950 function inspect () {
4951 if (!this.isValid()) {
4952 return 'moment.invalid(/* ' + this._i + ' */)';
4953 }
4954 var func = 'moment';
4955 var zone = '';
4956 if (!this.isLocal()) {
4957 func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
4958 zone = 'Z';
4959 }
4960 var prefix = '[' + func + '("]';
4961 var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
4962 var datetime = '-MM-DD[T]HH:mm:ss.SSS';
4963 var suffix = zone + '[")]';
4964
4965 return this.format(prefix + year + datetime + suffix);
4966 }
4967
4968 function format (inputString) {
4969 if (!inputString) {
4970 inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
4971 }
4972 var output = formatMoment(this, inputString);
4973 return this.localeData().postformat(output);
4974 }
4975
4976 function from (time, withoutSuffix) {
4977 if (this.isValid() &&
4978 ((isMoment(time) && time.isValid()) ||
4979 createLocal(time).isValid())) {
4980 return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
4981 } else {
4982 return this.localeData().invalidDate();
4983 }
4984 }
4985
4986 function fromNow (withoutSuffix) {
4987 return this.from(createLocal(), withoutSuffix);
4988 }
4989
4990 function to (time, withoutSuffix) {
4991 if (this.isValid() &&
4992 ((isMoment(time) && time.isValid()) ||
4993 createLocal(time).isValid())) {
4994 return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
4995 } else {
4996 return this.localeData().invalidDate();
4997 }
4998 }
4999
5000 function toNow (withoutSuffix) {
5001 return this.to(createLocal(), withoutSuffix);
5002 }
5003
5004 // If passed a locale key, it will set the locale for this
5005 // instance. Otherwise, it will return the locale configuration
5006 // variables for this instance.
5007 function locale (key) {
5008 var newLocaleData;
5009
5010 if (key === undefined) {
5011 return this._locale._abbr;
5012 } else {
5013 newLocaleData = getLocale(key);
5014 if (newLocaleData != null) {
5015 this._locale = newLocaleData;
5016 }
5017 return this;
5018 }
5019 }
5020
5021 var lang = deprecate(
5022 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
5023 function (key) {
5024 if (key === undefined) {
5025 return this.localeData();
5026 } else {
5027 return this.locale(key);
5028 }
5029 }
5030 );
5031
5032 function localeData () {
5033 return this._locale;
5034 }
5035
5036 function startOf (units) {
5037 units = normalizeUnits(units);
5038 // the following switch intentionally omits break keywords
5039 // to utilize falling through the cases.
5040 switch (units) {
5041 case 'year':
5042 this.month(0);
5043 /* falls through */
5044 case 'quarter':
5045 case 'month':
5046 this.date(1);
5047 /* falls through */
5048 case 'week':
5049 case 'isoWeek':
5050 case 'day':
5051 case 'date':
5052 this.hours(0);
5053 /* falls through */
5054 case 'hour':
5055 this.minutes(0);
5056 /* falls through */
5057 case 'minute':
5058 this.seconds(0);
5059 /* falls through */
5060 case 'second':
5061 this.milliseconds(0);
5062 }
5063
5064 // weeks are a special case
5065 if (units === 'week') {
5066 this.weekday(0);
5067 }
5068 if (units === 'isoWeek') {
5069 this.isoWeekday(1);
5070 }
5071
5072 // quarters are also special
5073 if (units === 'quarter') {
5074 this.month(Math.floor(this.month() / 3) * 3);
5075 }
5076
5077 return this;
5078 }
5079
5080 function endOf (units) {
5081 units = normalizeUnits(units);
5082 if (units === undefined || units === 'millisecond') {
5083 return this;
5084 }
5085
5086 // 'date' is an alias for 'day', so it should be considered as such.
5087 if (units === 'date') {
5088 units = 'day';
5089 }
5090
5091 return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
5092 }
5093
5094 function valueOf () {
5095 return this._d.valueOf() - ((this._offset || 0) * 60000);
5096 }
5097
5098 function unix () {
5099 return Math.floor(this.valueOf() / 1000);
5100 }
5101
5102 function toDate () {
5103 return new Date(this.valueOf());
5104 }
5105
5106 function toArray () {
5107 var m = this;
5108 return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
5109 }
5110
5111 function toObject () {
5112 var m = this;
5113 return {
5114 years: m.year(),
5115 months: m.month(),
5116 date: m.date(),
5117 hours: m.hours(),
5118 minutes: m.minutes(),
5119 seconds: m.seconds(),
5120 milliseconds: m.milliseconds()
5121 };
5122 }
5123
5124 function toJSON () {
5125 // new Date(NaN).toJSON() === null
5126 return this.isValid() ? this.toISOString() : null;
5127 }
5128
5129 function isValid$2 () {
5130 return isValid(this);
5131 }
5132
5133 function parsingFlags () {
5134 return extend({}, getParsingFlags(this));
5135 }
5136
5137 function invalidAt () {
5138 return getParsingFlags(this).overflow;
5139 }
5140
5141 function creationData() {
5142 return {
5143 input: this._i,
5144 format: this._f,
5145 locale: this._locale,
5146 isUTC: this._isUTC,
5147 strict: this._strict
5148 };
5149 }
5150
5151 // FORMATTING
5152
5153 addFormatToken(0, ['gg', 2], 0, function () {
5154 return this.weekYear() % 100;
5155 });
5156
5157 addFormatToken(0, ['GG', 2], 0, function () {
5158 return this.isoWeekYear() % 100;
5159 });
5160
5161 function addWeekYearFormatToken (token, getter) {
5162 addFormatToken(0, [token, token.length], 0, getter);
5163 }
5164
5165 addWeekYearFormatToken('gggg', 'weekYear');
5166 addWeekYearFormatToken('ggggg', 'weekYear');
5167 addWeekYearFormatToken('GGGG', 'isoWeekYear');
5168 addWeekYearFormatToken('GGGGG', 'isoWeekYear');
5169
5170 // ALIASES
5171
5172 addUnitAlias('weekYear', 'gg');
5173 addUnitAlias('isoWeekYear', 'GG');
5174
5175 // PRIORITY
5176
5177 addUnitPriority('weekYear', 1);
5178 addUnitPriority('isoWeekYear', 1);
5179
5180
5181 // PARSING
5182
5183 addRegexToken('G', matchSigned);
5184 addRegexToken('g', matchSigned);
5185 addRegexToken('GG', match1to2, match2);
5186 addRegexToken('gg', match1to2, match2);
5187 addRegexToken('GGGG', match1to4, match4);
5188 addRegexToken('gggg', match1to4, match4);
5189 addRegexToken('GGGGG', match1to6, match6);
5190 addRegexToken('ggggg', match1to6, match6);
5191
5192 addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
5193 week[token.substr(0, 2)] = toInt(input);
5194 });
5195
5196 addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
5197 week[token] = hooks.parseTwoDigitYear(input);
5198 });
5199
5200 // MOMENTS
5201
5202 function getSetWeekYear (input) {
5203 return getSetWeekYearHelper.call(this,
5204 input,
5205 this.week(),
5206 this.weekday(),
5207 this.localeData()._week.dow,
5208 this.localeData()._week.doy);
5209 }
5210
5211 function getSetISOWeekYear (input) {
5212 return getSetWeekYearHelper.call(this,
5213 input, this.isoWeek(), this.isoWeekday(), 1, 4);
5214 }
5215
5216 function getISOWeeksInYear () {
5217 return weeksInYear(this.year(), 1, 4);
5218 }
5219
5220 function getWeeksInYear () {
5221 var weekInfo = this.localeData()._week;
5222 return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
5223 }
5224
5225 function getSetWeekYearHelper(input, week, weekday, dow, doy) {
5226 var weeksTarget;
5227 if (input == null) {
5228 return weekOfYear(this, dow, doy).year;
5229 } else {
5230 weeksTarget = weeksInYear(input, dow, doy);
5231 if (week > weeksTarget) {
5232 week = weeksTarget;
5233 }
5234 return setWeekAll.call(this, input, week, weekday, dow, doy);
5235 }
5236 }
5237
5238 function setWeekAll(weekYear, week, weekday, dow, doy) {
5239 var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
5240 date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
5241
5242 this.year(date.getUTCFullYear());
5243 this.month(date.getUTCMonth());
5244 this.date(date.getUTCDate());
5245 return this;
5246 }
5247
5248 // FORMATTING
5249
5250 addFormatToken('Q', 0, 'Qo', 'quarter');
5251
5252 // ALIASES
5253
5254 addUnitAlias('quarter', 'Q');
5255
5256 // PRIORITY
5257
5258 addUnitPriority('quarter', 7);
5259
5260 // PARSING
5261
5262 addRegexToken('Q', match1);
5263 addParseToken('Q', function (input, array) {
5264 array[MONTH] = (toInt(input) - 1) * 3;
5265 });
5266
5267 // MOMENTS
5268
5269 function getSetQuarter (input) {
5270 return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
5271 }
5272
5273 // FORMATTING
5274
5275 addFormatToken('D', ['DD', 2], 'Do', 'date');
5276
5277 // ALIASES
5278
5279 addUnitAlias('date', 'D');
5280
5281 // PRIOROITY
5282 addUnitPriority('date', 9);
5283
5284 // PARSING
5285
5286 addRegexToken('D', match1to2);
5287 addRegexToken('DD', match1to2, match2);
5288 addRegexToken('Do', function (isStrict, locale) {
5289 // TODO: Remove "ordinalParse" fallback in next major release.
5290 return isStrict ?
5291 (locale._dayOfMonthOrdinalParse || locale._ordinalParse) :
5292 locale._dayOfMonthOrdinalParseLenient;
5293 });
5294
5295 addParseToken(['D', 'DD'], DATE);
5296 addParseToken('Do', function (input, array) {
5297 array[DATE] = toInt(input.match(match1to2)[0], 10);
5298 });
5299
5300 // MOMENTS
5301
5302 var getSetDayOfMonth = makeGetSet('Date', true);
5303
5304 // FORMATTING
5305
5306 addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
5307
5308 // ALIASES
5309
5310 addUnitAlias('dayOfYear', 'DDD');
5311
5312 // PRIORITY
5313 addUnitPriority('dayOfYear', 4);
5314
5315 // PARSING
5316
5317 addRegexToken('DDD', match1to3);
5318 addRegexToken('DDDD', match3);
5319 addParseToken(['DDD', 'DDDD'], function (input, array, config) {
5320 config._dayOfYear = toInt(input);
5321 });
5322
5323 // HELPERS
5324
5325 // MOMENTS
5326
5327 function getSetDayOfYear (input) {
5328 var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
5329 return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
5330 }
5331
5332 // FORMATTING
5333
5334 addFormatToken('m', ['mm', 2], 0, 'minute');
5335
5336 // ALIASES
5337
5338 addUnitAlias('minute', 'm');
5339
5340 // PRIORITY
5341
5342 addUnitPriority('minute', 14);
5343
5344 // PARSING
5345
5346 addRegexToken('m', match1to2);
5347 addRegexToken('mm', match1to2, match2);
5348 addParseToken(['m', 'mm'], MINUTE);
5349
5350 // MOMENTS
5351
5352 var getSetMinute = makeGetSet('Minutes', false);
5353
5354 // FORMATTING
5355
5356 addFormatToken('s', ['ss', 2], 0, 'second');
5357
5358 // ALIASES
5359
5360 addUnitAlias('second', 's');
5361
5362 // PRIORITY
5363
5364 addUnitPriority('second', 15);
5365
5366 // PARSING
5367
5368 addRegexToken('s', match1to2);
5369 addRegexToken('ss', match1to2, match2);
5370 addParseToken(['s', 'ss'], SECOND);
5371
5372 // MOMENTS
5373
5374 var getSetSecond = makeGetSet('Seconds', false);
5375
5376 // FORMATTING
5377
5378 addFormatToken('S', 0, 0, function () {
5379 return ~~(this.millisecond() / 100);
5380 });
5381
5382 addFormatToken(0, ['SS', 2], 0, function () {
5383 return ~~(this.millisecond() / 10);
5384 });
5385
5386 addFormatToken(0, ['SSS', 3], 0, 'millisecond');
5387 addFormatToken(0, ['SSSS', 4], 0, function () {
5388 return this.millisecond() * 10;
5389 });
5390 addFormatToken(0, ['SSSSS', 5], 0, function () {
5391 return this.millisecond() * 100;
5392 });
5393 addFormatToken(0, ['SSSSSS', 6], 0, function () {
5394 return this.millisecond() * 1000;
5395 });
5396 addFormatToken(0, ['SSSSSSS', 7], 0, function () {
5397 return this.millisecond() * 10000;
5398 });
5399 addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
5400 return this.millisecond() * 100000;
5401 });
5402 addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
5403 return this.millisecond() * 1000000;
5404 });
5405
5406
5407 // ALIASES
5408
5409 addUnitAlias('millisecond', 'ms');
5410
5411 // PRIORITY
5412
5413 addUnitPriority('millisecond', 16);
5414
5415 // PARSING
5416
5417 addRegexToken('S', match1to3, match1);
5418 addRegexToken('SS', match1to3, match2);
5419 addRegexToken('SSS', match1to3, match3);
5420
5421 var token;
5422 for (token = 'SSSS'; token.length <= 9; token += 'S') {
5423 addRegexToken(token, matchUnsigned);
5424 }
5425
5426 function parseMs(input, array) {
5427 array[MILLISECOND] = toInt(('0.' + input) * 1000);
5428 }
5429
5430 for (token = 'S'; token.length <= 9; token += 'S') {
5431 addParseToken(token, parseMs);
5432 }
5433 // MOMENTS
5434
5435 var getSetMillisecond = makeGetSet('Milliseconds', false);
5436
5437 // FORMATTING
5438
5439 addFormatToken('z', 0, 0, 'zoneAbbr');
5440 addFormatToken('zz', 0, 0, 'zoneName');
5441
5442 // MOMENTS
5443
5444 function getZoneAbbr () {
5445 return this._isUTC ? 'UTC' : '';
5446 }
5447
5448 function getZoneName () {
5449 return this._isUTC ? 'Coordinated Universal Time' : '';
5450 }
5451
5452 var proto = Moment.prototype;
5453
5454 proto.add = add;
5455 proto.calendar = calendar$1;
5456 proto.clone = clone;
5457 proto.diff = diff;
5458 proto.endOf = endOf;
5459 proto.format = format;
5460 proto.from = from;
5461 proto.fromNow = fromNow;
5462 proto.to = to;
5463 proto.toNow = toNow;
5464 proto.get = stringGet;
5465 proto.invalidAt = invalidAt;
5466 proto.isAfter = isAfter;
5467 proto.isBefore = isBefore;
5468 proto.isBetween = isBetween;
5469 proto.isSame = isSame;
5470 proto.isSameOrAfter = isSameOrAfter;
5471 proto.isSameOrBefore = isSameOrBefore;
5472 proto.isValid = isValid$2;
5473 proto.lang = lang;
5474 proto.locale = locale;
5475 proto.localeData = localeData;
5476 proto.max = prototypeMax;
5477 proto.min = prototypeMin;
5478 proto.parsingFlags = parsingFlags;
5479 proto.set = stringSet;
5480 proto.startOf = startOf;
5481 proto.subtract = subtract;
5482 proto.toArray = toArray;
5483 proto.toObject = toObject;
5484 proto.toDate = toDate;
5485 proto.toISOString = toISOString;
5486 proto.inspect = inspect;
5487 proto.toJSON = toJSON;
5488 proto.toString = toString;
5489 proto.unix = unix;
5490 proto.valueOf = valueOf;
5491 proto.creationData = creationData;
5492
5493 // Year
5494 proto.year = getSetYear;
5495 proto.isLeapYear = getIsLeapYear;
5496
5497 // Week Year
5498 proto.weekYear = getSetWeekYear;
5499 proto.isoWeekYear = getSetISOWeekYear;
5500
5501 // Quarter
5502 proto.quarter = proto.quarters = getSetQuarter;
5503
5504 // Month
5505 proto.month = getSetMonth;
5506 proto.daysInMonth = getDaysInMonth;
5507
5508 // Week
5509 proto.week = proto.weeks = getSetWeek;
5510 proto.isoWeek = proto.isoWeeks = getSetISOWeek;
5511 proto.weeksInYear = getWeeksInYear;
5512 proto.isoWeeksInYear = getISOWeeksInYear;
5513
5514 // Day
5515 proto.date = getSetDayOfMonth;
5516 proto.day = proto.days = getSetDayOfWeek;
5517 proto.weekday = getSetLocaleDayOfWeek;
5518 proto.isoWeekday = getSetISODayOfWeek;
5519 proto.dayOfYear = getSetDayOfYear;
5520
5521 // Hour
5522 proto.hour = proto.hours = getSetHour;
5523
5524 // Minute
5525 proto.minute = proto.minutes = getSetMinute;
5526
5527 // Second
5528 proto.second = proto.seconds = getSetSecond;
5529
5530 // Millisecond
5531 proto.millisecond = proto.milliseconds = getSetMillisecond;
5532
5533 // Offset
5534 proto.utcOffset = getSetOffset;
5535 proto.utc = setOffsetToUTC;
5536 proto.local = setOffsetToLocal;
5537 proto.parseZone = setOffsetToParsedOffset;
5538 proto.hasAlignedHourOffset = hasAlignedHourOffset;
5539 proto.isDST = isDaylightSavingTime;
5540 proto.isLocal = isLocal;
5541 proto.isUtcOffset = isUtcOffset;
5542 proto.isUtc = isUtc;
5543 proto.isUTC = isUtc;
5544
5545 // Timezone
5546 proto.zoneAbbr = getZoneAbbr;
5547 proto.zoneName = getZoneName;
5548
5549 // Deprecations
5550 proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
5551 proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
5552 proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
5553 proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
5554 proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
5555
5556 function createUnix (input) {
5557 return createLocal(input * 1000);
5558 }
5559
5560 function createInZone () {
5561 return createLocal.apply(null, arguments).parseZone();
5562 }
5563
5564 function preParsePostFormat (string) {
5565 return string;
5566 }
5567
5568 var proto$1 = Locale.prototype;
5569
5570 proto$1.calendar = calendar;
5571 proto$1.longDateFormat = longDateFormat;
5572 proto$1.invalidDate = invalidDate;
5573 proto$1.ordinal = ordinal;
5574 proto$1.preparse = preParsePostFormat;
5575 proto$1.postformat = preParsePostFormat;
5576 proto$1.relativeTime = relativeTime;
5577 proto$1.pastFuture = pastFuture;
5578 proto$1.set = set;
5579
5580 // Month
5581 proto$1.months = localeMonths;
5582 proto$1.monthsShort = localeMonthsShort;
5583 proto$1.monthsParse = localeMonthsParse;
5584 proto$1.monthsRegex = monthsRegex;
5585 proto$1.monthsShortRegex = monthsShortRegex;
5586
5587 // Week
5588 proto$1.week = localeWeek;
5589 proto$1.firstDayOfYear = localeFirstDayOfYear;
5590 proto$1.firstDayOfWeek = localeFirstDayOfWeek;
5591
5592 // Day of Week
5593 proto$1.weekdays = localeWeekdays;
5594 proto$1.weekdaysMin = localeWeekdaysMin;
5595 proto$1.weekdaysShort = localeWeekdaysShort;
5596 proto$1.weekdaysParse = localeWeekdaysParse;
5597
5598 proto$1.weekdaysRegex = weekdaysRegex;
5599 proto$1.weekdaysShortRegex = weekdaysShortRegex;
5600 proto$1.weekdaysMinRegex = weekdaysMinRegex;
5601
5602 // Hours
5603 proto$1.isPM = localeIsPM;
5604 proto$1.meridiem = localeMeridiem;
5605
5606 function get$1 (format, index, field, setter) {
5607 var locale = getLocale();
5608 var utc = createUTC().set(setter, index);
5609 return locale[field](utc, format);
5610 }
5611
5612 function listMonthsImpl (format, index, field) {
5613 if (isNumber(format)) {
5614 index = format;
5615 format = undefined;
5616 }
5617
5618 format = format || '';
5619
5620 if (index != null) {
5621 return get$1(format, index, field, 'month');
5622 }
5623
5624 var i;
5625 var out = [];
5626 for (i = 0; i < 12; i++) {
5627 out[i] = get$1(format, i, field, 'month');
5628 }
5629 return out;
5630 }
5631
5632 // ()
5633 // (5)
5634 // (fmt, 5)
5635 // (fmt)
5636 // (true)
5637 // (true, 5)
5638 // (true, fmt, 5)
5639 // (true, fmt)
5640 function listWeekdaysImpl (localeSorted, format, index, field) {
5641 if (typeof localeSorted === 'boolean') {
5642 if (isNumber(format)) {
5643 index = format;
5644 format = undefined;
5645 }
5646
5647 format = format || '';
5648 } else {
5649 format = localeSorted;
5650 index = format;
5651 localeSorted = false;
5652
5653 if (isNumber(format)) {
5654 index = format;
5655 format = undefined;
5656 }
5657
5658 format = format || '';
5659 }
5660
5661 var locale = getLocale(),
5662 shift = localeSorted ? locale._week.dow : 0;
5663
5664 if (index != null) {
5665 return get$1(format, (index + shift) % 7, field, 'day');
5666 }
5667
5668 var i;
5669 var out = [];
5670 for (i = 0; i < 7; i++) {
5671 out[i] = get$1(format, (i + shift) % 7, field, 'day');
5672 }
5673 return out;
5674 }
5675
5676 function listMonths (format, index) {
5677 return listMonthsImpl(format, index, 'months');
5678 }
5679
5680 function listMonthsShort (format, index) {
5681 return listMonthsImpl(format, index, 'monthsShort');
5682 }
5683
5684 function listWeekdays (localeSorted, format, index) {
5685 return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
5686 }
5687
5688 function listWeekdaysShort (localeSorted, format, index) {
5689 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
5690 }
5691
5692 function listWeekdaysMin (localeSorted, format, index) {
5693 return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
5694 }
5695
5696 getSetGlobalLocale('en', {
5697 dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
5698 ordinal : function (number) {
5699 var b = number % 10,
5700 output = (toInt(number % 100 / 10) === 1) ? 'th' :
5701 (b === 1) ? 'st' :
5702 (b === 2) ? 'nd' :
5703 (b === 3) ? 'rd' : 'th';
5704 return number + output;
5705 }
5706 });
5707
5708 // Side effect imports
5709 hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
5710 hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
5711
5712 var mathAbs = Math.abs;
5713
5714 function abs () {
5715 var data = this._data;
5716
5717 this._milliseconds = mathAbs(this._milliseconds);
5718 this._days = mathAbs(this._days);
5719 this._months = mathAbs(this._months);
5720
5721 data.milliseconds = mathAbs(data.milliseconds);
5722 data.seconds = mathAbs(data.seconds);
5723 data.minutes = mathAbs(data.minutes);
5724 data.hours = mathAbs(data.hours);
5725 data.months = mathAbs(data.months);
5726 data.years = mathAbs(data.years);
5727
5728 return this;
5729 }
5730
5731 function addSubtract$1 (duration, input, value, direction) {
5732 var other = createDuration(input, value);
5733
5734 duration._milliseconds += direction * other._milliseconds;
5735 duration._days += direction * other._days;
5736 duration._months += direction * other._months;
5737
5738 return duration._bubble();
5739 }
5740
5741 // supports only 2.0-style add(1, 's') or add(duration)
5742 function add$1 (input, value) {
5743 return addSubtract$1(this, input, value, 1);
5744 }
5745
5746 // supports only 2.0-style subtract(1, 's') or subtract(duration)
5747 function subtract$1 (input, value) {
5748 return addSubtract$1(this, input, value, -1);
5749 }
5750
5751 function absCeil (number) {
5752 if (number < 0) {
5753 return Math.floor(number);
5754 } else {
5755 return Math.ceil(number);
5756 }
5757 }
5758
5759 function bubble () {
5760 var milliseconds = this._milliseconds;
5761 var days = this._days;
5762 var months = this._months;
5763 var data = this._data;
5764 var seconds, minutes, hours, years, monthsFromDays;
5765
5766 // if we have a mix of positive and negative values, bubble down first
5767 // check: https://github.com/moment/moment/issues/2166
5768 if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
5769 (milliseconds <= 0 && days <= 0 && months <= 0))) {
5770 milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
5771 days = 0;
5772 months = 0;
5773 }
5774
5775 // The following code bubbles up values, see the tests for
5776 // examples of what that means.
5777 data.milliseconds = milliseconds % 1000;
5778
5779 seconds = absFloor(milliseconds / 1000);
5780 data.seconds = seconds % 60;
5781
5782 minutes = absFloor(seconds / 60);
5783 data.minutes = minutes % 60;
5784
5785 hours = absFloor(minutes / 60);
5786 data.hours = hours % 24;
5787
5788 days += absFloor(hours / 24);
5789
5790 // convert days to months
5791 monthsFromDays = absFloor(daysToMonths(days));
5792 months += monthsFromDays;
5793 days -= absCeil(monthsToDays(monthsFromDays));
5794
5795 // 12 months -> 1 year
5796 years = absFloor(months / 12);
5797 months %= 12;
5798
5799 data.days = days;
5800 data.months = months;
5801 data.years = years;
5802
5803 return this;
5804 }
5805
5806 function daysToMonths (days) {
5807 // 400 years have 146097 days (taking into account leap year rules)
5808 // 400 years have 12 months === 4800
5809 return days * 4800 / 146097;
5810 }
5811
5812 function monthsToDays (months) {
5813 // the reverse of daysToMonths
5814 return months * 146097 / 4800;
5815 }
5816
5817 function as (units) {
5818 if (!this.isValid()) {
5819 return NaN;
5820 }
5821 var days;
5822 var months;
5823 var milliseconds = this._milliseconds;
5824
5825 units = normalizeUnits(units);
5826
5827 if (units === 'month' || units === 'year') {
5828 days = this._days + milliseconds / 864e5;
5829 months = this._months + daysToMonths(days);
5830 return units === 'month' ? months : months / 12;
5831 } else {
5832 // handle milliseconds separately because of floating point math errors (issue #1867)
5833 days = this._days + Math.round(monthsToDays(this._months));
5834 switch (units) {
5835 case 'week' : return days / 7 + milliseconds / 6048e5;
5836 case 'day' : return days + milliseconds / 864e5;
5837 case 'hour' : return days * 24 + milliseconds / 36e5;
5838 case 'minute' : return days * 1440 + milliseconds / 6e4;
5839 case 'second' : return days * 86400 + milliseconds / 1000;
5840 // Math.floor prevents floating point math errors here
5841 case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
5842 default: throw new Error('Unknown unit ' + units);
5843 }
5844 }
5845 }
5846
5847 // TODO: Use this.as('ms')?
5848 function valueOf$1 () {
5849 if (!this.isValid()) {
5850 return NaN;
5851 }
5852 return (
5853 this._milliseconds +
5854 this._days * 864e5 +
5855 (this._months % 12) * 2592e6 +
5856 toInt(this._months / 12) * 31536e6
5857 );
5858 }
5859
5860 function makeAs (alias) {
5861 return function () {
5862 return this.as(alias);
5863 };
5864 }
5865
5866 var asMilliseconds = makeAs('ms');
5867 var asSeconds = makeAs('s');
5868 var asMinutes = makeAs('m');
5869 var asHours = makeAs('h');
5870 var asDays = makeAs('d');
5871 var asWeeks = makeAs('w');
5872 var asMonths = makeAs('M');
5873 var asYears = makeAs('y');
5874
5875 function get$2 (units) {
5876 units = normalizeUnits(units);
5877 return this.isValid() ? this[units + 's']() : NaN;
5878 }
5879
5880 function makeGetter(name) {
5881 return function () {
5882 return this.isValid() ? this._data[name] : NaN;
5883 };
5884 }
5885
5886 var milliseconds = makeGetter('milliseconds');
5887 var seconds = makeGetter('seconds');
5888 var minutes = makeGetter('minutes');
5889 var hours = makeGetter('hours');
5890 var days = makeGetter('days');
5891 var months = makeGetter('months');
5892 var years = makeGetter('years');
5893
5894 function weeks () {
5895 return absFloor(this.days() / 7);
5896 }
5897
5898 var round = Math.round;
5899 var thresholds = {
5900 ss: 44, // a few seconds to seconds
5901 s : 45, // seconds to minute
5902 m : 45, // minutes to hour
5903 h : 22, // hours to day
5904 d : 26, // days to month
5905 M : 11 // months to year
5906 };
5907
5908 // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
5909 function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
5910 return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
5911 }
5912
5913 function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
5914 var duration = createDuration(posNegDuration).abs();
5915 var seconds = round(duration.as('s'));
5916 var minutes = round(duration.as('m'));
5917 var hours = round(duration.as('h'));
5918 var days = round(duration.as('d'));
5919 var months = round(duration.as('M'));
5920 var years = round(duration.as('y'));
5921
5922 var a = seconds <= thresholds.ss && ['s', seconds] ||
5923 seconds < thresholds.s && ['ss', seconds] ||
5924 minutes <= 1 && ['m'] ||
5925 minutes < thresholds.m && ['mm', minutes] ||
5926 hours <= 1 && ['h'] ||
5927 hours < thresholds.h && ['hh', hours] ||
5928 days <= 1 && ['d'] ||
5929 days < thresholds.d && ['dd', days] ||
5930 months <= 1 && ['M'] ||
5931 months < thresholds.M && ['MM', months] ||
5932 years <= 1 && ['y'] || ['yy', years];
5933
5934 a[2] = withoutSuffix;
5935 a[3] = +posNegDuration > 0;
5936 a[4] = locale;
5937 return substituteTimeAgo.apply(null, a);
5938 }
5939
5940 // This function allows you to set the rounding function for relative time strings
5941 function getSetRelativeTimeRounding (roundingFunction) {
5942 if (roundingFunction === undefined) {
5943 return round;
5944 }
5945 if (typeof(roundingFunction) === 'function') {
5946 round = roundingFunction;
5947 return true;
5948 }
5949 return false;
5950 }
5951
5952 // This function allows you to set a threshold for relative time strings
5953 function getSetRelativeTimeThreshold (threshold, limit) {
5954 if (thresholds[threshold] === undefined) {
5955 return false;
5956 }
5957 if (limit === undefined) {
5958 return thresholds[threshold];
5959 }
5960 thresholds[threshold] = limit;
5961 if (threshold === 's') {
5962 thresholds.ss = limit - 1;
5963 }
5964 return true;
5965 }
5966
5967 function humanize (withSuffix) {
5968 if (!this.isValid()) {
5969 return this.localeData().invalidDate();
5970 }
5971
5972 var locale = this.localeData();
5973 var output = relativeTime$1(this, !withSuffix, locale);
5974
5975 if (withSuffix) {
5976 output = locale.pastFuture(+this, output);
5977 }
5978
5979 return locale.postformat(output);
5980 }
5981
5982 var abs$1 = Math.abs;
5983
5984 function toISOString$1() {
5985 // for ISO strings we do not use the normal bubbling rules:
5986 // * milliseconds bubble up until they become hours
5987 // * days do not bubble at all
5988 // * months bubble up until they become years
5989 // This is because there is no context-free conversion between hours and days
5990 // (think of clock changes)
5991 // and also not between days and months (28-31 days per month)
5992 if (!this.isValid()) {
5993 return this.localeData().invalidDate();
5994 }
5995
5996 var seconds = abs$1(this._milliseconds) / 1000;
5997 var days = abs$1(this._days);
5998 var months = abs$1(this._months);
5999 var minutes, hours, years;
6000
6001 // 3600 seconds -> 60 minutes -> 1 hour
6002 minutes = absFloor(seconds / 60);
6003 hours = absFloor(minutes / 60);
6004 seconds %= 60;
6005 minutes %= 60;
6006
6007 // 12 months -> 1 year
6008 years = absFloor(months / 12);
6009 months %= 12;
6010
6011
6012 // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
6013 var Y = years;
6014 var M = months;
6015 var D = days;
6016 var h = hours;
6017 var m = minutes;
6018 var s = seconds;
6019 var total = this.asSeconds();
6020
6021 if (!total) {
6022 // this is the same as C#'s (Noda) and python (isodate)...
6023 // but not other JS (goog.date)
6024 return 'P0D';
6025 }
6026
6027 return (total < 0 ? '-' : '') +
6028 'P' +
6029 (Y ? Y + 'Y' : '') +
6030 (M ? M + 'M' : '') +
6031 (D ? D + 'D' : '') +
6032 ((h || m || s) ? 'T' : '') +
6033 (h ? h + 'H' : '') +
6034 (m ? m + 'M' : '') +
6035 (s ? s + 'S' : '');
6036 }
6037
6038 var proto$2 = Duration.prototype;
6039
6040 proto$2.isValid = isValid$1;
6041 proto$2.abs = abs;
6042 proto$2.add = add$1;
6043 proto$2.subtract = subtract$1;
6044 proto$2.as = as;
6045 proto$2.asMilliseconds = asMilliseconds;
6046 proto$2.asSeconds = asSeconds;
6047 proto$2.asMinutes = asMinutes;
6048 proto$2.asHours = asHours;
6049 proto$2.asDays = asDays;
6050 proto$2.asWeeks = asWeeks;
6051 proto$2.asMonths = asMonths;
6052 proto$2.asYears = asYears;
6053 proto$2.valueOf = valueOf$1;
6054 proto$2._bubble = bubble;
6055 proto$2.get = get$2;
6056 proto$2.milliseconds = milliseconds;
6057 proto$2.seconds = seconds;
6058 proto$2.minutes = minutes;
6059 proto$2.hours = hours;
6060 proto$2.days = days;
6061 proto$2.weeks = weeks;
6062 proto$2.months = months;
6063 proto$2.years = years;
6064 proto$2.humanize = humanize;
6065 proto$2.toISOString = toISOString$1;
6066 proto$2.toString = toISOString$1;
6067 proto$2.toJSON = toISOString$1;
6068 proto$2.locale = locale;
6069 proto$2.localeData = localeData;
6070
6071 // Deprecations
6072 proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
6073 proto$2.lang = lang;
6074
6075 // Side effect imports
6076
6077 // FORMATTING
6078
6079 addFormatToken('X', 0, 0, 'unix');
6080 addFormatToken('x', 0, 0, 'valueOf');
6081
6082 // PARSING
6083
6084 addRegexToken('x', matchSigned);
6085 addRegexToken('X', matchTimestamp);
6086 addParseToken('X', function (input, array, config) {
6087 config._d = new Date(parseFloat(input, 10) * 1000);
6088 });
6089 addParseToken('x', function (input, array, config) {
6090 config._d = new Date(toInt(input));
6091 });
6092
6093 // Side effect imports
6094
6095
6096 hooks.version = '2.18.1';
6097
6098 setHookCallback(createLocal);
6099
6100 hooks.fn = proto;
6101 hooks.min = min;
6102 hooks.max = max;
6103 hooks.now = now;
6104 hooks.utc = createUTC;
6105 hooks.unix = createUnix;
6106 hooks.months = listMonths;
6107 hooks.isDate = isDate;
6108 hooks.locale = getSetGlobalLocale;
6109 hooks.invalid = createInvalid;
6110 hooks.duration = createDuration;
6111 hooks.isMoment = isMoment;
6112 hooks.weekdays = listWeekdays;
6113 hooks.parseZone = createInZone;
6114 hooks.localeData = getLocale;
6115 hooks.isDuration = isDuration;
6116 hooks.monthsShort = listMonthsShort;
6117 hooks.weekdaysMin = listWeekdaysMin;
6118 hooks.defineLocale = defineLocale;
6119 hooks.updateLocale = updateLocale;
6120 hooks.locales = listLocales;
6121 hooks.weekdaysShort = listWeekdaysShort;
6122 hooks.normalizeUnits = normalizeUnits;
6123 hooks.relativeTimeRounding = getSetRelativeTimeRounding;
6124 hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
6125 hooks.calendarFormat = getCalendarFormat;
6126 hooks.prototype = proto;
6127
6128 return hooks;
6129
6130 })));
6131
6132 },{}],7:[function(require,module,exports){
6133 /**
6134 * @namespace Chart
6135 */
6136 var Chart = require(29)();
6137
6138 Chart.helpers = require(45);
6139
6140 // @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
6141 require(27)(Chart);
6142
6143 Chart.defaults = require(25);
6144 Chart.Element = require(26);
6145 Chart.elements = require(40);
6146 Chart.Interaction = require(28);
6147 Chart.platform = require(48);
6148
6149 require(31)(Chart);
6150 require(22)(Chart);
6151 require(23)(Chart);
6152 require(24)(Chart);
6153 require(30)(Chart);
6154 require(33)(Chart);
6155 require(32)(Chart);
6156 require(35)(Chart);
6157
6158 require(54)(Chart);
6159 require(52)(Chart);
6160 require(53)(Chart);
6161 require(55)(Chart);
6162 require(56)(Chart);
6163 require(57)(Chart);
6164
6165 // Controllers must be loaded after elements
6166 // See Chart.core.datasetController.dataElementType
6167 require(15)(Chart);
6168 require(16)(Chart);
6169 require(17)(Chart);
6170 require(18)(Chart);
6171 require(19)(Chart);
6172 require(20)(Chart);
6173 require(21)(Chart);
6174
6175 require(8)(Chart);
6176 require(9)(Chart);
6177 require(10)(Chart);
6178 require(11)(Chart);
6179 require(12)(Chart);
6180 require(13)(Chart);
6181 require(14)(Chart);
6182
6183 // Loading built-it plugins
6184 var plugins = [];
6185
6186 plugins.push(
6187 require(49)(Chart),
6188 require(50)(Chart),
6189 require(51)(Chart)
6190 );
6191
6192 Chart.plugins.register(plugins);
6193
6194 Chart.platform.initialize();
6195
6196 module.exports = Chart;
6197 if (typeof window !== 'undefined') {
6198 window.Chart = Chart;
6199 }
6200
6201 // DEPRECATIONS
6202
6203 /**
6204 * Provided for backward compatibility, use Chart.helpers.canvas instead.
6205 * @namespace Chart.canvasHelpers
6206 * @deprecated since version 2.6.0
6207 * @todo remove at version 3
6208 * @private
6209 */
6210 Chart.canvasHelpers = Chart.helpers.canvas;
6211
6212 },{"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,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){
6213 'use strict';
6214
6215 module.exports = function(Chart) {
6216
6217 Chart.Bar = function(context, config) {
6218 config.type = 'bar';
6219
6220 return new Chart(context, config);
6221 };
6222
6223 };
6224
6225 },{}],9:[function(require,module,exports){
6226 'use strict';
6227
6228 module.exports = function(Chart) {
6229
6230 Chart.Bubble = function(context, config) {
6231 config.type = 'bubble';
6232 return new Chart(context, config);
6233 };
6234
6235 };
6236
6237 },{}],10:[function(require,module,exports){
6238 'use strict';
6239
6240 module.exports = function(Chart) {
6241
6242 Chart.Doughnut = function(context, config) {
6243 config.type = 'doughnut';
6244
6245 return new Chart(context, config);
6246 };
6247
6248 };
6249
6250 },{}],11:[function(require,module,exports){
6251 'use strict';
6252
6253 module.exports = function(Chart) {
6254
6255 Chart.Line = function(context, config) {
6256 config.type = 'line';
6257
6258 return new Chart(context, config);
6259 };
6260
6261 };
6262
6263 },{}],12:[function(require,module,exports){
6264 'use strict';
6265
6266 module.exports = function(Chart) {
6267
6268 Chart.PolarArea = function(context, config) {
6269 config.type = 'polarArea';
6270
6271 return new Chart(context, config);
6272 };
6273
6274 };
6275
6276 },{}],13:[function(require,module,exports){
6277 'use strict';
6278
6279 module.exports = function(Chart) {
6280
6281 Chart.Radar = function(context, config) {
6282 config.type = 'radar';
6283
6284 return new Chart(context, config);
6285 };
6286
6287 };
6288
6289 },{}],14:[function(require,module,exports){
6290 'use strict';
6291
6292 module.exports = function(Chart) {
6293 Chart.Scatter = function(context, config) {
6294 config.type = 'scatter';
6295 return new Chart(context, config);
6296 };
6297 };
6298
6299 },{}],15:[function(require,module,exports){
6300 'use strict';
6301
6302 var defaults = require(25);
6303 var elements = require(40);
6304 var helpers = require(45);
6305
6306 defaults._set('bar', {
6307 hover: {
6308 mode: 'label'
6309 },
6310
6311 scales: {
6312 xAxes: [{
6313 type: 'category',
6314
6315 // Specific to Bar Controller
6316 categoryPercentage: 0.8,
6317 barPercentage: 0.9,
6318
6319 // offset settings
6320 offset: true,
6321
6322 // grid line settings
6323 gridLines: {
6324 offsetGridLines: true
6325 }
6326 }],
6327
6328 yAxes: [{
6329 type: 'linear'
6330 }]
6331 }
6332 });
6333
6334 defaults._set('horizontalBar', {
6335 hover: {
6336 mode: 'index',
6337 axis: 'y'
6338 },
6339
6340 scales: {
6341 xAxes: [{
6342 type: 'linear',
6343 position: 'bottom'
6344 }],
6345
6346 yAxes: [{
6347 position: 'left',
6348 type: 'category',
6349
6350 // Specific to Horizontal Bar Controller
6351 categoryPercentage: 0.8,
6352 barPercentage: 0.9,
6353
6354 // offset settings
6355 offset: true,
6356
6357 // grid line settings
6358 gridLines: {
6359 offsetGridLines: true
6360 }
6361 }]
6362 },
6363
6364 elements: {
6365 rectangle: {
6366 borderSkipped: 'left'
6367 }
6368 },
6369
6370 tooltips: {
6371 callbacks: {
6372 title: function(item, data) {
6373 // Pick first xLabel for now
6374 var title = '';
6375
6376 if (item.length > 0) {
6377 if (item[0].yLabel) {
6378 title = item[0].yLabel;
6379 } else if (data.labels.length > 0 && item[0].index < data.labels.length) {
6380 title = data.labels[item[0].index];
6381 }
6382 }
6383
6384 return title;
6385 },
6386
6387 label: function(item, data) {
6388 var datasetLabel = data.datasets[item.datasetIndex].label || '';
6389 return datasetLabel + ': ' + item.xLabel;
6390 }
6391 },
6392 mode: 'index',
6393 axis: 'y'
6394 }
6395 });
6396
6397 module.exports = function(Chart) {
6398
6399 Chart.controllers.bar = Chart.DatasetController.extend({
6400
6401 dataElementType: elements.Rectangle,
6402
6403 initialize: function() {
6404 var me = this;
6405 var meta;
6406
6407 Chart.DatasetController.prototype.initialize.apply(me, arguments);
6408
6409 meta = me.getMeta();
6410 meta.stack = me.getDataset().stack;
6411 meta.bar = true;
6412 },
6413
6414 update: function(reset) {
6415 var me = this;
6416 var rects = me.getMeta().data;
6417 var i, ilen;
6418
6419 me._ruler = me.getRuler();
6420
6421 for (i = 0, ilen = rects.length; i < ilen; ++i) {
6422 me.updateElement(rects[i], i, reset);
6423 }
6424 },
6425
6426 updateElement: function(rectangle, index, reset) {
6427 var me = this;
6428 var chart = me.chart;
6429 var meta = me.getMeta();
6430 var dataset = me.getDataset();
6431 var custom = rectangle.custom || {};
6432 var rectangleOptions = chart.options.elements.rectangle;
6433
6434 rectangle._xScale = me.getScaleForId(meta.xAxisID);
6435 rectangle._yScale = me.getScaleForId(meta.yAxisID);
6436 rectangle._datasetIndex = me.index;
6437 rectangle._index = index;
6438
6439 rectangle._model = {
6440 datasetLabel: dataset.label,
6441 label: chart.data.labels[index],
6442 borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
6443 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
6444 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
6445 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
6446 };
6447
6448 me.updateElementGeometry(rectangle, index, reset);
6449
6450 rectangle.pivot();
6451 },
6452
6453 /**
6454 * @private
6455 */
6456 updateElementGeometry: function(rectangle, index, reset) {
6457 var me = this;
6458 var model = rectangle._model;
6459 var vscale = me.getValueScale();
6460 var base = vscale.getBasePixel();
6461 var horizontal = vscale.isHorizontal();
6462 var ruler = me._ruler || me.getRuler();
6463 var vpixels = me.calculateBarValuePixels(me.index, index);
6464 var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
6465
6466 model.horizontal = horizontal;
6467 model.base = reset ? base : vpixels.base;
6468 model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
6469 model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
6470 model.height = horizontal ? ipixels.size : undefined;
6471 model.width = horizontal ? undefined : ipixels.size;
6472 },
6473
6474 /**
6475 * @private
6476 */
6477 getValueScaleId: function() {
6478 return this.getMeta().yAxisID;
6479 },
6480
6481 /**
6482 * @private
6483 */
6484 getIndexScaleId: function() {
6485 return this.getMeta().xAxisID;
6486 },
6487
6488 /**
6489 * @private
6490 */
6491 getValueScale: function() {
6492 return this.getScaleForId(this.getValueScaleId());
6493 },
6494
6495 /**
6496 * @private
6497 */
6498 getIndexScale: function() {
6499 return this.getScaleForId(this.getIndexScaleId());
6500 },
6501
6502 /**
6503 * Returns the effective number of stacks based on groups and bar visibility.
6504 * @private
6505 */
6506 getStackCount: function(last) {
6507 var me = this;
6508 var chart = me.chart;
6509 var scale = me.getIndexScale();
6510 var stacked = scale.options.stacked;
6511 var ilen = last === undefined ? chart.data.datasets.length : last + 1;
6512 var stacks = [];
6513 var i, meta;
6514
6515 for (i = 0; i < ilen; ++i) {
6516 meta = chart.getDatasetMeta(i);
6517 if (meta.bar && chart.isDatasetVisible(i) &&
6518 (stacked === false ||
6519 (stacked === true && stacks.indexOf(meta.stack) === -1) ||
6520 (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
6521 stacks.push(meta.stack);
6522 }
6523 }
6524
6525 return stacks.length;
6526 },
6527
6528 /**
6529 * Returns the stack index for the given dataset based on groups and bar visibility.
6530 * @private
6531 */
6532 getStackIndex: function(datasetIndex) {
6533 return this.getStackCount(datasetIndex) - 1;
6534 },
6535
6536 /**
6537 * @private
6538 */
6539 getRuler: function() {
6540 var me = this;
6541 var scale = me.getIndexScale();
6542 var stackCount = me.getStackCount();
6543 var datasetIndex = me.index;
6544 var pixels = [];
6545 var isHorizontal = scale.isHorizontal();
6546 var start = isHorizontal ? scale.left : scale.top;
6547 var end = start + (isHorizontal ? scale.width : scale.height);
6548 var i, ilen;
6549
6550 for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
6551 pixels.push(scale.getPixelForValue(null, i, datasetIndex));
6552 }
6553
6554 return {
6555 pixels: pixels,
6556 start: start,
6557 end: end,
6558 stackCount: stackCount,
6559 scale: scale
6560 };
6561 },
6562
6563 /**
6564 * Note: pixel values are not clamped to the scale area.
6565 * @private
6566 */
6567 calculateBarValuePixels: function(datasetIndex, index) {
6568 var me = this;
6569 var chart = me.chart;
6570 var meta = me.getMeta();
6571 var scale = me.getValueScale();
6572 var datasets = chart.data.datasets;
6573 var value = scale.getRightValue(datasets[datasetIndex].data[index]);
6574 var stacked = scale.options.stacked;
6575 var stack = meta.stack;
6576 var start = 0;
6577 var i, imeta, ivalue, base, head, size;
6578
6579 if (stacked || (stacked === undefined && stack !== undefined)) {
6580 for (i = 0; i < datasetIndex; ++i) {
6581 imeta = chart.getDatasetMeta(i);
6582
6583 if (imeta.bar &&
6584 imeta.stack === stack &&
6585 imeta.controller.getValueScaleId() === scale.id &&
6586 chart.isDatasetVisible(i)) {
6587
6588 ivalue = scale.getRightValue(datasets[i].data[index]);
6589 if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
6590 start += ivalue;
6591 }
6592 }
6593 }
6594 }
6595
6596 base = scale.getPixelForValue(start);
6597 head = scale.getPixelForValue(start + value);
6598 size = (head - base) / 2;
6599
6600 return {
6601 size: size,
6602 base: base,
6603 head: head,
6604 center: head + size / 2
6605 };
6606 },
6607
6608 /**
6609 * @private
6610 */
6611 calculateBarIndexPixels: function(datasetIndex, index, ruler) {
6612 var me = this;
6613 var options = ruler.scale.options;
6614 var stackIndex = me.getStackIndex(datasetIndex);
6615 var pixels = ruler.pixels;
6616 var base = pixels[index];
6617 var length = pixels.length;
6618 var start = ruler.start;
6619 var end = ruler.end;
6620 var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size;
6621
6622 if (length === 1) {
6623 leftSampleSize = base > start ? base - start : end - base;
6624 rightSampleSize = base < end ? end - base : base - start;
6625 } else {
6626 if (index > 0) {
6627 leftSampleSize = (base - pixels[index - 1]) / 2;
6628 if (index === length - 1) {
6629 rightSampleSize = leftSampleSize;
6630 }
6631 }
6632 if (index < length - 1) {
6633 rightSampleSize = (pixels[index + 1] - base) / 2;
6634 if (index === 0) {
6635 leftSampleSize = rightSampleSize;
6636 }
6637 }
6638 }
6639
6640 leftCategorySize = leftSampleSize * options.categoryPercentage;
6641 rightCategorySize = rightSampleSize * options.categoryPercentage;
6642 fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount;
6643 size = fullBarSize * options.barPercentage;
6644
6645 size = Math.min(
6646 helpers.valueOrDefault(options.barThickness, size),
6647 helpers.valueOrDefault(options.maxBarThickness, Infinity));
6648
6649 base -= leftCategorySize;
6650 base += fullBarSize * stackIndex;
6651 base += (fullBarSize - size) / 2;
6652
6653 return {
6654 size: size,
6655 base: base,
6656 head: base + size,
6657 center: base + size / 2
6658 };
6659 },
6660
6661 draw: function() {
6662 var me = this;
6663 var chart = me.chart;
6664 var scale = me.getValueScale();
6665 var rects = me.getMeta().data;
6666 var dataset = me.getDataset();
6667 var ilen = rects.length;
6668 var i = 0;
6669
6670 helpers.canvas.clipArea(chart.ctx, chart.chartArea);
6671
6672 for (; i < ilen; ++i) {
6673 if (!isNaN(scale.getRightValue(dataset.data[i]))) {
6674 rects[i].draw();
6675 }
6676 }
6677
6678 helpers.canvas.unclipArea(chart.ctx);
6679 },
6680
6681 setHoverStyle: function(rectangle) {
6682 var dataset = this.chart.data.datasets[rectangle._datasetIndex];
6683 var index = rectangle._index;
6684 var custom = rectangle.custom || {};
6685 var model = rectangle._model;
6686
6687 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
6688 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
6689 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
6690 },
6691
6692 removeHoverStyle: function(rectangle) {
6693 var dataset = this.chart.data.datasets[rectangle._datasetIndex];
6694 var index = rectangle._index;
6695 var custom = rectangle.custom || {};
6696 var model = rectangle._model;
6697 var rectangleElementOptions = this.chart.options.elements.rectangle;
6698
6699 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
6700 model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
6701 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
6702 }
6703 });
6704
6705 Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
6706 /**
6707 * @private
6708 */
6709 getValueScaleId: function() {
6710 return this.getMeta().xAxisID;
6711 },
6712
6713 /**
6714 * @private
6715 */
6716 getIndexScaleId: function() {
6717 return this.getMeta().yAxisID;
6718 }
6719 });
6720 };
6721
6722 },{"25":25,"40":40,"45":45}],16:[function(require,module,exports){
6723 'use strict';
6724
6725 var defaults = require(25);
6726 var elements = require(40);
6727 var helpers = require(45);
6728
6729 defaults._set('bubble', {
6730 hover: {
6731 mode: 'single'
6732 },
6733
6734 scales: {
6735 xAxes: [{
6736 type: 'linear', // bubble should probably use a linear scale by default
6737 position: 'bottom',
6738 id: 'x-axis-0' // need an ID so datasets can reference the scale
6739 }],
6740 yAxes: [{
6741 type: 'linear',
6742 position: 'left',
6743 id: 'y-axis-0'
6744 }]
6745 },
6746
6747 tooltips: {
6748 callbacks: {
6749 title: function() {
6750 // Title doesn't make sense for scatter since we format the data as a point
6751 return '';
6752 },
6753 label: function(item, data) {
6754 var datasetLabel = data.datasets[item.datasetIndex].label || '';
6755 var dataPoint = data.datasets[item.datasetIndex].data[item.index];
6756 return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
6757 }
6758 }
6759 }
6760 });
6761
6762
6763 module.exports = function(Chart) {
6764
6765 Chart.controllers.bubble = Chart.DatasetController.extend({
6766 /**
6767 * @protected
6768 */
6769 dataElementType: elements.Point,
6770
6771 /**
6772 * @protected
6773 */
6774 update: function(reset) {
6775 var me = this;
6776 var meta = me.getMeta();
6777 var points = meta.data;
6778
6779 // Update Points
6780 helpers.each(points, function(point, index) {
6781 me.updateElement(point, index, reset);
6782 });
6783 },
6784
6785 /**
6786 * @protected
6787 */
6788 updateElement: function(point, index, reset) {
6789 var me = this;
6790 var meta = me.getMeta();
6791 var custom = point.custom || {};
6792 var xScale = me.getScaleForId(meta.xAxisID);
6793 var yScale = me.getScaleForId(meta.yAxisID);
6794 var options = me._resolveElementOptions(point, index);
6795 var data = me.getDataset().data[index];
6796 var dsIndex = me.index;
6797
6798 var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
6799 var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
6800
6801 point._xScale = xScale;
6802 point._yScale = yScale;
6803 point._options = options;
6804 point._datasetIndex = dsIndex;
6805 point._index = index;
6806 point._model = {
6807 backgroundColor: options.backgroundColor,
6808 borderColor: options.borderColor,
6809 borderWidth: options.borderWidth,
6810 hitRadius: options.hitRadius,
6811 pointStyle: options.pointStyle,
6812 radius: reset ? 0 : options.radius,
6813 skip: custom.skip || isNaN(x) || isNaN(y),
6814 x: x,
6815 y: y,
6816 };
6817
6818 point.pivot();
6819 },
6820
6821 /**
6822 * @protected
6823 */
6824 setHoverStyle: function(point) {
6825 var model = point._model;
6826 var options = point._options;
6827
6828 model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
6829 model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
6830 model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
6831 model.radius = options.radius + options.hoverRadius;
6832 },
6833
6834 /**
6835 * @protected
6836 */
6837 removeHoverStyle: function(point) {
6838 var model = point._model;
6839 var options = point._options;
6840
6841 model.backgroundColor = options.backgroundColor;
6842 model.borderColor = options.borderColor;
6843 model.borderWidth = options.borderWidth;
6844 model.radius = options.radius;
6845 },
6846
6847 /**
6848 * @private
6849 */
6850 _resolveElementOptions: function(point, index) {
6851 var me = this;
6852 var chart = me.chart;
6853 var datasets = chart.data.datasets;
6854 var dataset = datasets[me.index];
6855 var custom = point.custom || {};
6856 var options = chart.options.elements.point;
6857 var resolve = helpers.options.resolve;
6858 var data = dataset.data[index];
6859 var values = {};
6860 var i, ilen, key;
6861
6862 // Scriptable options
6863 var context = {
6864 chart: chart,
6865 dataIndex: index,
6866 dataset: dataset,
6867 datasetIndex: me.index
6868 };
6869
6870 var keys = [
6871 'backgroundColor',
6872 'borderColor',
6873 'borderWidth',
6874 'hoverBackgroundColor',
6875 'hoverBorderColor',
6876 'hoverBorderWidth',
6877 'hoverRadius',
6878 'hitRadius',
6879 'pointStyle'
6880 ];
6881
6882 for (i = 0, ilen = keys.length; i < ilen; ++i) {
6883 key = keys[i];
6884 values[key] = resolve([
6885 custom[key],
6886 dataset[key],
6887 options[key]
6888 ], context, index);
6889 }
6890
6891 // Custom radius resolution
6892 values.radius = resolve([
6893 custom.radius,
6894 data ? data.r : undefined,
6895 dataset.radius,
6896 options.radius
6897 ], context, index);
6898
6899 return values;
6900 }
6901 });
6902 };
6903
6904 },{"25":25,"40":40,"45":45}],17:[function(require,module,exports){
6905 'use strict';
6906
6907 var defaults = require(25);
6908 var elements = require(40);
6909 var helpers = require(45);
6910
6911 defaults._set('doughnut', {
6912 animation: {
6913 // Boolean - Whether we animate the rotation of the Doughnut
6914 animateRotate: true,
6915 // Boolean - Whether we animate scaling the Doughnut from the centre
6916 animateScale: false
6917 },
6918 hover: {
6919 mode: 'single'
6920 },
6921 legendCallback: function(chart) {
6922 var text = [];
6923 text.push('<ul class="' + chart.id + '-legend">');
6924
6925 var data = chart.data;
6926 var datasets = data.datasets;
6927 var labels = data.labels;
6928
6929 if (datasets.length) {
6930 for (var i = 0; i < datasets[0].data.length; ++i) {
6931 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
6932 if (labels[i]) {
6933 text.push(labels[i]);
6934 }
6935 text.push('</li>');
6936 }
6937 }
6938
6939 text.push('</ul>');
6940 return text.join('');
6941 },
6942 legend: {
6943 labels: {
6944 generateLabels: function(chart) {
6945 var data = chart.data;
6946 if (data.labels.length && data.datasets.length) {
6947 return data.labels.map(function(label, i) {
6948 var meta = chart.getDatasetMeta(0);
6949 var ds = data.datasets[0];
6950 var arc = meta.data[i];
6951 var custom = arc && arc.custom || {};
6952 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
6953 var arcOpts = chart.options.elements.arc;
6954 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
6955 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
6956 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
6957
6958 return {
6959 text: label,
6960 fillStyle: fill,
6961 strokeStyle: stroke,
6962 lineWidth: bw,
6963 hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
6964
6965 // Extra data used for toggling the correct item
6966 index: i
6967 };
6968 });
6969 }
6970 return [];
6971 }
6972 },
6973
6974 onClick: function(e, legendItem) {
6975 var index = legendItem.index;
6976 var chart = this.chart;
6977 var i, ilen, meta;
6978
6979 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
6980 meta = chart.getDatasetMeta(i);
6981 // toggle visibility of index if exists
6982 if (meta.data[index]) {
6983 meta.data[index].hidden = !meta.data[index].hidden;
6984 }
6985 }
6986
6987 chart.update();
6988 }
6989 },
6990
6991 // The percentage of the chart that we cut out of the middle.
6992 cutoutPercentage: 50,
6993
6994 // The rotation of the chart, where the first data arc begins.
6995 rotation: Math.PI * -0.5,
6996
6997 // The total circumference of the chart.
6998 circumference: Math.PI * 2.0,
6999
7000 // Need to override these to give a nice default
7001 tooltips: {
7002 callbacks: {
7003 title: function() {
7004 return '';
7005 },
7006 label: function(tooltipItem, data) {
7007 var dataLabel = data.labels[tooltipItem.index];
7008 var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
7009
7010 if (helpers.isArray(dataLabel)) {
7011 // show value on first line of multiline label
7012 // need to clone because we are changing the value
7013 dataLabel = dataLabel.slice();
7014 dataLabel[0] += value;
7015 } else {
7016 dataLabel += value;
7017 }
7018
7019 return dataLabel;
7020 }
7021 }
7022 }
7023 });
7024
7025 defaults._set('pie', helpers.clone(defaults.doughnut));
7026 defaults._set('pie', {
7027 cutoutPercentage: 0
7028 });
7029
7030 module.exports = function(Chart) {
7031
7032 Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
7033
7034 dataElementType: elements.Arc,
7035
7036 linkScales: helpers.noop,
7037
7038 // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
7039 getRingIndex: function(datasetIndex) {
7040 var ringIndex = 0;
7041
7042 for (var j = 0; j < datasetIndex; ++j) {
7043 if (this.chart.isDatasetVisible(j)) {
7044 ++ringIndex;
7045 }
7046 }
7047
7048 return ringIndex;
7049 },
7050
7051 update: function(reset) {
7052 var me = this;
7053 var chart = me.chart;
7054 var chartArea = chart.chartArea;
7055 var opts = chart.options;
7056 var arcOpts = opts.elements.arc;
7057 var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
7058 var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
7059 var minSize = Math.min(availableWidth, availableHeight);
7060 var offset = {x: 0, y: 0};
7061 var meta = me.getMeta();
7062 var cutoutPercentage = opts.cutoutPercentage;
7063 var circumference = opts.circumference;
7064
7065 // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
7066 if (circumference < Math.PI * 2.0) {
7067 var startAngle = opts.rotation % (Math.PI * 2.0);
7068 startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
7069 var endAngle = startAngle + circumference;
7070 var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
7071 var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
7072 var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
7073 var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
7074 var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
7075 var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
7076 var cutout = cutoutPercentage / 100.0;
7077 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))};
7078 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))};
7079 var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
7080 minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
7081 offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
7082 }
7083
7084 chart.borderWidth = me.getMaxBorderWidth(meta.data);
7085 chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
7086 chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
7087 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
7088 chart.offsetX = offset.x * chart.outerRadius;
7089 chart.offsetY = offset.y * chart.outerRadius;
7090
7091 meta.total = me.calculateTotal();
7092
7093 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
7094 me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
7095
7096 helpers.each(meta.data, function(arc, index) {
7097 me.updateElement(arc, index, reset);
7098 });
7099 },
7100
7101 updateElement: function(arc, index, reset) {
7102 var me = this;
7103 var chart = me.chart;
7104 var chartArea = chart.chartArea;
7105 var opts = chart.options;
7106 var animationOpts = opts.animation;
7107 var centerX = (chartArea.left + chartArea.right) / 2;
7108 var centerY = (chartArea.top + chartArea.bottom) / 2;
7109 var startAngle = opts.rotation; // non reset case handled later
7110 var endAngle = opts.rotation; // non reset case handled later
7111 var dataset = me.getDataset();
7112 var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
7113 var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
7114 var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
7115 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
7116
7117 helpers.extend(arc, {
7118 // Utility
7119 _datasetIndex: me.index,
7120 _index: index,
7121
7122 // Desired view properties
7123 _model: {
7124 x: centerX + chart.offsetX,
7125 y: centerY + chart.offsetY,
7126 startAngle: startAngle,
7127 endAngle: endAngle,
7128 circumference: circumference,
7129 outerRadius: outerRadius,
7130 innerRadius: innerRadius,
7131 label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
7132 }
7133 });
7134
7135 var model = arc._model;
7136 // Resets the visual styles
7137 this.removeHoverStyle(arc);
7138
7139 // Set correct angles if not resetting
7140 if (!reset || !animationOpts.animateRotate) {
7141 if (index === 0) {
7142 model.startAngle = opts.rotation;
7143 } else {
7144 model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
7145 }
7146
7147 model.endAngle = model.startAngle + model.circumference;
7148 }
7149
7150 arc.pivot();
7151 },
7152
7153 removeHoverStyle: function(arc) {
7154 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
7155 },
7156
7157 calculateTotal: function() {
7158 var dataset = this.getDataset();
7159 var meta = this.getMeta();
7160 var total = 0;
7161 var value;
7162
7163 helpers.each(meta.data, function(element, index) {
7164 value = dataset.data[index];
7165 if (!isNaN(value) && !element.hidden) {
7166 total += Math.abs(value);
7167 }
7168 });
7169
7170 /* if (total === 0) {
7171 total = NaN;
7172 }*/
7173
7174 return total;
7175 },
7176
7177 calculateCircumference: function(value) {
7178 var total = this.getMeta().total;
7179 if (total > 0 && !isNaN(value)) {
7180 return (Math.PI * 2.0) * (value / total);
7181 }
7182 return 0;
7183 },
7184
7185 // gets the max border or hover width to properly scale pie charts
7186 getMaxBorderWidth: function(arcs) {
7187 var max = 0;
7188 var index = this.index;
7189 var length = arcs.length;
7190 var borderWidth;
7191 var hoverWidth;
7192
7193 for (var i = 0; i < length; i++) {
7194 borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
7195 hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
7196
7197 max = borderWidth > max ? borderWidth : max;
7198 max = hoverWidth > max ? hoverWidth : max;
7199 }
7200 return max;
7201 }
7202 });
7203 };
7204
7205 },{"25":25,"40":40,"45":45}],18:[function(require,module,exports){
7206 'use strict';
7207
7208 var defaults = require(25);
7209 var elements = require(40);
7210 var helpers = require(45);
7211
7212 defaults._set('line', {
7213 showLines: true,
7214 spanGaps: false,
7215
7216 hover: {
7217 mode: 'label'
7218 },
7219
7220 scales: {
7221 xAxes: [{
7222 type: 'category',
7223 id: 'x-axis-0'
7224 }],
7225 yAxes: [{
7226 type: 'linear',
7227 id: 'y-axis-0'
7228 }]
7229 }
7230 });
7231
7232 module.exports = function(Chart) {
7233
7234 function lineEnabled(dataset, options) {
7235 return helpers.valueOrDefault(dataset.showLine, options.showLines);
7236 }
7237
7238 Chart.controllers.line = Chart.DatasetController.extend({
7239
7240 datasetElementType: elements.Line,
7241
7242 dataElementType: elements.Point,
7243
7244 update: function(reset) {
7245 var me = this;
7246 var meta = me.getMeta();
7247 var line = meta.dataset;
7248 var points = meta.data || [];
7249 var options = me.chart.options;
7250 var lineElementOptions = options.elements.line;
7251 var scale = me.getScaleForId(meta.yAxisID);
7252 var i, ilen, custom;
7253 var dataset = me.getDataset();
7254 var showLine = lineEnabled(dataset, options);
7255
7256 // Update Line
7257 if (showLine) {
7258 custom = line.custom || {};
7259
7260 // Compatibility: If the properties are defined with only the old name, use those values
7261 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
7262 dataset.lineTension = dataset.tension;
7263 }
7264
7265 // Utility
7266 line._scale = scale;
7267 line._datasetIndex = me.index;
7268 // Data
7269 line._children = points;
7270 // Model
7271 line._model = {
7272 // Appearance
7273 // The default behavior of lines is to break at null values, according
7274 // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
7275 // This option gives lines the ability to span gaps
7276 spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
7277 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
7278 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
7279 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
7280 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
7281 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
7282 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
7283 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
7284 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
7285 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
7286 steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
7287 cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
7288 };
7289
7290 line.pivot();
7291 }
7292
7293 // Update Points
7294 for (i = 0, ilen = points.length; i < ilen; ++i) {
7295 me.updateElement(points[i], i, reset);
7296 }
7297
7298 if (showLine && line._model.tension !== 0) {
7299 me.updateBezierControlPoints();
7300 }
7301
7302 // Now pivot the point for animation
7303 for (i = 0, ilen = points.length; i < ilen; ++i) {
7304 points[i].pivot();
7305 }
7306 },
7307
7308 getPointBackgroundColor: function(point, index) {
7309 var backgroundColor = this.chart.options.elements.point.backgroundColor;
7310 var dataset = this.getDataset();
7311 var custom = point.custom || {};
7312
7313 if (custom.backgroundColor) {
7314 backgroundColor = custom.backgroundColor;
7315 } else if (dataset.pointBackgroundColor) {
7316 backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
7317 } else if (dataset.backgroundColor) {
7318 backgroundColor = dataset.backgroundColor;
7319 }
7320
7321 return backgroundColor;
7322 },
7323
7324 getPointBorderColor: function(point, index) {
7325 var borderColor = this.chart.options.elements.point.borderColor;
7326 var dataset = this.getDataset();
7327 var custom = point.custom || {};
7328
7329 if (custom.borderColor) {
7330 borderColor = custom.borderColor;
7331 } else if (dataset.pointBorderColor) {
7332 borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
7333 } else if (dataset.borderColor) {
7334 borderColor = dataset.borderColor;
7335 }
7336
7337 return borderColor;
7338 },
7339
7340 getPointBorderWidth: function(point, index) {
7341 var borderWidth = this.chart.options.elements.point.borderWidth;
7342 var dataset = this.getDataset();
7343 var custom = point.custom || {};
7344
7345 if (!isNaN(custom.borderWidth)) {
7346 borderWidth = custom.borderWidth;
7347 } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
7348 borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
7349 } else if (!isNaN(dataset.borderWidth)) {
7350 borderWidth = dataset.borderWidth;
7351 }
7352
7353 return borderWidth;
7354 },
7355
7356 updateElement: function(point, index, reset) {
7357 var me = this;
7358 var meta = me.getMeta();
7359 var custom = point.custom || {};
7360 var dataset = me.getDataset();
7361 var datasetIndex = me.index;
7362 var value = dataset.data[index];
7363 var yScale = me.getScaleForId(meta.yAxisID);
7364 var xScale = me.getScaleForId(meta.xAxisID);
7365 var pointOptions = me.chart.options.elements.point;
7366 var x, y;
7367
7368 // Compatibility: If the properties are defined with only the old name, use those values
7369 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
7370 dataset.pointRadius = dataset.radius;
7371 }
7372 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
7373 dataset.pointHitRadius = dataset.hitRadius;
7374 }
7375
7376 x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
7377 y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
7378
7379 // Utility
7380 point._xScale = xScale;
7381 point._yScale = yScale;
7382 point._datasetIndex = datasetIndex;
7383 point._index = index;
7384
7385 // Desired view properties
7386 point._model = {
7387 x: x,
7388 y: y,
7389 skip: custom.skip || isNaN(x) || isNaN(y),
7390 // Appearance
7391 radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
7392 pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
7393 backgroundColor: me.getPointBackgroundColor(point, index),
7394 borderColor: me.getPointBorderColor(point, index),
7395 borderWidth: me.getPointBorderWidth(point, index),
7396 tension: meta.dataset._model ? meta.dataset._model.tension : 0,
7397 steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
7398 // Tooltip
7399 hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
7400 };
7401 },
7402
7403 calculatePointY: function(value, index, datasetIndex) {
7404 var me = this;
7405 var chart = me.chart;
7406 var meta = me.getMeta();
7407 var yScale = me.getScaleForId(meta.yAxisID);
7408 var sumPos = 0;
7409 var sumNeg = 0;
7410 var i, ds, dsMeta;
7411
7412 if (yScale.options.stacked) {
7413 for (i = 0; i < datasetIndex; i++) {
7414 ds = chart.data.datasets[i];
7415 dsMeta = chart.getDatasetMeta(i);
7416 if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
7417 var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
7418 if (stackedRightValue < 0) {
7419 sumNeg += stackedRightValue || 0;
7420 } else {
7421 sumPos += stackedRightValue || 0;
7422 }
7423 }
7424 }
7425
7426 var rightValue = Number(yScale.getRightValue(value));
7427 if (rightValue < 0) {
7428 return yScale.getPixelForValue(sumNeg + rightValue);
7429 }
7430 return yScale.getPixelForValue(sumPos + rightValue);
7431 }
7432
7433 return yScale.getPixelForValue(value);
7434 },
7435
7436 updateBezierControlPoints: function() {
7437 var me = this;
7438 var meta = me.getMeta();
7439 var area = me.chart.chartArea;
7440 var points = (meta.data || []);
7441 var i, ilen, point, model, controlPoints;
7442
7443 // Only consider points that are drawn in case the spanGaps option is used
7444 if (meta.dataset._model.spanGaps) {
7445 points = points.filter(function(pt) {
7446 return !pt._model.skip;
7447 });
7448 }
7449
7450 function capControlPoint(pt, min, max) {
7451 return Math.max(Math.min(pt, max), min);
7452 }
7453
7454 if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
7455 helpers.splineCurveMonotone(points);
7456 } else {
7457 for (i = 0, ilen = points.length; i < ilen; ++i) {
7458 point = points[i];
7459 model = point._model;
7460 controlPoints = helpers.splineCurve(
7461 helpers.previousItem(points, i)._model,
7462 model,
7463 helpers.nextItem(points, i)._model,
7464 meta.dataset._model.tension
7465 );
7466 model.controlPointPreviousX = controlPoints.previous.x;
7467 model.controlPointPreviousY = controlPoints.previous.y;
7468 model.controlPointNextX = controlPoints.next.x;
7469 model.controlPointNextY = controlPoints.next.y;
7470 }
7471 }
7472
7473 if (me.chart.options.elements.line.capBezierPoints) {
7474 for (i = 0, ilen = points.length; i < ilen; ++i) {
7475 model = points[i]._model;
7476 model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
7477 model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
7478 model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
7479 model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
7480 }
7481 }
7482 },
7483
7484 draw: function() {
7485 var me = this;
7486 var chart = me.chart;
7487 var meta = me.getMeta();
7488 var points = meta.data || [];
7489 var area = chart.chartArea;
7490 var ilen = points.length;
7491 var i = 0;
7492
7493 helpers.canvas.clipArea(chart.ctx, area);
7494
7495 if (lineEnabled(me.getDataset(), chart.options)) {
7496 meta.dataset.draw();
7497 }
7498
7499 helpers.canvas.unclipArea(chart.ctx);
7500
7501 // Draw the points
7502 for (; i < ilen; ++i) {
7503 points[i].draw(area);
7504 }
7505 },
7506
7507 setHoverStyle: function(point) {
7508 // Point
7509 var dataset = this.chart.data.datasets[point._datasetIndex];
7510 var index = point._index;
7511 var custom = point.custom || {};
7512 var model = point._model;
7513
7514 model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
7515 model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
7516 model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
7517 model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
7518 },
7519
7520 removeHoverStyle: function(point) {
7521 var me = this;
7522 var dataset = me.chart.data.datasets[point._datasetIndex];
7523 var index = point._index;
7524 var custom = point.custom || {};
7525 var model = point._model;
7526
7527 // Compatibility: If the properties are defined with only the old name, use those values
7528 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
7529 dataset.pointRadius = dataset.radius;
7530 }
7531
7532 model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
7533 model.backgroundColor = me.getPointBackgroundColor(point, index);
7534 model.borderColor = me.getPointBorderColor(point, index);
7535 model.borderWidth = me.getPointBorderWidth(point, index);
7536 }
7537 });
7538 };
7539
7540 },{"25":25,"40":40,"45":45}],19:[function(require,module,exports){
7541 'use strict';
7542
7543 var defaults = require(25);
7544 var elements = require(40);
7545 var helpers = require(45);
7546
7547 defaults._set('polarArea', {
7548 scale: {
7549 type: 'radialLinear',
7550 angleLines: {
7551 display: false
7552 },
7553 gridLines: {
7554 circular: true
7555 },
7556 pointLabels: {
7557 display: false
7558 },
7559 ticks: {
7560 beginAtZero: true
7561 }
7562 },
7563
7564 // Boolean - Whether to animate the rotation of the chart
7565 animation: {
7566 animateRotate: true,
7567 animateScale: true
7568 },
7569
7570 startAngle: -0.5 * Math.PI,
7571 legendCallback: function(chart) {
7572 var text = [];
7573 text.push('<ul class="' + chart.id + '-legend">');
7574
7575 var data = chart.data;
7576 var datasets = data.datasets;
7577 var labels = data.labels;
7578
7579 if (datasets.length) {
7580 for (var i = 0; i < datasets[0].data.length; ++i) {
7581 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
7582 if (labels[i]) {
7583 text.push(labels[i]);
7584 }
7585 text.push('</li>');
7586 }
7587 }
7588
7589 text.push('</ul>');
7590 return text.join('');
7591 },
7592 legend: {
7593 labels: {
7594 generateLabels: function(chart) {
7595 var data = chart.data;
7596 if (data.labels.length && data.datasets.length) {
7597 return data.labels.map(function(label, i) {
7598 var meta = chart.getDatasetMeta(0);
7599 var ds = data.datasets[0];
7600 var arc = meta.data[i];
7601 var custom = arc.custom || {};
7602 var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
7603 var arcOpts = chart.options.elements.arc;
7604 var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
7605 var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
7606 var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
7607
7608 return {
7609 text: label,
7610 fillStyle: fill,
7611 strokeStyle: stroke,
7612 lineWidth: bw,
7613 hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
7614
7615 // Extra data used for toggling the correct item
7616 index: i
7617 };
7618 });
7619 }
7620 return [];
7621 }
7622 },
7623
7624 onClick: function(e, legendItem) {
7625 var index = legendItem.index;
7626 var chart = this.chart;
7627 var i, ilen, meta;
7628
7629 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
7630 meta = chart.getDatasetMeta(i);
7631 meta.data[index].hidden = !meta.data[index].hidden;
7632 }
7633
7634 chart.update();
7635 }
7636 },
7637
7638 // Need to override these to give a nice default
7639 tooltips: {
7640 callbacks: {
7641 title: function() {
7642 return '';
7643 },
7644 label: function(item, data) {
7645 return data.labels[item.index] + ': ' + item.yLabel;
7646 }
7647 }
7648 }
7649 });
7650
7651 module.exports = function(Chart) {
7652
7653 Chart.controllers.polarArea = Chart.DatasetController.extend({
7654
7655 dataElementType: elements.Arc,
7656
7657 linkScales: helpers.noop,
7658
7659 update: function(reset) {
7660 var me = this;
7661 var chart = me.chart;
7662 var chartArea = chart.chartArea;
7663 var meta = me.getMeta();
7664 var opts = chart.options;
7665 var arcOpts = opts.elements.arc;
7666 var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
7667 chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
7668 chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
7669 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
7670
7671 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
7672 me.innerRadius = me.outerRadius - chart.radiusLength;
7673
7674 meta.count = me.countVisibleElements();
7675
7676 helpers.each(meta.data, function(arc, index) {
7677 me.updateElement(arc, index, reset);
7678 });
7679 },
7680
7681 updateElement: function(arc, index, reset) {
7682 var me = this;
7683 var chart = me.chart;
7684 var dataset = me.getDataset();
7685 var opts = chart.options;
7686 var animationOpts = opts.animation;
7687 var scale = chart.scale;
7688 var labels = chart.data.labels;
7689
7690 var circumference = me.calculateCircumference(dataset.data[index]);
7691 var centerX = scale.xCenter;
7692 var centerY = scale.yCenter;
7693
7694 // If there is NaN data before us, we need to calculate the starting angle correctly.
7695 // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
7696 var visibleCount = 0;
7697 var meta = me.getMeta();
7698 for (var i = 0; i < index; ++i) {
7699 if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
7700 ++visibleCount;
7701 }
7702 }
7703
7704 // var negHalfPI = -0.5 * Math.PI;
7705 var datasetStartAngle = opts.startAngle;
7706 var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
7707 var startAngle = datasetStartAngle + (circumference * visibleCount);
7708 var endAngle = startAngle + (arc.hidden ? 0 : circumference);
7709
7710 var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
7711
7712 helpers.extend(arc, {
7713 // Utility
7714 _datasetIndex: me.index,
7715 _index: index,
7716 _scale: scale,
7717
7718 // Desired view properties
7719 _model: {
7720 x: centerX,
7721 y: centerY,
7722 innerRadius: 0,
7723 outerRadius: reset ? resetRadius : distance,
7724 startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
7725 endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
7726 label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
7727 }
7728 });
7729
7730 // Apply border and fill style
7731 me.removeHoverStyle(arc);
7732
7733 arc.pivot();
7734 },
7735
7736 removeHoverStyle: function(arc) {
7737 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
7738 },
7739
7740 countVisibleElements: function() {
7741 var dataset = this.getDataset();
7742 var meta = this.getMeta();
7743 var count = 0;
7744
7745 helpers.each(meta.data, function(element, index) {
7746 if (!isNaN(dataset.data[index]) && !element.hidden) {
7747 count++;
7748 }
7749 });
7750
7751 return count;
7752 },
7753
7754 calculateCircumference: function(value) {
7755 var count = this.getMeta().count;
7756 if (count > 0 && !isNaN(value)) {
7757 return (2 * Math.PI) / count;
7758 }
7759 return 0;
7760 }
7761 });
7762 };
7763
7764 },{"25":25,"40":40,"45":45}],20:[function(require,module,exports){
7765 'use strict';
7766
7767 var defaults = require(25);
7768 var elements = require(40);
7769 var helpers = require(45);
7770
7771 defaults._set('radar', {
7772 scale: {
7773 type: 'radialLinear'
7774 },
7775 elements: {
7776 line: {
7777 tension: 0 // no bezier in radar
7778 }
7779 }
7780 });
7781
7782 module.exports = function(Chart) {
7783
7784 Chart.controllers.radar = Chart.DatasetController.extend({
7785
7786 datasetElementType: elements.Line,
7787
7788 dataElementType: elements.Point,
7789
7790 linkScales: helpers.noop,
7791
7792 update: function(reset) {
7793 var me = this;
7794 var meta = me.getMeta();
7795 var line = meta.dataset;
7796 var points = meta.data;
7797 var custom = line.custom || {};
7798 var dataset = me.getDataset();
7799 var lineElementOptions = me.chart.options.elements.line;
7800 var scale = me.chart.scale;
7801
7802 // Compatibility: If the properties are defined with only the old name, use those values
7803 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
7804 dataset.lineTension = dataset.tension;
7805 }
7806
7807 helpers.extend(meta.dataset, {
7808 // Utility
7809 _datasetIndex: me.index,
7810 _scale: scale,
7811 // Data
7812 _children: points,
7813 _loop: true,
7814 // Model
7815 _model: {
7816 // Appearance
7817 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
7818 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
7819 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
7820 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
7821 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
7822 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
7823 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
7824 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
7825 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
7826 }
7827 });
7828
7829 meta.dataset.pivot();
7830
7831 // Update Points
7832 helpers.each(points, function(point, index) {
7833 me.updateElement(point, index, reset);
7834 }, me);
7835
7836 // Update bezier control points
7837 me.updateBezierControlPoints();
7838 },
7839 updateElement: function(point, index, reset) {
7840 var me = this;
7841 var custom = point.custom || {};
7842 var dataset = me.getDataset();
7843 var scale = me.chart.scale;
7844 var pointElementOptions = me.chart.options.elements.point;
7845 var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
7846
7847 // Compatibility: If the properties are defined with only the old name, use those values
7848 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
7849 dataset.pointRadius = dataset.radius;
7850 }
7851 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
7852 dataset.pointHitRadius = dataset.hitRadius;
7853 }
7854
7855 helpers.extend(point, {
7856 // Utility
7857 _datasetIndex: me.index,
7858 _index: index,
7859 _scale: scale,
7860
7861 // Desired view properties
7862 _model: {
7863 x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
7864 y: reset ? scale.yCenter : pointPosition.y,
7865
7866 // Appearance
7867 tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
7868 radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
7869 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
7870 borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
7871 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
7872 pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
7873
7874 // Tooltip
7875 hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
7876 }
7877 });
7878
7879 point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
7880 },
7881 updateBezierControlPoints: function() {
7882 var chartArea = this.chart.chartArea;
7883 var meta = this.getMeta();
7884
7885 helpers.each(meta.data, function(point, index) {
7886 var model = point._model;
7887 var controlPoints = helpers.splineCurve(
7888 helpers.previousItem(meta.data, index, true)._model,
7889 model,
7890 helpers.nextItem(meta.data, index, true)._model,
7891 model.tension
7892 );
7893
7894 // Prevent the bezier going outside of the bounds of the graph
7895 model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
7896 model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
7897
7898 model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
7899 model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
7900
7901 // Now pivot the point for animation
7902 point.pivot();
7903 });
7904 },
7905
7906 setHoverStyle: function(point) {
7907 // Point
7908 var dataset = this.chart.data.datasets[point._datasetIndex];
7909 var custom = point.custom || {};
7910 var index = point._index;
7911 var model = point._model;
7912
7913 model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
7914 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
7915 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
7916 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
7917 },
7918
7919 removeHoverStyle: function(point) {
7920 var dataset = this.chart.data.datasets[point._datasetIndex];
7921 var custom = point.custom || {};
7922 var index = point._index;
7923 var model = point._model;
7924 var pointElementOptions = this.chart.options.elements.point;
7925
7926 model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius);
7927 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);
7928 model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);
7929 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);
7930 }
7931 });
7932 };
7933
7934 },{"25":25,"40":40,"45":45}],21:[function(require,module,exports){
7935 'use strict';
7936
7937 var defaults = require(25);
7938
7939 defaults._set('scatter', {
7940 hover: {
7941 mode: 'single'
7942 },
7943
7944 scales: {
7945 xAxes: [{
7946 id: 'x-axis-1', // need an ID so datasets can reference the scale
7947 type: 'linear', // scatter should not use a category axis
7948 position: 'bottom'
7949 }],
7950 yAxes: [{
7951 id: 'y-axis-1',
7952 type: 'linear',
7953 position: 'left'
7954 }]
7955 },
7956
7957 showLines: false,
7958
7959 tooltips: {
7960 callbacks: {
7961 title: function() {
7962 return ''; // doesn't make sense for scatter since data are formatted as a point
7963 },
7964 label: function(item) {
7965 return '(' + item.xLabel + ', ' + item.yLabel + ')';
7966 }
7967 }
7968 }
7969 });
7970
7971 module.exports = function(Chart) {
7972
7973 // Scatter charts use line controllers
7974 Chart.controllers.scatter = Chart.controllers.line;
7975
7976 };
7977
7978 },{"25":25}],22:[function(require,module,exports){
7979 /* global window: false */
7980 'use strict';
7981
7982 var defaults = require(25);
7983 var Element = require(26);
7984 var helpers = require(45);
7985
7986 defaults._set('global', {
7987 animation: {
7988 duration: 1000,
7989 easing: 'easeOutQuart',
7990 onProgress: helpers.noop,
7991 onComplete: helpers.noop
7992 }
7993 });
7994
7995 module.exports = function(Chart) {
7996
7997 Chart.Animation = Element.extend({
7998 chart: null, // the animation associated chart instance
7999 currentStep: 0, // the current animation step
8000 numSteps: 60, // default number of steps
8001 easing: '', // the easing to use for this animation
8002 render: null, // render function used by the animation service
8003
8004 onAnimationProgress: null, // user specified callback to fire on each step of the animation
8005 onAnimationComplete: null, // user specified callback to fire when the animation finishes
8006 });
8007
8008 Chart.animationService = {
8009 frameDuration: 17,
8010 animations: [],
8011 dropFrames: 0,
8012 request: null,
8013
8014 /**
8015 * @param {Chart} chart - The chart to animate.
8016 * @param {Chart.Animation} animation - The animation that we will animate.
8017 * @param {Number} duration - The animation duration in ms.
8018 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
8019 */
8020 addAnimation: function(chart, animation, duration, lazy) {
8021 var animations = this.animations;
8022 var i, ilen;
8023
8024 animation.chart = chart;
8025
8026 if (!lazy) {
8027 chart.animating = true;
8028 }
8029
8030 for (i = 0, ilen = animations.length; i < ilen; ++i) {
8031 if (animations[i].chart === chart) {
8032 animations[i] = animation;
8033 return;
8034 }
8035 }
8036
8037 animations.push(animation);
8038
8039 // If there are no animations queued, manually kickstart a digest, for lack of a better word
8040 if (animations.length === 1) {
8041 this.requestAnimationFrame();
8042 }
8043 },
8044
8045 cancelAnimation: function(chart) {
8046 var index = helpers.findIndex(this.animations, function(animation) {
8047 return animation.chart === chart;
8048 });
8049
8050 if (index !== -1) {
8051 this.animations.splice(index, 1);
8052 chart.animating = false;
8053 }
8054 },
8055
8056 requestAnimationFrame: function() {
8057 var me = this;
8058 if (me.request === null) {
8059 // Skip animation frame requests until the active one is executed.
8060 // This can happen when processing mouse events, e.g. 'mousemove'
8061 // and 'mouseout' events will trigger multiple renders.
8062 me.request = helpers.requestAnimFrame.call(window, function() {
8063 me.request = null;
8064 me.startDigest();
8065 });
8066 }
8067 },
8068
8069 /**
8070 * @private
8071 */
8072 startDigest: function() {
8073 var me = this;
8074 var startTime = Date.now();
8075 var framesToDrop = 0;
8076
8077 if (me.dropFrames > 1) {
8078 framesToDrop = Math.floor(me.dropFrames);
8079 me.dropFrames = me.dropFrames % 1;
8080 }
8081
8082 me.advance(1 + framesToDrop);
8083
8084 var endTime = Date.now();
8085
8086 me.dropFrames += (endTime - startTime) / me.frameDuration;
8087
8088 // Do we have more stuff to animate?
8089 if (me.animations.length > 0) {
8090 me.requestAnimationFrame();
8091 }
8092 },
8093
8094 /**
8095 * @private
8096 */
8097 advance: function(count) {
8098 var animations = this.animations;
8099 var animation, chart;
8100 var i = 0;
8101
8102 while (i < animations.length) {
8103 animation = animations[i];
8104 chart = animation.chart;
8105
8106 animation.currentStep = (animation.currentStep || 0) + count;
8107 animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
8108
8109 helpers.callback(animation.render, [chart, animation], chart);
8110 helpers.callback(animation.onAnimationProgress, [animation], chart);
8111
8112 if (animation.currentStep >= animation.numSteps) {
8113 helpers.callback(animation.onAnimationComplete, [animation], chart);
8114 chart.animating = false;
8115 animations.splice(i, 1);
8116 } else {
8117 ++i;
8118 }
8119 }
8120 }
8121 };
8122
8123 /**
8124 * Provided for backward compatibility, use Chart.Animation instead
8125 * @prop Chart.Animation#animationObject
8126 * @deprecated since version 2.6.0
8127 * @todo remove at version 3
8128 */
8129 Object.defineProperty(Chart.Animation.prototype, 'animationObject', {
8130 get: function() {
8131 return this;
8132 }
8133 });
8134
8135 /**
8136 * Provided for backward compatibility, use Chart.Animation#chart instead
8137 * @prop Chart.Animation#chartInstance
8138 * @deprecated since version 2.6.0
8139 * @todo remove at version 3
8140 */
8141 Object.defineProperty(Chart.Animation.prototype, 'chartInstance', {
8142 get: function() {
8143 return this.chart;
8144 },
8145 set: function(value) {
8146 this.chart = value;
8147 }
8148 });
8149
8150 };
8151
8152 },{"25":25,"26":26,"45":45}],23:[function(require,module,exports){
8153 'use strict';
8154
8155 var defaults = require(25);
8156 var helpers = require(45);
8157 var Interaction = require(28);
8158 var platform = require(48);
8159
8160 module.exports = function(Chart) {
8161 var plugins = Chart.plugins;
8162
8163 // Create a dictionary of chart types, to allow for extension of existing types
8164 Chart.types = {};
8165
8166 // Store a reference to each instance - allowing us to globally resize chart instances on window resize.
8167 // Destroy method on the chart will remove the instance of the chart from this reference.
8168 Chart.instances = {};
8169
8170 // Controllers available for dataset visualization eg. bar, line, slice, etc.
8171 Chart.controllers = {};
8172
8173 /**
8174 * Initializes the given config with global and chart default values.
8175 */
8176 function initConfig(config) {
8177 config = config || {};
8178
8179 // Do NOT use configMerge() for the data object because this method merges arrays
8180 // and so would change references to labels and datasets, preventing data updates.
8181 var data = config.data = config.data || {};
8182 data.datasets = data.datasets || [];
8183 data.labels = data.labels || [];
8184
8185 config.options = helpers.configMerge(
8186 defaults.global,
8187 defaults[config.type],
8188 config.options || {});
8189
8190 return config;
8191 }
8192
8193 /**
8194 * Updates the config of the chart
8195 * @param chart {Chart} chart to update the options for
8196 */
8197 function updateConfig(chart) {
8198 var newOptions = chart.options;
8199
8200 // Update Scale(s) with options
8201 if (newOptions.scale) {
8202 chart.scale.options = newOptions.scale;
8203 } else if (newOptions.scales) {
8204 newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
8205 chart.scales[scaleOptions.id].options = scaleOptions;
8206 });
8207 }
8208
8209 // Tooltip
8210 chart.tooltip._options = newOptions.tooltips;
8211 }
8212
8213 function positionIsHorizontal(position) {
8214 return position === 'top' || position === 'bottom';
8215 }
8216
8217 helpers.extend(Chart.prototype, /** @lends Chart */ {
8218 /**
8219 * @private
8220 */
8221 construct: function(item, config) {
8222 var me = this;
8223
8224 config = initConfig(config);
8225
8226 var context = platform.acquireContext(item, config);
8227 var canvas = context && context.canvas;
8228 var height = canvas && canvas.height;
8229 var width = canvas && canvas.width;
8230
8231 me.id = helpers.uid();
8232 me.ctx = context;
8233 me.canvas = canvas;
8234 me.config = config;
8235 me.width = width;
8236 me.height = height;
8237 me.aspectRatio = height ? width / height : null;
8238 me.options = config.options;
8239 me._bufferedRender = false;
8240
8241 /**
8242 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
8243 * the "instance" still need to be defined since it might be called from plugins.
8244 * @prop Chart#chart
8245 * @deprecated since version 2.6.0
8246 * @todo remove at version 3
8247 * @private
8248 */
8249 me.chart = me;
8250 me.controller = me; // chart.chart.controller #inception
8251
8252 // Add the chart instance to the global namespace
8253 Chart.instances[me.id] = me;
8254
8255 // Define alias to the config data: `chart.data === chart.config.data`
8256 Object.defineProperty(me, 'data', {
8257 get: function() {
8258 return me.config.data;
8259 },
8260 set: function(value) {
8261 me.config.data = value;
8262 }
8263 });
8264
8265 if (!context || !canvas) {
8266 // The given item is not a compatible context2d element, let's return before finalizing
8267 // the chart initialization but after setting basic chart / controller properties that
8268 // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
8269 // https://github.com/chartjs/Chart.js/issues/2807
8270 console.error("Failed to create chart: can't acquire context from the given item");
8271 return;
8272 }
8273
8274 me.initialize();
8275 me.update();
8276 },
8277
8278 /**
8279 * @private
8280 */
8281 initialize: function() {
8282 var me = this;
8283
8284 // Before init plugin notification
8285 plugins.notify(me, 'beforeInit');
8286
8287 helpers.retinaScale(me, me.options.devicePixelRatio);
8288
8289 me.bindEvents();
8290
8291 if (me.options.responsive) {
8292 // Initial resize before chart draws (must be silent to preserve initial animations).
8293 me.resize(true);
8294 }
8295
8296 // Make sure scales have IDs and are built before we build any controllers.
8297 me.ensureScalesHaveIDs();
8298 me.buildScales();
8299 me.initToolTip();
8300
8301 // After init plugin notification
8302 plugins.notify(me, 'afterInit');
8303
8304 return me;
8305 },
8306
8307 clear: function() {
8308 helpers.canvas.clear(this);
8309 return this;
8310 },
8311
8312 stop: function() {
8313 // Stops any current animation loop occurring
8314 Chart.animationService.cancelAnimation(this);
8315 return this;
8316 },
8317
8318 resize: function(silent) {
8319 var me = this;
8320 var options = me.options;
8321 var canvas = me.canvas;
8322 var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
8323
8324 // the canvas render width and height will be casted to integers so make sure that
8325 // the canvas display style uses the same integer values to avoid blurring effect.
8326
8327 // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased
8328 var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
8329 var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
8330
8331 if (me.width === newWidth && me.height === newHeight) {
8332 return;
8333 }
8334
8335 canvas.width = me.width = newWidth;
8336 canvas.height = me.height = newHeight;
8337 canvas.style.width = newWidth + 'px';
8338 canvas.style.height = newHeight + 'px';
8339
8340 helpers.retinaScale(me, options.devicePixelRatio);
8341
8342 if (!silent) {
8343 // Notify any plugins about the resize
8344 var newSize = {width: newWidth, height: newHeight};
8345 plugins.notify(me, 'resize', [newSize]);
8346
8347 // Notify of resize
8348 if (me.options.onResize) {
8349 me.options.onResize(me, newSize);
8350 }
8351
8352 me.stop();
8353 me.update(me.options.responsiveAnimationDuration);
8354 }
8355 },
8356
8357 ensureScalesHaveIDs: function() {
8358 var options = this.options;
8359 var scalesOptions = options.scales || {};
8360 var scaleOptions = options.scale;
8361
8362 helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
8363 xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
8364 });
8365
8366 helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
8367 yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
8368 });
8369
8370 if (scaleOptions) {
8371 scaleOptions.id = scaleOptions.id || 'scale';
8372 }
8373 },
8374
8375 /**
8376 * Builds a map of scale ID to scale object for future lookup.
8377 */
8378 buildScales: function() {
8379 var me = this;
8380 var options = me.options;
8381 var scales = me.scales = {};
8382 var items = [];
8383
8384 if (options.scales) {
8385 items = items.concat(
8386 (options.scales.xAxes || []).map(function(xAxisOptions) {
8387 return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
8388 }),
8389 (options.scales.yAxes || []).map(function(yAxisOptions) {
8390 return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
8391 })
8392 );
8393 }
8394
8395 if (options.scale) {
8396 items.push({
8397 options: options.scale,
8398 dtype: 'radialLinear',
8399 isDefault: true,
8400 dposition: 'chartArea'
8401 });
8402 }
8403
8404 helpers.each(items, function(item) {
8405 var scaleOptions = item.options;
8406 var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);
8407 var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
8408 if (!scaleClass) {
8409 return;
8410 }
8411
8412 if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
8413 scaleOptions.position = item.dposition;
8414 }
8415
8416 var scale = new scaleClass({
8417 id: scaleOptions.id,
8418 options: scaleOptions,
8419 ctx: me.ctx,
8420 chart: me
8421 });
8422
8423 scales[scale.id] = scale;
8424 scale.mergeTicksOptions();
8425
8426 // TODO(SB): I think we should be able to remove this custom case (options.scale)
8427 // and consider it as a regular scale part of the "scales"" map only! This would
8428 // make the logic easier and remove some useless? custom code.
8429 if (item.isDefault) {
8430 me.scale = scale;
8431 }
8432 });
8433
8434 Chart.scaleService.addScalesToLayout(this);
8435 },
8436
8437 buildOrUpdateControllers: function() {
8438 var me = this;
8439 var types = [];
8440 var newControllers = [];
8441
8442 helpers.each(me.data.datasets, function(dataset, datasetIndex) {
8443 var meta = me.getDatasetMeta(datasetIndex);
8444 var type = dataset.type || me.config.type;
8445
8446 if (meta.type && meta.type !== type) {
8447 me.destroyDatasetMeta(datasetIndex);
8448 meta = me.getDatasetMeta(datasetIndex);
8449 }
8450 meta.type = type;
8451
8452 types.push(meta.type);
8453
8454 if (meta.controller) {
8455 meta.controller.updateIndex(datasetIndex);
8456 } else {
8457 var ControllerClass = Chart.controllers[meta.type];
8458 if (ControllerClass === undefined) {
8459 throw new Error('"' + meta.type + '" is not a chart type.');
8460 }
8461
8462 meta.controller = new ControllerClass(me, datasetIndex);
8463 newControllers.push(meta.controller);
8464 }
8465 }, me);
8466
8467 return newControllers;
8468 },
8469
8470 /**
8471 * Reset the elements of all datasets
8472 * @private
8473 */
8474 resetElements: function() {
8475 var me = this;
8476 helpers.each(me.data.datasets, function(dataset, datasetIndex) {
8477 me.getDatasetMeta(datasetIndex).controller.reset();
8478 }, me);
8479 },
8480
8481 /**
8482 * Resets the chart back to it's state before the initial animation
8483 */
8484 reset: function() {
8485 this.resetElements();
8486 this.tooltip.initialize();
8487 },
8488
8489 update: function(config) {
8490 var me = this;
8491
8492 if (!config || typeof config !== 'object') {
8493 // backwards compatibility
8494 config = {
8495 duration: config,
8496 lazy: arguments[1]
8497 };
8498 }
8499
8500 updateConfig(me);
8501
8502 if (plugins.notify(me, 'beforeUpdate') === false) {
8503 return;
8504 }
8505
8506 // In case the entire data object changed
8507 me.tooltip._data = me.data;
8508
8509 // Make sure dataset controllers are updated and new controllers are reset
8510 var newControllers = me.buildOrUpdateControllers();
8511
8512 // Make sure all dataset controllers have correct meta data counts
8513 helpers.each(me.data.datasets, function(dataset, datasetIndex) {
8514 me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
8515 }, me);
8516
8517 me.updateLayout();
8518
8519 // Can only reset the new controllers after the scales have been updated
8520 helpers.each(newControllers, function(controller) {
8521 controller.reset();
8522 });
8523
8524 me.updateDatasets();
8525
8526 // Do this before render so that any plugins that need final scale updates can use it
8527 plugins.notify(me, 'afterUpdate');
8528
8529 if (me._bufferedRender) {
8530 me._bufferedRequest = {
8531 duration: config.duration,
8532 easing: config.easing,
8533 lazy: config.lazy
8534 };
8535 } else {
8536 me.render(config);
8537 }
8538 },
8539
8540 /**
8541 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
8542 * hook, in which case, plugins will not be called on `afterLayout`.
8543 * @private
8544 */
8545 updateLayout: function() {
8546 var me = this;
8547
8548 if (plugins.notify(me, 'beforeLayout') === false) {
8549 return;
8550 }
8551
8552 Chart.layoutService.update(this, this.width, this.height);
8553
8554 /**
8555 * Provided for backward compatibility, use `afterLayout` instead.
8556 * @method IPlugin#afterScaleUpdate
8557 * @deprecated since version 2.5.0
8558 * @todo remove at version 3
8559 * @private
8560 */
8561 plugins.notify(me, 'afterScaleUpdate');
8562 plugins.notify(me, 'afterLayout');
8563 },
8564
8565 /**
8566 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
8567 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
8568 * @private
8569 */
8570 updateDatasets: function() {
8571 var me = this;
8572
8573 if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
8574 return;
8575 }
8576
8577 for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
8578 me.updateDataset(i);
8579 }
8580
8581 plugins.notify(me, 'afterDatasetsUpdate');
8582 },
8583
8584 /**
8585 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
8586 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
8587 * @private
8588 */
8589 updateDataset: function(index) {
8590 var me = this;
8591 var meta = me.getDatasetMeta(index);
8592 var args = {
8593 meta: meta,
8594 index: index
8595 };
8596
8597 if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
8598 return;
8599 }
8600
8601 meta.controller.update();
8602
8603 plugins.notify(me, 'afterDatasetUpdate', [args]);
8604 },
8605
8606 render: function(config) {
8607 var me = this;
8608
8609 if (!config || typeof config !== 'object') {
8610 // backwards compatibility
8611 config = {
8612 duration: config,
8613 lazy: arguments[1]
8614 };
8615 }
8616
8617 var duration = config.duration;
8618 var lazy = config.lazy;
8619
8620 if (plugins.notify(me, 'beforeRender') === false) {
8621 return;
8622 }
8623
8624 var animationOptions = me.options.animation;
8625 var onComplete = function(animation) {
8626 plugins.notify(me, 'afterRender');
8627 helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
8628 };
8629
8630 if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
8631 var animation = new Chart.Animation({
8632 numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
8633 easing: config.easing || animationOptions.easing,
8634
8635 render: function(chart, animationObject) {
8636 var easingFunction = helpers.easing.effects[animationObject.easing];
8637 var currentStep = animationObject.currentStep;
8638 var stepDecimal = currentStep / animationObject.numSteps;
8639
8640 chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
8641 },
8642
8643 onAnimationProgress: animationOptions.onProgress,
8644 onAnimationComplete: onComplete
8645 });
8646
8647 Chart.animationService.addAnimation(me, animation, duration, lazy);
8648 } else {
8649 me.draw();
8650
8651 // See https://github.com/chartjs/Chart.js/issues/3781
8652 onComplete(new Chart.Animation({numSteps: 0, chart: me}));
8653 }
8654
8655 return me;
8656 },
8657
8658 draw: function(easingValue) {
8659 var me = this;
8660
8661 me.clear();
8662
8663 if (helpers.isNullOrUndef(easingValue)) {
8664 easingValue = 1;
8665 }
8666
8667 me.transition(easingValue);
8668
8669 if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
8670 return;
8671 }
8672
8673 // Draw all the scales
8674 helpers.each(me.boxes, function(box) {
8675 box.draw(me.chartArea);
8676 }, me);
8677
8678 if (me.scale) {
8679 me.scale.draw();
8680 }
8681
8682 me.drawDatasets(easingValue);
8683
8684 // Finally draw the tooltip
8685 me.tooltip.draw();
8686
8687 plugins.notify(me, 'afterDraw', [easingValue]);
8688 },
8689
8690 /**
8691 * @private
8692 */
8693 transition: function(easingValue) {
8694 var me = this;
8695
8696 for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
8697 if (me.isDatasetVisible(i)) {
8698 me.getDatasetMeta(i).controller.transition(easingValue);
8699 }
8700 }
8701
8702 me.tooltip.transition(easingValue);
8703 },
8704
8705 /**
8706 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
8707 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
8708 * @private
8709 */
8710 drawDatasets: function(easingValue) {
8711 var me = this;
8712
8713 if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
8714 return;
8715 }
8716
8717 // Draw datasets reversed to support proper line stacking
8718 for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
8719 if (me.isDatasetVisible(i)) {
8720 me.drawDataset(i, easingValue);
8721 }
8722 }
8723
8724 plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
8725 },
8726
8727 /**
8728 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
8729 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
8730 * @private
8731 */
8732 drawDataset: function(index, easingValue) {
8733 var me = this;
8734 var meta = me.getDatasetMeta(index);
8735 var args = {
8736 meta: meta,
8737 index: index,
8738 easingValue: easingValue
8739 };
8740
8741 if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
8742 return;
8743 }
8744
8745 meta.controller.draw(easingValue);
8746
8747 plugins.notify(me, 'afterDatasetDraw', [args]);
8748 },
8749
8750 // Get the single element that was clicked on
8751 // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
8752 getElementAtEvent: function(e) {
8753 return Interaction.modes.single(this, e);
8754 },
8755
8756 getElementsAtEvent: function(e) {
8757 return Interaction.modes.label(this, e, {intersect: true});
8758 },
8759
8760 getElementsAtXAxis: function(e) {
8761 return Interaction.modes['x-axis'](this, e, {intersect: true});
8762 },
8763
8764 getElementsAtEventForMode: function(e, mode, options) {
8765 var method = Interaction.modes[mode];
8766 if (typeof method === 'function') {
8767 return method(this, e, options);
8768 }
8769
8770 return [];
8771 },
8772
8773 getDatasetAtEvent: function(e) {
8774 return Interaction.modes.dataset(this, e, {intersect: true});
8775 },
8776
8777 getDatasetMeta: function(datasetIndex) {
8778 var me = this;
8779 var dataset = me.data.datasets[datasetIndex];
8780 if (!dataset._meta) {
8781 dataset._meta = {};
8782 }
8783
8784 var meta = dataset._meta[me.id];
8785 if (!meta) {
8786 meta = dataset._meta[me.id] = {
8787 type: null,
8788 data: [],
8789 dataset: null,
8790 controller: null,
8791 hidden: null, // See isDatasetVisible() comment
8792 xAxisID: null,
8793 yAxisID: null
8794 };
8795 }
8796
8797 return meta;
8798 },
8799
8800 getVisibleDatasetCount: function() {
8801 var count = 0;
8802 for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
8803 if (this.isDatasetVisible(i)) {
8804 count++;
8805 }
8806 }
8807 return count;
8808 },
8809
8810 isDatasetVisible: function(datasetIndex) {
8811 var meta = this.getDatasetMeta(datasetIndex);
8812
8813 // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
8814 // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
8815 return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
8816 },
8817
8818 generateLegend: function() {
8819 return this.options.legendCallback(this);
8820 },
8821
8822 /**
8823 * @private
8824 */
8825 destroyDatasetMeta: function(datasetIndex) {
8826 var id = this.id;
8827 var dataset = this.data.datasets[datasetIndex];
8828 var meta = dataset._meta && dataset._meta[id];
8829
8830 if (meta) {
8831 meta.controller.destroy();
8832 delete dataset._meta[id];
8833 }
8834 },
8835
8836 destroy: function() {
8837 var me = this;
8838 var canvas = me.canvas;
8839 var i, ilen;
8840
8841 me.stop();
8842
8843 // dataset controllers need to cleanup associated data
8844 for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
8845 me.destroyDatasetMeta(i);
8846 }
8847
8848 if (canvas) {
8849 me.unbindEvents();
8850 helpers.canvas.clear(me);
8851 platform.releaseContext(me.ctx);
8852 me.canvas = null;
8853 me.ctx = null;
8854 }
8855
8856 plugins.notify(me, 'destroy');
8857
8858 delete Chart.instances[me.id];
8859 },
8860
8861 toBase64Image: function() {
8862 return this.canvas.toDataURL.apply(this.canvas, arguments);
8863 },
8864
8865 initToolTip: function() {
8866 var me = this;
8867 me.tooltip = new Chart.Tooltip({
8868 _chart: me,
8869 _chartInstance: me, // deprecated, backward compatibility
8870 _data: me.data,
8871 _options: me.options.tooltips
8872 }, me);
8873 },
8874
8875 /**
8876 * @private
8877 */
8878 bindEvents: function() {
8879 var me = this;
8880 var listeners = me._listeners = {};
8881 var listener = function() {
8882 me.eventHandler.apply(me, arguments);
8883 };
8884
8885 helpers.each(me.options.events, function(type) {
8886 platform.addEventListener(me, type, listener);
8887 listeners[type] = listener;
8888 });
8889
8890 // Elements used to detect size change should not be injected for non responsive charts.
8891 // See https://github.com/chartjs/Chart.js/issues/2210
8892 if (me.options.responsive) {
8893 listener = function() {
8894 me.resize();
8895 };
8896
8897 platform.addEventListener(me, 'resize', listener);
8898 listeners.resize = listener;
8899 }
8900 },
8901
8902 /**
8903 * @private
8904 */
8905 unbindEvents: function() {
8906 var me = this;
8907 var listeners = me._listeners;
8908 if (!listeners) {
8909 return;
8910 }
8911
8912 delete me._listeners;
8913 helpers.each(listeners, function(listener, type) {
8914 platform.removeEventListener(me, type, listener);
8915 });
8916 },
8917
8918 updateHoverStyle: function(elements, mode, enabled) {
8919 var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
8920 var element, i, ilen;
8921
8922 for (i = 0, ilen = elements.length; i < ilen; ++i) {
8923 element = elements[i];
8924 if (element) {
8925 this.getDatasetMeta(element._datasetIndex).controller[method](element);
8926 }
8927 }
8928 },
8929
8930 /**
8931 * @private
8932 */
8933 eventHandler: function(e) {
8934 var me = this;
8935 var tooltip = me.tooltip;
8936
8937 if (plugins.notify(me, 'beforeEvent', [e]) === false) {
8938 return;
8939 }
8940
8941 // Buffer any update calls so that renders do not occur
8942 me._bufferedRender = true;
8943 me._bufferedRequest = null;
8944
8945 var changed = me.handleEvent(e);
8946 changed |= tooltip && tooltip.handleEvent(e);
8947
8948 plugins.notify(me, 'afterEvent', [e]);
8949
8950 var bufferedRequest = me._bufferedRequest;
8951 if (bufferedRequest) {
8952 // If we have an update that was triggered, we need to do a normal render
8953 me.render(bufferedRequest);
8954 } else if (changed && !me.animating) {
8955 // If entering, leaving, or changing elements, animate the change via pivot
8956 me.stop();
8957
8958 // We only need to render at this point. Updating will cause scales to be
8959 // recomputed generating flicker & using more memory than necessary.
8960 me.render(me.options.hover.animationDuration, true);
8961 }
8962
8963 me._bufferedRender = false;
8964 me._bufferedRequest = null;
8965
8966 return me;
8967 },
8968
8969 /**
8970 * Handle an event
8971 * @private
8972 * @param {IEvent} event the event to handle
8973 * @return {Boolean} true if the chart needs to re-render
8974 */
8975 handleEvent: function(e) {
8976 var me = this;
8977 var options = me.options || {};
8978 var hoverOptions = options.hover;
8979 var changed = false;
8980
8981 me.lastActive = me.lastActive || [];
8982
8983 // Find Active Elements for hover and tooltips
8984 if (e.type === 'mouseout') {
8985 me.active = [];
8986 } else {
8987 me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
8988 }
8989
8990 // Invoke onHover hook
8991 // Need to call with native event here to not break backwards compatibility
8992 helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
8993
8994 if (e.type === 'mouseup' || e.type === 'click') {
8995 if (options.onClick) {
8996 // Use e.native here for backwards compatibility
8997 options.onClick.call(me, e.native, me.active);
8998 }
8999 }
9000
9001 // Remove styling for last active (even if it may still be active)
9002 if (me.lastActive.length) {
9003 me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
9004 }
9005
9006 // Built in hover styling
9007 if (me.active.length && hoverOptions.mode) {
9008 me.updateHoverStyle(me.active, hoverOptions.mode, true);
9009 }
9010
9011 changed = !helpers.arrayEquals(me.active, me.lastActive);
9012
9013 // Remember Last Actives
9014 me.lastActive = me.active;
9015
9016 return changed;
9017 }
9018 });
9019
9020 /**
9021 * Provided for backward compatibility, use Chart instead.
9022 * @class Chart.Controller
9023 * @deprecated since version 2.6.0
9024 * @todo remove at version 3
9025 * @private
9026 */
9027 Chart.Controller = Chart;
9028 };
9029
9030 },{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){
9031 'use strict';
9032
9033 var helpers = require(45);
9034
9035 module.exports = function(Chart) {
9036
9037 var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
9038
9039 /**
9040 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
9041 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
9042 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
9043 */
9044 function listenArrayEvents(array, listener) {
9045 if (array._chartjs) {
9046 array._chartjs.listeners.push(listener);
9047 return;
9048 }
9049
9050 Object.defineProperty(array, '_chartjs', {
9051 configurable: true,
9052 enumerable: false,
9053 value: {
9054 listeners: [listener]
9055 }
9056 });
9057
9058 arrayEvents.forEach(function(key) {
9059 var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
9060 var base = array[key];
9061
9062 Object.defineProperty(array, key, {
9063 configurable: true,
9064 enumerable: false,
9065 value: function() {
9066 var args = Array.prototype.slice.call(arguments);
9067 var res = base.apply(this, args);
9068
9069 helpers.each(array._chartjs.listeners, function(object) {
9070 if (typeof object[method] === 'function') {
9071 object[method].apply(object, args);
9072 }
9073 });
9074
9075 return res;
9076 }
9077 });
9078 });
9079 }
9080
9081 /**
9082 * Removes the given array event listener and cleanup extra attached properties (such as
9083 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
9084 */
9085 function unlistenArrayEvents(array, listener) {
9086 var stub = array._chartjs;
9087 if (!stub) {
9088 return;
9089 }
9090
9091 var listeners = stub.listeners;
9092 var index = listeners.indexOf(listener);
9093 if (index !== -1) {
9094 listeners.splice(index, 1);
9095 }
9096
9097 if (listeners.length > 0) {
9098 return;
9099 }
9100
9101 arrayEvents.forEach(function(key) {
9102 delete array[key];
9103 });
9104
9105 delete array._chartjs;
9106 }
9107
9108 // Base class for all dataset controllers (line, bar, etc)
9109 Chart.DatasetController = function(chart, datasetIndex) {
9110 this.initialize(chart, datasetIndex);
9111 };
9112
9113 helpers.extend(Chart.DatasetController.prototype, {
9114
9115 /**
9116 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
9117 * @type {Chart.core.element}
9118 */
9119 datasetElementType: null,
9120
9121 /**
9122 * Element type used to generate a meta data (e.g. Chart.element.Point).
9123 * @type {Chart.core.element}
9124 */
9125 dataElementType: null,
9126
9127 initialize: function(chart, datasetIndex) {
9128 var me = this;
9129 me.chart = chart;
9130 me.index = datasetIndex;
9131 me.linkScales();
9132 me.addElements();
9133 },
9134
9135 updateIndex: function(datasetIndex) {
9136 this.index = datasetIndex;
9137 },
9138
9139 linkScales: function() {
9140 var me = this;
9141 var meta = me.getMeta();
9142 var dataset = me.getDataset();
9143
9144 if (meta.xAxisID === null) {
9145 meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
9146 }
9147 if (meta.yAxisID === null) {
9148 meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
9149 }
9150 },
9151
9152 getDataset: function() {
9153 return this.chart.data.datasets[this.index];
9154 },
9155
9156 getMeta: function() {
9157 return this.chart.getDatasetMeta(this.index);
9158 },
9159
9160 getScaleForId: function(scaleID) {
9161 return this.chart.scales[scaleID];
9162 },
9163
9164 reset: function() {
9165 this.update(true);
9166 },
9167
9168 /**
9169 * @private
9170 */
9171 destroy: function() {
9172 if (this._data) {
9173 unlistenArrayEvents(this._data, this);
9174 }
9175 },
9176
9177 createMetaDataset: function() {
9178 var me = this;
9179 var type = me.datasetElementType;
9180 return type && new type({
9181 _chart: me.chart,
9182 _datasetIndex: me.index
9183 });
9184 },
9185
9186 createMetaData: function(index) {
9187 var me = this;
9188 var type = me.dataElementType;
9189 return type && new type({
9190 _chart: me.chart,
9191 _datasetIndex: me.index,
9192 _index: index
9193 });
9194 },
9195
9196 addElements: function() {
9197 var me = this;
9198 var meta = me.getMeta();
9199 var data = me.getDataset().data || [];
9200 var metaData = meta.data;
9201 var i, ilen;
9202
9203 for (i = 0, ilen = data.length; i < ilen; ++i) {
9204 metaData[i] = metaData[i] || me.createMetaData(i);
9205 }
9206
9207 meta.dataset = meta.dataset || me.createMetaDataset();
9208 },
9209
9210 addElementAndReset: function(index) {
9211 var element = this.createMetaData(index);
9212 this.getMeta().data.splice(index, 0, element);
9213 this.updateElement(element, index, true);
9214 },
9215
9216 buildOrUpdateElements: function() {
9217 var me = this;
9218 var dataset = me.getDataset();
9219 var data = dataset.data || (dataset.data = []);
9220
9221 // In order to correctly handle data addition/deletion animation (an thus simulate
9222 // real-time charts), we need to monitor these data modifications and synchronize
9223 // the internal meta data accordingly.
9224 if (me._data !== data) {
9225 if (me._data) {
9226 // This case happens when the user replaced the data array instance.
9227 unlistenArrayEvents(me._data, me);
9228 }
9229
9230 listenArrayEvents(data, me);
9231 me._data = data;
9232 }
9233
9234 // Re-sync meta data in case the user replaced the data array or if we missed
9235 // any updates and so make sure that we handle number of datapoints changing.
9236 me.resyncElements();
9237 },
9238
9239 update: helpers.noop,
9240
9241 transition: function(easingValue) {
9242 var meta = this.getMeta();
9243 var elements = meta.data || [];
9244 var ilen = elements.length;
9245 var i = 0;
9246
9247 for (; i < ilen; ++i) {
9248 elements[i].transition(easingValue);
9249 }
9250
9251 if (meta.dataset) {
9252 meta.dataset.transition(easingValue);
9253 }
9254 },
9255
9256 draw: function() {
9257 var meta = this.getMeta();
9258 var elements = meta.data || [];
9259 var ilen = elements.length;
9260 var i = 0;
9261
9262 if (meta.dataset) {
9263 meta.dataset.draw();
9264 }
9265
9266 for (; i < ilen; ++i) {
9267 elements[i].draw();
9268 }
9269 },
9270
9271 removeHoverStyle: function(element, elementOpts) {
9272 var dataset = this.chart.data.datasets[element._datasetIndex];
9273 var index = element._index;
9274 var custom = element.custom || {};
9275 var valueOrDefault = helpers.valueAtIndexOrDefault;
9276 var model = element._model;
9277
9278 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
9279 model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
9280 model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
9281 },
9282
9283 setHoverStyle: function(element) {
9284 var dataset = this.chart.data.datasets[element._datasetIndex];
9285 var index = element._index;
9286 var custom = element.custom || {};
9287 var valueOrDefault = helpers.valueAtIndexOrDefault;
9288 var getHoverColor = helpers.getHoverColor;
9289 var model = element._model;
9290
9291 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
9292 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
9293 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
9294 },
9295
9296 /**
9297 * @private
9298 */
9299 resyncElements: function() {
9300 var me = this;
9301 var meta = me.getMeta();
9302 var data = me.getDataset().data;
9303 var numMeta = meta.data.length;
9304 var numData = data.length;
9305
9306 if (numData < numMeta) {
9307 meta.data.splice(numData, numMeta - numData);
9308 } else if (numData > numMeta) {
9309 me.insertElements(numMeta, numData - numMeta);
9310 }
9311 },
9312
9313 /**
9314 * @private
9315 */
9316 insertElements: function(start, count) {
9317 for (var i = 0; i < count; ++i) {
9318 this.addElementAndReset(start + i);
9319 }
9320 },
9321
9322 /**
9323 * @private
9324 */
9325 onDataPush: function() {
9326 this.insertElements(this.getDataset().data.length - 1, arguments.length);
9327 },
9328
9329 /**
9330 * @private
9331 */
9332 onDataPop: function() {
9333 this.getMeta().data.pop();
9334 },
9335
9336 /**
9337 * @private
9338 */
9339 onDataShift: function() {
9340 this.getMeta().data.shift();
9341 },
9342
9343 /**
9344 * @private
9345 */
9346 onDataSplice: function(start, count) {
9347 this.getMeta().data.splice(start, count);
9348 this.insertElements(start, arguments.length - 2);
9349 },
9350
9351 /**
9352 * @private
9353 */
9354 onDataUnshift: function() {
9355 this.insertElements(0, arguments.length);
9356 }
9357 });
9358
9359 Chart.DatasetController.extend = helpers.inherits;
9360 };
9361
9362 },{"45":45}],25:[function(require,module,exports){
9363 'use strict';
9364
9365 var helpers = require(45);
9366
9367 module.exports = {
9368 /**
9369 * @private
9370 */
9371 _set: function(scope, values) {
9372 return helpers.merge(this[scope] || (this[scope] = {}), values);
9373 }
9374 };
9375
9376 },{"45":45}],26:[function(require,module,exports){
9377 'use strict';
9378
9379 var color = require(2);
9380 var helpers = require(45);
9381
9382 function interpolate(start, view, model, ease) {
9383 var keys = Object.keys(model);
9384 var i, ilen, key, actual, origin, target, type, c0, c1;
9385
9386 for (i = 0, ilen = keys.length; i < ilen; ++i) {
9387 key = keys[i];
9388
9389 target = model[key];
9390
9391 // if a value is added to the model after pivot() has been called, the view
9392 // doesn't contain it, so let's initialize the view to the target value.
9393 if (!view.hasOwnProperty(key)) {
9394 view[key] = target;
9395 }
9396
9397 actual = view[key];
9398
9399 if (actual === target || key[0] === '_') {
9400 continue;
9401 }
9402
9403 if (!start.hasOwnProperty(key)) {
9404 start[key] = actual;
9405 }
9406
9407 origin = start[key];
9408
9409 type = typeof target;
9410
9411 if (type === typeof origin) {
9412 if (type === 'string') {
9413 c0 = color(origin);
9414 if (c0.valid) {
9415 c1 = color(target);
9416 if (c1.valid) {
9417 view[key] = c1.mix(c0, ease).rgbString();
9418 continue;
9419 }
9420 }
9421 } else if (type === 'number' && isFinite(origin) && isFinite(target)) {
9422 view[key] = origin + (target - origin) * ease;
9423 continue;
9424 }
9425 }
9426
9427 view[key] = target;
9428 }
9429 }
9430
9431 var Element = function(configuration) {
9432 helpers.extend(this, configuration);
9433 this.initialize.apply(this, arguments);
9434 };
9435
9436 helpers.extend(Element.prototype, {
9437
9438 initialize: function() {
9439 this.hidden = false;
9440 },
9441
9442 pivot: function() {
9443 var me = this;
9444 if (!me._view) {
9445 me._view = helpers.clone(me._model);
9446 }
9447 me._start = {};
9448 return me;
9449 },
9450
9451 transition: function(ease) {
9452 var me = this;
9453 var model = me._model;
9454 var start = me._start;
9455 var view = me._view;
9456
9457 // No animation -> No Transition
9458 if (!model || ease === 1) {
9459 me._view = model;
9460 me._start = null;
9461 return me;
9462 }
9463
9464 if (!view) {
9465 view = me._view = {};
9466 }
9467
9468 if (!start) {
9469 start = me._start = {};
9470 }
9471
9472 interpolate(start, view, model, ease);
9473
9474 return me;
9475 },
9476
9477 tooltipPosition: function() {
9478 return {
9479 x: this._model.x,
9480 y: this._model.y
9481 };
9482 },
9483
9484 hasValue: function() {
9485 return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
9486 }
9487 });
9488
9489 Element.extend = helpers.inherits;
9490
9491 module.exports = Element;
9492
9493 },{"2":2,"45":45}],27:[function(require,module,exports){
9494 /* global window: false */
9495 /* global document: false */
9496 'use strict';
9497
9498 var color = require(2);
9499 var defaults = require(25);
9500 var helpers = require(45);
9501
9502 module.exports = function(Chart) {
9503
9504 // -- Basic js utility methods
9505
9506 helpers.extend = function(base) {
9507 var setFn = function(value, key) {
9508 base[key] = value;
9509 };
9510 for (var i = 1, ilen = arguments.length; i < ilen; i++) {
9511 helpers.each(arguments[i], setFn);
9512 }
9513 return base;
9514 };
9515
9516 helpers.configMerge = function(/* objects ... */) {
9517 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
9518 merger: function(key, target, source, options) {
9519 var tval = target[key] || {};
9520 var sval = source[key];
9521
9522 if (key === 'scales') {
9523 // scale config merging is complex. Add our own function here for that
9524 target[key] = helpers.scaleMerge(tval, sval);
9525 } else if (key === 'scale') {
9526 // used in polar area & radar charts since there is only one scale
9527 target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]);
9528 } else {
9529 helpers._merger(key, target, source, options);
9530 }
9531 }
9532 });
9533 };
9534
9535 helpers.scaleMerge = function(/* objects ... */) {
9536 return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
9537 merger: function(key, target, source, options) {
9538 if (key === 'xAxes' || key === 'yAxes') {
9539 var slen = source[key].length;
9540 var i, type, scale;
9541
9542 if (!target[key]) {
9543 target[key] = [];
9544 }
9545
9546 for (i = 0; i < slen; ++i) {
9547 scale = source[key][i];
9548 type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');
9549
9550 if (i >= target[key].length) {
9551 target[key].push({});
9552 }
9553
9554 if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
9555 // new/untyped scale or type changed: let's apply the new defaults
9556 // then merge source scale to correctly overwrite the defaults.
9557 helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]);
9558 } else {
9559 // scales type are the same
9560 helpers.merge(target[key][i], scale);
9561 }
9562 }
9563 } else {
9564 helpers._merger(key, target, source, options);
9565 }
9566 }
9567 });
9568 };
9569
9570 helpers.where = function(collection, filterCallback) {
9571 if (helpers.isArray(collection) && Array.prototype.filter) {
9572 return collection.filter(filterCallback);
9573 }
9574 var filtered = [];
9575
9576 helpers.each(collection, function(item) {
9577 if (filterCallback(item)) {
9578 filtered.push(item);
9579 }
9580 });
9581
9582 return filtered;
9583 };
9584 helpers.findIndex = Array.prototype.findIndex ?
9585 function(array, callback, scope) {
9586 return array.findIndex(callback, scope);
9587 } :
9588 function(array, callback, scope) {
9589 scope = scope === undefined ? array : scope;
9590 for (var i = 0, ilen = array.length; i < ilen; ++i) {
9591 if (callback.call(scope, array[i], i, array)) {
9592 return i;
9593 }
9594 }
9595 return -1;
9596 };
9597 helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
9598 // Default to start of the array
9599 if (helpers.isNullOrUndef(startIndex)) {
9600 startIndex = -1;
9601 }
9602 for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
9603 var currentItem = arrayToSearch[i];
9604 if (filterCallback(currentItem)) {
9605 return currentItem;
9606 }
9607 }
9608 };
9609 helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
9610 // Default to end of the array
9611 if (helpers.isNullOrUndef(startIndex)) {
9612 startIndex = arrayToSearch.length;
9613 }
9614 for (var i = startIndex - 1; i >= 0; i--) {
9615 var currentItem = arrayToSearch[i];
9616 if (filterCallback(currentItem)) {
9617 return currentItem;
9618 }
9619 }
9620 };
9621 helpers.inherits = function(extensions) {
9622 // Basic javascript inheritance based on the model created in Backbone.js
9623 var me = this;
9624 var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
9625 return me.apply(this, arguments);
9626 };
9627
9628 var Surrogate = function() {
9629 this.constructor = ChartElement;
9630 };
9631 Surrogate.prototype = me.prototype;
9632 ChartElement.prototype = new Surrogate();
9633
9634 ChartElement.extend = helpers.inherits;
9635
9636 if (extensions) {
9637 helpers.extend(ChartElement.prototype, extensions);
9638 }
9639
9640 ChartElement.__super__ = me.prototype;
9641
9642 return ChartElement;
9643 };
9644 // -- Math methods
9645 helpers.isNumber = function(n) {
9646 return !isNaN(parseFloat(n)) && isFinite(n);
9647 };
9648 helpers.almostEquals = function(x, y, epsilon) {
9649 return Math.abs(x - y) < epsilon;
9650 };
9651 helpers.almostWhole = function(x, epsilon) {
9652 var rounded = Math.round(x);
9653 return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
9654 };
9655 helpers.max = function(array) {
9656 return array.reduce(function(max, value) {
9657 if (!isNaN(value)) {
9658 return Math.max(max, value);
9659 }
9660 return max;
9661 }, Number.NEGATIVE_INFINITY);
9662 };
9663 helpers.min = function(array) {
9664 return array.reduce(function(min, value) {
9665 if (!isNaN(value)) {
9666 return Math.min(min, value);
9667 }
9668 return min;
9669 }, Number.POSITIVE_INFINITY);
9670 };
9671 helpers.sign = Math.sign ?
9672 function(x) {
9673 return Math.sign(x);
9674 } :
9675 function(x) {
9676 x = +x; // convert to a number
9677 if (x === 0 || isNaN(x)) {
9678 return x;
9679 }
9680 return x > 0 ? 1 : -1;
9681 };
9682 helpers.log10 = Math.log10 ?
9683 function(x) {
9684 return Math.log10(x);
9685 } :
9686 function(x) {
9687 return Math.log(x) / Math.LN10;
9688 };
9689 helpers.toRadians = function(degrees) {
9690 return degrees * (Math.PI / 180);
9691 };
9692 helpers.toDegrees = function(radians) {
9693 return radians * (180 / Math.PI);
9694 };
9695 // Gets the angle from vertical upright to the point about a centre.
9696 helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
9697 var distanceFromXCenter = anglePoint.x - centrePoint.x;
9698 var distanceFromYCenter = anglePoint.y - centrePoint.y;
9699 var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
9700
9701 var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
9702
9703 if (angle < (-0.5 * Math.PI)) {
9704 angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
9705 }
9706
9707 return {
9708 angle: angle,
9709 distance: radialDistanceFromCenter
9710 };
9711 };
9712 helpers.distanceBetweenPoints = function(pt1, pt2) {
9713 return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
9714 };
9715 helpers.aliasPixel = function(pixelWidth) {
9716 return (pixelWidth % 2 === 0) ? 0 : 0.5;
9717 };
9718 helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
9719 // Props to Rob Spencer at scaled innovation for his post on splining between points
9720 // http://scaledinnovation.com/analytics/splines/aboutSplines.html
9721
9722 // This function must also respect "skipped" points
9723
9724 var previous = firstPoint.skip ? middlePoint : firstPoint;
9725 var current = middlePoint;
9726 var next = afterPoint.skip ? middlePoint : afterPoint;
9727
9728 var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
9729 var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
9730
9731 var s01 = d01 / (d01 + d12);
9732 var s12 = d12 / (d01 + d12);
9733
9734 // If all points are the same, s01 & s02 will be inf
9735 s01 = isNaN(s01) ? 0 : s01;
9736 s12 = isNaN(s12) ? 0 : s12;
9737
9738 var fa = t * s01; // scaling factor for triangle Ta
9739 var fb = t * s12;
9740
9741 return {
9742 previous: {
9743 x: current.x - fa * (next.x - previous.x),
9744 y: current.y - fa * (next.y - previous.y)
9745 },
9746 next: {
9747 x: current.x + fb * (next.x - previous.x),
9748 y: current.y + fb * (next.y - previous.y)
9749 }
9750 };
9751 };
9752 helpers.EPSILON = Number.EPSILON || 1e-14;
9753 helpers.splineCurveMonotone = function(points) {
9754 // This function calculates Bézier control points in a similar way than |splineCurve|,
9755 // but preserves monotonicity of the provided data and ensures no local extremums are added
9756 // between the dataset discrete points due to the interpolation.
9757 // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
9758
9759 var pointsWithTangents = (points || []).map(function(point) {
9760 return {
9761 model: point._model,
9762 deltaK: 0,
9763 mK: 0
9764 };
9765 });
9766
9767 // Calculate slopes (deltaK) and initialize tangents (mK)
9768 var pointsLen = pointsWithTangents.length;
9769 var i, pointBefore, pointCurrent, pointAfter;
9770 for (i = 0; i < pointsLen; ++i) {
9771 pointCurrent = pointsWithTangents[i];
9772 if (pointCurrent.model.skip) {
9773 continue;
9774 }
9775
9776 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
9777 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
9778 if (pointAfter && !pointAfter.model.skip) {
9779 var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
9780
9781 // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
9782 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
9783 }
9784
9785 if (!pointBefore || pointBefore.model.skip) {
9786 pointCurrent.mK = pointCurrent.deltaK;
9787 } else if (!pointAfter || pointAfter.model.skip) {
9788 pointCurrent.mK = pointBefore.deltaK;
9789 } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
9790 pointCurrent.mK = 0;
9791 } else {
9792 pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
9793 }
9794 }
9795
9796 // Adjust tangents to ensure monotonic properties
9797 var alphaK, betaK, tauK, squaredMagnitude;
9798 for (i = 0; i < pointsLen - 1; ++i) {
9799 pointCurrent = pointsWithTangents[i];
9800 pointAfter = pointsWithTangents[i + 1];
9801 if (pointCurrent.model.skip || pointAfter.model.skip) {
9802 continue;
9803 }
9804
9805 if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
9806 pointCurrent.mK = pointAfter.mK = 0;
9807 continue;
9808 }
9809
9810 alphaK = pointCurrent.mK / pointCurrent.deltaK;
9811 betaK = pointAfter.mK / pointCurrent.deltaK;
9812 squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
9813 if (squaredMagnitude <= 9) {
9814 continue;
9815 }
9816
9817 tauK = 3 / Math.sqrt(squaredMagnitude);
9818 pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
9819 pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
9820 }
9821
9822 // Compute control points
9823 var deltaX;
9824 for (i = 0; i < pointsLen; ++i) {
9825 pointCurrent = pointsWithTangents[i];
9826 if (pointCurrent.model.skip) {
9827 continue;
9828 }
9829
9830 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
9831 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
9832 if (pointBefore && !pointBefore.model.skip) {
9833 deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
9834 pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
9835 pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
9836 }
9837 if (pointAfter && !pointAfter.model.skip) {
9838 deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
9839 pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
9840 pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
9841 }
9842 }
9843 };
9844 helpers.nextItem = function(collection, index, loop) {
9845 if (loop) {
9846 return index >= collection.length - 1 ? collection[0] : collection[index + 1];
9847 }
9848 return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
9849 };
9850 helpers.previousItem = function(collection, index, loop) {
9851 if (loop) {
9852 return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
9853 }
9854 return index <= 0 ? collection[0] : collection[index - 1];
9855 };
9856 // Implementation of the nice number algorithm used in determining where axis labels will go
9857 helpers.niceNum = function(range, round) {
9858 var exponent = Math.floor(helpers.log10(range));
9859 var fraction = range / Math.pow(10, exponent);
9860 var niceFraction;
9861
9862 if (round) {
9863 if (fraction < 1.5) {
9864 niceFraction = 1;
9865 } else if (fraction < 3) {
9866 niceFraction = 2;
9867 } else if (fraction < 7) {
9868 niceFraction = 5;
9869 } else {
9870 niceFraction = 10;
9871 }
9872 } else if (fraction <= 1.0) {
9873 niceFraction = 1;
9874 } else if (fraction <= 2) {
9875 niceFraction = 2;
9876 } else if (fraction <= 5) {
9877 niceFraction = 5;
9878 } else {
9879 niceFraction = 10;
9880 }
9881
9882 return niceFraction * Math.pow(10, exponent);
9883 };
9884 // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
9885 helpers.requestAnimFrame = (function() {
9886 if (typeof window === 'undefined') {
9887 return function(callback) {
9888 callback();
9889 };
9890 }
9891 return window.requestAnimationFrame ||
9892 window.webkitRequestAnimationFrame ||
9893 window.mozRequestAnimationFrame ||
9894 window.oRequestAnimationFrame ||
9895 window.msRequestAnimationFrame ||
9896 function(callback) {
9897 return window.setTimeout(callback, 1000 / 60);
9898 };
9899 }());
9900 // -- DOM methods
9901 helpers.getRelativePosition = function(evt, chart) {
9902 var mouseX, mouseY;
9903 var e = evt.originalEvent || evt;
9904 var canvas = evt.currentTarget || evt.srcElement;
9905 var boundingRect = canvas.getBoundingClientRect();
9906
9907 var touches = e.touches;
9908 if (touches && touches.length > 0) {
9909 mouseX = touches[0].clientX;
9910 mouseY = touches[0].clientY;
9911
9912 } else {
9913 mouseX = e.clientX;
9914 mouseY = e.clientY;
9915 }
9916
9917 // Scale mouse coordinates into canvas coordinates
9918 // by following the pattern laid out by 'jerryj' in the comments of
9919 // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
9920 var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
9921 var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
9922 var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
9923 var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
9924 var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
9925 var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
9926
9927 // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
9928 // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
9929 mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
9930 mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
9931
9932 return {
9933 x: mouseX,
9934 y: mouseY
9935 };
9936
9937 };
9938
9939 // Private helper function to convert max-width/max-height values that may be percentages into a number
9940 function parseMaxStyle(styleValue, node, parentProperty) {
9941 var valueInPixels;
9942 if (typeof styleValue === 'string') {
9943 valueInPixels = parseInt(styleValue, 10);
9944
9945 if (styleValue.indexOf('%') !== -1) {
9946 // percentage * size in dimension
9947 valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
9948 }
9949 } else {
9950 valueInPixels = styleValue;
9951 }
9952
9953 return valueInPixels;
9954 }
9955
9956 /**
9957 * Returns if the given value contains an effective constraint.
9958 * @private
9959 */
9960 function isConstrainedValue(value) {
9961 return value !== undefined && value !== null && value !== 'none';
9962 }
9963
9964 // Private helper to get a constraint dimension
9965 // @param domNode : the node to check the constraint on
9966 // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
9967 // @param percentageProperty : property of parent to use when calculating width as a percentage
9968 // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
9969 function getConstraintDimension(domNode, maxStyle, percentageProperty) {
9970 var view = document.defaultView;
9971 var parentNode = domNode.parentNode;
9972 var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
9973 var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
9974 var hasCNode = isConstrainedValue(constrainedNode);
9975 var hasCContainer = isConstrainedValue(constrainedContainer);
9976 var infinity = Number.POSITIVE_INFINITY;
9977
9978 if (hasCNode || hasCContainer) {
9979 return Math.min(
9980 hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
9981 hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
9982 }
9983
9984 return 'none';
9985 }
9986 // returns Number or undefined if no constraint
9987 helpers.getConstraintWidth = function(domNode) {
9988 return getConstraintDimension(domNode, 'max-width', 'clientWidth');
9989 };
9990 // returns Number or undefined if no constraint
9991 helpers.getConstraintHeight = function(domNode) {
9992 return getConstraintDimension(domNode, 'max-height', 'clientHeight');
9993 };
9994 helpers.getMaximumWidth = function(domNode) {
9995 var container = domNode.parentNode;
9996 if (!container) {
9997 return domNode.clientWidth;
9998 }
9999
10000 var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
10001 var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
10002 var w = container.clientWidth - paddingLeft - paddingRight;
10003 var cw = helpers.getConstraintWidth(domNode);
10004 return isNaN(cw) ? w : Math.min(w, cw);
10005 };
10006 helpers.getMaximumHeight = function(domNode) {
10007 var container = domNode.parentNode;
10008 if (!container) {
10009 return domNode.clientHeight;
10010 }
10011
10012 var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
10013 var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
10014 var h = container.clientHeight - paddingTop - paddingBottom;
10015 var ch = helpers.getConstraintHeight(domNode);
10016 return isNaN(ch) ? h : Math.min(h, ch);
10017 };
10018 helpers.getStyle = function(el, property) {
10019 return el.currentStyle ?
10020 el.currentStyle[property] :
10021 document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
10022 };
10023 helpers.retinaScale = function(chart, forceRatio) {
10024 var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1;
10025 if (pixelRatio === 1) {
10026 return;
10027 }
10028
10029 var canvas = chart.canvas;
10030 var height = chart.height;
10031 var width = chart.width;
10032
10033 canvas.height = height * pixelRatio;
10034 canvas.width = width * pixelRatio;
10035 chart.ctx.scale(pixelRatio, pixelRatio);
10036
10037 // If no style has been set on the canvas, the render size is used as display size,
10038 // making the chart visually bigger, so let's enforce it to the "correct" values.
10039 // See https://github.com/chartjs/Chart.js/issues/3575
10040 canvas.style.height = height + 'px';
10041 canvas.style.width = width + 'px';
10042 };
10043 // -- Canvas methods
10044 helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
10045 return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
10046 };
10047 helpers.longestText = function(ctx, font, arrayOfThings, cache) {
10048 cache = cache || {};
10049 var data = cache.data = cache.data || {};
10050 var gc = cache.garbageCollect = cache.garbageCollect || [];
10051
10052 if (cache.font !== font) {
10053 data = cache.data = {};
10054 gc = cache.garbageCollect = [];
10055 cache.font = font;
10056 }
10057
10058 ctx.font = font;
10059 var longest = 0;
10060 helpers.each(arrayOfThings, function(thing) {
10061 // Undefined strings and arrays should not be measured
10062 if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
10063 longest = helpers.measureText(ctx, data, gc, longest, thing);
10064 } else if (helpers.isArray(thing)) {
10065 // if it is an array lets measure each element
10066 // to do maybe simplify this function a bit so we can do this more recursively?
10067 helpers.each(thing, function(nestedThing) {
10068 // Undefined strings and arrays should not be measured
10069 if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
10070 longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
10071 }
10072 });
10073 }
10074 });
10075
10076 var gcLen = gc.length / 2;
10077 if (gcLen > arrayOfThings.length) {
10078 for (var i = 0; i < gcLen; i++) {
10079 delete data[gc[i]];
10080 }
10081 gc.splice(0, gcLen);
10082 }
10083 return longest;
10084 };
10085 helpers.measureText = function(ctx, data, gc, longest, string) {
10086 var textWidth = data[string];
10087 if (!textWidth) {
10088 textWidth = data[string] = ctx.measureText(string).width;
10089 gc.push(string);
10090 }
10091 if (textWidth > longest) {
10092 longest = textWidth;
10093 }
10094 return longest;
10095 };
10096 helpers.numberOfLabelLines = function(arrayOfThings) {
10097 var numberOfLines = 1;
10098 helpers.each(arrayOfThings, function(thing) {
10099 if (helpers.isArray(thing)) {
10100 if (thing.length > numberOfLines) {
10101 numberOfLines = thing.length;
10102 }
10103 }
10104 });
10105 return numberOfLines;
10106 };
10107
10108 helpers.color = !color ?
10109 function(value) {
10110 console.error('Color.js not found!');
10111 return value;
10112 } :
10113 function(value) {
10114 /* global CanvasGradient */
10115 if (value instanceof CanvasGradient) {
10116 value = defaults.global.defaultColor;
10117 }
10118
10119 return color(value);
10120 };
10121
10122 helpers.getHoverColor = function(colorValue) {
10123 /* global CanvasPattern */
10124 return (colorValue instanceof CanvasPattern) ?
10125 colorValue :
10126 helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
10127 };
10128 };
10129
10130 },{"2":2,"25":25,"45":45}],28:[function(require,module,exports){
10131 'use strict';
10132
10133 var helpers = require(45);
10134
10135 /**
10136 * Helper function to get relative position for an event
10137 * @param {Event|IEvent} event - The event to get the position for
10138 * @param {Chart} chart - The chart
10139 * @returns {Point} the event position
10140 */
10141 function getRelativePosition(e, chart) {
10142 if (e.native) {
10143 return {
10144 x: e.x,
10145 y: e.y
10146 };
10147 }
10148
10149 return helpers.getRelativePosition(e, chart);
10150 }
10151
10152 /**
10153 * Helper function to traverse all of the visible elements in the chart
10154 * @param chart {chart} the chart
10155 * @param handler {Function} the callback to execute for each visible item
10156 */
10157 function parseVisibleItems(chart, handler) {
10158 var datasets = chart.data.datasets;
10159 var meta, i, j, ilen, jlen;
10160
10161 for (i = 0, ilen = datasets.length; i < ilen; ++i) {
10162 if (!chart.isDatasetVisible(i)) {
10163 continue;
10164 }
10165
10166 meta = chart.getDatasetMeta(i);
10167 for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
10168 var element = meta.data[j];
10169 if (!element._view.skip) {
10170 handler(element);
10171 }
10172 }
10173 }
10174 }
10175
10176 /**
10177 * Helper function to get the items that intersect the event position
10178 * @param items {ChartElement[]} elements to filter
10179 * @param position {Point} the point to be nearest to
10180 * @return {ChartElement[]} the nearest items
10181 */
10182 function getIntersectItems(chart, position) {
10183 var elements = [];
10184
10185 parseVisibleItems(chart, function(element) {
10186 if (element.inRange(position.x, position.y)) {
10187 elements.push(element);
10188 }
10189 });
10190
10191 return elements;
10192 }
10193
10194 /**
10195 * Helper function to get the items nearest to the event position considering all visible items in teh chart
10196 * @param chart {Chart} the chart to look at elements from
10197 * @param position {Point} the point to be nearest to
10198 * @param intersect {Boolean} if true, only consider items that intersect the position
10199 * @param distanceMetric {Function} function to provide the distance between points
10200 * @return {ChartElement[]} the nearest items
10201 */
10202 function getNearestItems(chart, position, intersect, distanceMetric) {
10203 var minDistance = Number.POSITIVE_INFINITY;
10204 var nearestItems = [];
10205
10206 parseVisibleItems(chart, function(element) {
10207 if (intersect && !element.inRange(position.x, position.y)) {
10208 return;
10209 }
10210
10211 var center = element.getCenterPoint();
10212 var distance = distanceMetric(position, center);
10213
10214 if (distance < minDistance) {
10215 nearestItems = [element];
10216 minDistance = distance;
10217 } else if (distance === minDistance) {
10218 // Can have multiple items at the same distance in which case we sort by size
10219 nearestItems.push(element);
10220 }
10221 });
10222
10223 return nearestItems;
10224 }
10225
10226 /**
10227 * Get a distance metric function for two points based on the
10228 * axis mode setting
10229 * @param {String} axis the axis mode. x|y|xy
10230 */
10231 function getDistanceMetricForAxis(axis) {
10232 var useX = axis.indexOf('x') !== -1;
10233 var useY = axis.indexOf('y') !== -1;
10234
10235 return function(pt1, pt2) {
10236 var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
10237 var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
10238 return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
10239 };
10240 }
10241
10242 function indexMode(chart, e, options) {
10243 var position = getRelativePosition(e, chart);
10244 // Default axis for index mode is 'x' to match old behaviour
10245 options.axis = options.axis || 'x';
10246 var distanceMetric = getDistanceMetricForAxis(options.axis);
10247 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
10248 var elements = [];
10249
10250 if (!items.length) {
10251 return [];
10252 }
10253
10254 chart.data.datasets.forEach(function(dataset, datasetIndex) {
10255 if (chart.isDatasetVisible(datasetIndex)) {
10256 var meta = chart.getDatasetMeta(datasetIndex);
10257 var element = meta.data[items[0]._index];
10258
10259 // don't count items that are skipped (null data)
10260 if (element && !element._view.skip) {
10261 elements.push(element);
10262 }
10263 }
10264 });
10265
10266 return elements;
10267 }
10268
10269 /**
10270 * @interface IInteractionOptions
10271 */
10272 /**
10273 * If true, only consider items that intersect the point
10274 * @name IInterfaceOptions#boolean
10275 * @type Boolean
10276 */
10277
10278 /**
10279 * Contains interaction related functions
10280 * @namespace Chart.Interaction
10281 */
10282 module.exports = {
10283 // Helper function for different modes
10284 modes: {
10285 single: function(chart, e) {
10286 var position = getRelativePosition(e, chart);
10287 var elements = [];
10288
10289 parseVisibleItems(chart, function(element) {
10290 if (element.inRange(position.x, position.y)) {
10291 elements.push(element);
10292 return elements;
10293 }
10294 });
10295
10296 return elements.slice(0, 1);
10297 },
10298
10299 /**
10300 * @function Chart.Interaction.modes.label
10301 * @deprecated since version 2.4.0
10302 * @todo remove at version 3
10303 * @private
10304 */
10305 label: indexMode,
10306
10307 /**
10308 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
10309 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
10310 * @function Chart.Interaction.modes.index
10311 * @since v2.4.0
10312 * @param chart {chart} the chart we are returning items from
10313 * @param e {Event} the event we are find things at
10314 * @param options {IInteractionOptions} options to use during interaction
10315 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
10316 */
10317 index: indexMode,
10318
10319 /**
10320 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
10321 * If the options.intersect is false, we find the nearest item and return the items in that dataset
10322 * @function Chart.Interaction.modes.dataset
10323 * @param chart {chart} the chart we are returning items from
10324 * @param e {Event} the event we are find things at
10325 * @param options {IInteractionOptions} options to use during interaction
10326 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
10327 */
10328 dataset: function(chart, e, options) {
10329 var position = getRelativePosition(e, chart);
10330 options.axis = options.axis || 'xy';
10331 var distanceMetric = getDistanceMetricForAxis(options.axis);
10332 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
10333
10334 if (items.length > 0) {
10335 items = chart.getDatasetMeta(items[0]._datasetIndex).data;
10336 }
10337
10338 return items;
10339 },
10340
10341 /**
10342 * @function Chart.Interaction.modes.x-axis
10343 * @deprecated since version 2.4.0. Use index mode and intersect == true
10344 * @todo remove at version 3
10345 * @private
10346 */
10347 'x-axis': function(chart, e) {
10348 return indexMode(chart, e, {intersect: true});
10349 },
10350
10351 /**
10352 * Point mode returns all elements that hit test based on the event position
10353 * of the event
10354 * @function Chart.Interaction.modes.intersect
10355 * @param chart {chart} the chart we are returning items from
10356 * @param e {Event} the event we are find things at
10357 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
10358 */
10359 point: function(chart, e) {
10360 var position = getRelativePosition(e, chart);
10361 return getIntersectItems(chart, position);
10362 },
10363
10364 /**
10365 * nearest mode returns the element closest to the point
10366 * @function Chart.Interaction.modes.intersect
10367 * @param chart {chart} the chart we are returning items from
10368 * @param e {Event} the event we are find things at
10369 * @param options {IInteractionOptions} options to use
10370 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
10371 */
10372 nearest: function(chart, e, options) {
10373 var position = getRelativePosition(e, chart);
10374 options.axis = options.axis || 'xy';
10375 var distanceMetric = getDistanceMetricForAxis(options.axis);
10376 var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);
10377
10378 // We have multiple items at the same distance from the event. Now sort by smallest
10379 if (nearestItems.length > 1) {
10380 nearestItems.sort(function(a, b) {
10381 var sizeA = a.getArea();
10382 var sizeB = b.getArea();
10383 var ret = sizeA - sizeB;
10384
10385 if (ret === 0) {
10386 // if equal sort by dataset index
10387 ret = a._datasetIndex - b._datasetIndex;
10388 }
10389
10390 return ret;
10391 });
10392 }
10393
10394 // Return only 1 item
10395 return nearestItems.slice(0, 1);
10396 },
10397
10398 /**
10399 * x mode returns the elements that hit-test at the current x coordinate
10400 * @function Chart.Interaction.modes.x
10401 * @param chart {chart} the chart we are returning items from
10402 * @param e {Event} the event we are find things at
10403 * @param options {IInteractionOptions} options to use
10404 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
10405 */
10406 x: function(chart, e, options) {
10407 var position = getRelativePosition(e, chart);
10408 var items = [];
10409 var intersectsItem = false;
10410
10411 parseVisibleItems(chart, function(element) {
10412 if (element.inXRange(position.x)) {
10413 items.push(element);
10414 }
10415
10416 if (element.inRange(position.x, position.y)) {
10417 intersectsItem = true;
10418 }
10419 });
10420
10421 // If we want to trigger on an intersect and we don't have any items
10422 // that intersect the position, return nothing
10423 if (options.intersect && !intersectsItem) {
10424 items = [];
10425 }
10426 return items;
10427 },
10428
10429 /**
10430 * y mode returns the elements that hit-test at the current y coordinate
10431 * @function Chart.Interaction.modes.y
10432 * @param chart {chart} the chart we are returning items from
10433 * @param e {Event} the event we are find things at
10434 * @param options {IInteractionOptions} options to use
10435 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
10436 */
10437 y: function(chart, e, options) {
10438 var position = getRelativePosition(e, chart);
10439 var items = [];
10440 var intersectsItem = false;
10441
10442 parseVisibleItems(chart, function(element) {
10443 if (element.inYRange(position.y)) {
10444 items.push(element);
10445 }
10446
10447 if (element.inRange(position.x, position.y)) {
10448 intersectsItem = true;
10449 }
10450 });
10451
10452 // If we want to trigger on an intersect and we don't have any items
10453 // that intersect the position, return nothing
10454 if (options.intersect && !intersectsItem) {
10455 items = [];
10456 }
10457 return items;
10458 }
10459 }
10460 };
10461
10462 },{"45":45}],29:[function(require,module,exports){
10463 'use strict';
10464
10465 var defaults = require(25);
10466
10467 defaults._set('global', {
10468 responsive: true,
10469 responsiveAnimationDuration: 0,
10470 maintainAspectRatio: true,
10471 events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
10472 hover: {
10473 onHover: null,
10474 mode: 'nearest',
10475 intersect: true,
10476 animationDuration: 400
10477 },
10478 onClick: null,
10479 defaultColor: 'rgba(0,0,0,0.1)',
10480 defaultFontColor: '#666',
10481 defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
10482 defaultFontSize: 12,
10483 defaultFontStyle: 'normal',
10484 showLines: true,
10485
10486 // Element defaults defined in element extensions
10487 elements: {},
10488
10489 // Layout options such as padding
10490 layout: {
10491 padding: {
10492 top: 0,
10493 right: 0,
10494 bottom: 0,
10495 left: 0
10496 }
10497 }
10498 });
10499
10500 module.exports = function() {
10501
10502 // Occupy the global variable of Chart, and create a simple base class
10503 var Chart = function(item, config) {
10504 this.construct(item, config);
10505 return this;
10506 };
10507
10508 Chart.Chart = Chart;
10509
10510 return Chart;
10511 };
10512
10513 },{"25":25}],30:[function(require,module,exports){
10514 'use strict';
10515
10516 var helpers = require(45);
10517
10518 module.exports = function(Chart) {
10519
10520 function filterByPosition(array, position) {
10521 return helpers.where(array, function(v) {
10522 return v.position === position;
10523 });
10524 }
10525
10526 function sortByWeight(array, reverse) {
10527 array.forEach(function(v, i) {
10528 v._tmpIndex_ = i;
10529 return v;
10530 });
10531 array.sort(function(a, b) {
10532 var v0 = reverse ? b : a;
10533 var v1 = reverse ? a : b;
10534 return v0.weight === v1.weight ?
10535 v0._tmpIndex_ - v1._tmpIndex_ :
10536 v0.weight - v1.weight;
10537 });
10538 array.forEach(function(v) {
10539 delete v._tmpIndex_;
10540 });
10541 }
10542
10543 /**
10544 * @interface ILayoutItem
10545 * @prop {String} position - The position of the item in the chart layout. Possible values are
10546 * 'left', 'top', 'right', 'bottom', and 'chartArea'
10547 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
10548 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
10549 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
10550 * @prop {Function} update - Takes two parameters: width and height. Returns size of item
10551 * @prop {Function} getPadding - Returns an object with padding on the edges
10552 * @prop {Number} width - Width of item. Must be valid after update()
10553 * @prop {Number} height - Height of item. Must be valid after update()
10554 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
10555 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
10556 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
10557 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
10558 */
10559
10560 // The layout service is very self explanatory. It's responsible for the layout within a chart.
10561 // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
10562 // It is this service's responsibility of carrying out that layout.
10563 Chart.layoutService = {
10564 defaults: {},
10565
10566 /**
10567 * Register a box to a chart.
10568 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
10569 * @param {Chart} chart - the chart to use
10570 * @param {ILayoutItem} item - the item to add to be layed out
10571 */
10572 addBox: function(chart, item) {
10573 if (!chart.boxes) {
10574 chart.boxes = [];
10575 }
10576
10577 // initialize item with default values
10578 item.fullWidth = item.fullWidth || false;
10579 item.position = item.position || 'top';
10580 item.weight = item.weight || 0;
10581
10582 chart.boxes.push(item);
10583 },
10584
10585 /**
10586 * Remove a layoutItem from a chart
10587 * @param {Chart} chart - the chart to remove the box from
10588 * @param {Object} layoutItem - the item to remove from the layout
10589 */
10590 removeBox: function(chart, layoutItem) {
10591 var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
10592 if (index !== -1) {
10593 chart.boxes.splice(index, 1);
10594 }
10595 },
10596
10597 /**
10598 * Sets (or updates) options on the given `item`.
10599 * @param {Chart} chart - the chart in which the item lives (or will be added to)
10600 * @param {Object} item - the item to configure with the given options
10601 * @param {Object} options - the new item options.
10602 */
10603 configure: function(chart, item, options) {
10604 var props = ['fullWidth', 'position', 'weight'];
10605 var ilen = props.length;
10606 var i = 0;
10607 var prop;
10608
10609 for (; i < ilen; ++i) {
10610 prop = props[i];
10611 if (options.hasOwnProperty(prop)) {
10612 item[prop] = options[prop];
10613 }
10614 }
10615 },
10616
10617 /**
10618 * Fits boxes of the given chart into the given size by having each box measure itself
10619 * then running a fitting algorithm
10620 * @param {Chart} chart - the chart
10621 * @param {Number} width - the width to fit into
10622 * @param {Number} height - the height to fit into
10623 */
10624 update: function(chart, width, height) {
10625 if (!chart) {
10626 return;
10627 }
10628
10629 var layoutOptions = chart.options.layout || {};
10630 var padding = helpers.options.toPadding(layoutOptions.padding);
10631 var leftPadding = padding.left;
10632 var rightPadding = padding.right;
10633 var topPadding = padding.top;
10634 var bottomPadding = padding.bottom;
10635
10636 var leftBoxes = filterByPosition(chart.boxes, 'left');
10637 var rightBoxes = filterByPosition(chart.boxes, 'right');
10638 var topBoxes = filterByPosition(chart.boxes, 'top');
10639 var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
10640 var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');
10641
10642 // Sort boxes by weight. A higher weight is further away from the chart area
10643 sortByWeight(leftBoxes, true);
10644 sortByWeight(rightBoxes, false);
10645 sortByWeight(topBoxes, true);
10646 sortByWeight(bottomBoxes, false);
10647
10648 // Essentially we now have any number of boxes on each of the 4 sides.
10649 // Our canvas looks like the following.
10650 // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
10651 // B1 is the bottom axis
10652 // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
10653 // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
10654 // an error will be thrown.
10655 //
10656 // |----------------------------------------------------|
10657 // | T1 (Full Width) |
10658 // |----------------------------------------------------|
10659 // | | | T2 | |
10660 // | |----|-------------------------------------|----|
10661 // | | | C1 | | C2 | |
10662 // | | |----| |----| |
10663 // | | | | |
10664 // | L1 | L2 | ChartArea (C0) | R1 |
10665 // | | | | |
10666 // | | |----| |----| |
10667 // | | | C3 | | C4 | |
10668 // | |----|-------------------------------------|----|
10669 // | | | B1 | |
10670 // |----------------------------------------------------|
10671 // | B2 (Full Width) |
10672 // |----------------------------------------------------|
10673 //
10674 // What we do to find the best sizing, we do the following
10675 // 1. Determine the minimum size of the chart area.
10676 // 2. Split the remaining width equally between each vertical axis
10677 // 3. Split the remaining height equally between each horizontal axis
10678 // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
10679 // 5. Adjust the sizes of each axis based on it's minimum reported size.
10680 // 6. Refit each axis
10681 // 7. Position each axis in the final location
10682 // 8. Tell the chart the final location of the chart area
10683 // 9. Tell any axes that overlay the chart area the positions of the chart area
10684
10685 // Step 1
10686 var chartWidth = width - leftPadding - rightPadding;
10687 var chartHeight = height - topPadding - bottomPadding;
10688 var chartAreaWidth = chartWidth / 2; // min 50%
10689 var chartAreaHeight = chartHeight / 2; // min 50%
10690
10691 // Step 2
10692 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
10693
10694 // Step 3
10695 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
10696
10697 // Step 4
10698 var maxChartAreaWidth = chartWidth;
10699 var maxChartAreaHeight = chartHeight;
10700 var minBoxSizes = [];
10701
10702 function getMinimumBoxSize(box) {
10703 var minSize;
10704 var isHorizontal = box.isHorizontal();
10705
10706 if (isHorizontal) {
10707 minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
10708 maxChartAreaHeight -= minSize.height;
10709 } else {
10710 minSize = box.update(verticalBoxWidth, chartAreaHeight);
10711 maxChartAreaWidth -= minSize.width;
10712 }
10713
10714 minBoxSizes.push({
10715 horizontal: isHorizontal,
10716 minSize: minSize,
10717 box: box,
10718 });
10719 }
10720
10721 helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
10722
10723 // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
10724 var maxHorizontalLeftPadding = 0;
10725 var maxHorizontalRightPadding = 0;
10726 var maxVerticalTopPadding = 0;
10727 var maxVerticalBottomPadding = 0;
10728
10729 helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
10730 if (horizontalBox.getPadding) {
10731 var boxPadding = horizontalBox.getPadding();
10732 maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
10733 maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
10734 }
10735 });
10736
10737 helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
10738 if (verticalBox.getPadding) {
10739 var boxPadding = verticalBox.getPadding();
10740 maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
10741 maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
10742 }
10743 });
10744
10745 // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
10746 // be if the axes are drawn at their minimum sizes.
10747 // Steps 5 & 6
10748 var totalLeftBoxesWidth = leftPadding;
10749 var totalRightBoxesWidth = rightPadding;
10750 var totalTopBoxesHeight = topPadding;
10751 var totalBottomBoxesHeight = bottomPadding;
10752
10753 // Function to fit a box
10754 function fitBox(box) {
10755 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
10756 return minBox.box === box;
10757 });
10758
10759 if (minBoxSize) {
10760 if (box.isHorizontal()) {
10761 var scaleMargin = {
10762 left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
10763 right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
10764 top: 0,
10765 bottom: 0
10766 };
10767
10768 // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
10769 // on the margin. Sometimes they need to increase in size slightly
10770 box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
10771 } else {
10772 box.update(minBoxSize.minSize.width, maxChartAreaHeight);
10773 }
10774 }
10775 }
10776
10777 // Update, and calculate the left and right margins for the horizontal boxes
10778 helpers.each(leftBoxes.concat(rightBoxes), fitBox);
10779
10780 helpers.each(leftBoxes, function(box) {
10781 totalLeftBoxesWidth += box.width;
10782 });
10783
10784 helpers.each(rightBoxes, function(box) {
10785 totalRightBoxesWidth += box.width;
10786 });
10787
10788 // Set the Left and Right margins for the horizontal boxes
10789 helpers.each(topBoxes.concat(bottomBoxes), fitBox);
10790
10791 // Figure out how much margin is on the top and bottom of the vertical boxes
10792 helpers.each(topBoxes, function(box) {
10793 totalTopBoxesHeight += box.height;
10794 });
10795
10796 helpers.each(bottomBoxes, function(box) {
10797 totalBottomBoxesHeight += box.height;
10798 });
10799
10800 function finalFitVerticalBox(box) {
10801 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
10802 return minSize.box === box;
10803 });
10804
10805 var scaleMargin = {
10806 left: 0,
10807 right: 0,
10808 top: totalTopBoxesHeight,
10809 bottom: totalBottomBoxesHeight
10810 };
10811
10812 if (minBoxSize) {
10813 box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
10814 }
10815 }
10816
10817 // Let the left layout know the final margin
10818 helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
10819
10820 // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
10821 totalLeftBoxesWidth = leftPadding;
10822 totalRightBoxesWidth = rightPadding;
10823 totalTopBoxesHeight = topPadding;
10824 totalBottomBoxesHeight = bottomPadding;
10825
10826 helpers.each(leftBoxes, function(box) {
10827 totalLeftBoxesWidth += box.width;
10828 });
10829
10830 helpers.each(rightBoxes, function(box) {
10831 totalRightBoxesWidth += box.width;
10832 });
10833
10834 helpers.each(topBoxes, function(box) {
10835 totalTopBoxesHeight += box.height;
10836 });
10837 helpers.each(bottomBoxes, function(box) {
10838 totalBottomBoxesHeight += box.height;
10839 });
10840
10841 // We may be adding some padding to account for rotated x axis labels
10842 var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
10843 totalLeftBoxesWidth += leftPaddingAddition;
10844 totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);
10845
10846 var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
10847 totalTopBoxesHeight += topPaddingAddition;
10848 totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);
10849
10850 // Figure out if our chart area changed. This would occur if the dataset layout label rotation
10851 // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
10852 // without calling `fit` again
10853 var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
10854 var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
10855
10856 if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
10857 helpers.each(leftBoxes, function(box) {
10858 box.height = newMaxChartAreaHeight;
10859 });
10860
10861 helpers.each(rightBoxes, function(box) {
10862 box.height = newMaxChartAreaHeight;
10863 });
10864
10865 helpers.each(topBoxes, function(box) {
10866 if (!box.fullWidth) {
10867 box.width = newMaxChartAreaWidth;
10868 }
10869 });
10870
10871 helpers.each(bottomBoxes, function(box) {
10872 if (!box.fullWidth) {
10873 box.width = newMaxChartAreaWidth;
10874 }
10875 });
10876
10877 maxChartAreaHeight = newMaxChartAreaHeight;
10878 maxChartAreaWidth = newMaxChartAreaWidth;
10879 }
10880
10881 // Step 7 - Position the boxes
10882 var left = leftPadding + leftPaddingAddition;
10883 var top = topPadding + topPaddingAddition;
10884
10885 function placeBox(box) {
10886 if (box.isHorizontal()) {
10887 box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
10888 box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
10889 box.top = top;
10890 box.bottom = top + box.height;
10891
10892 // Move to next point
10893 top = box.bottom;
10894
10895 } else {
10896
10897 box.left = left;
10898 box.right = left + box.width;
10899 box.top = totalTopBoxesHeight;
10900 box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
10901
10902 // Move to next point
10903 left = box.right;
10904 }
10905 }
10906
10907 helpers.each(leftBoxes.concat(topBoxes), placeBox);
10908
10909 // Account for chart width and height
10910 left += maxChartAreaWidth;
10911 top += maxChartAreaHeight;
10912
10913 helpers.each(rightBoxes, placeBox);
10914 helpers.each(bottomBoxes, placeBox);
10915
10916 // Step 8
10917 chart.chartArea = {
10918 left: totalLeftBoxesWidth,
10919 top: totalTopBoxesHeight,
10920 right: totalLeftBoxesWidth + maxChartAreaWidth,
10921 bottom: totalTopBoxesHeight + maxChartAreaHeight
10922 };
10923
10924 // Step 9
10925 helpers.each(chartAreaBoxes, function(box) {
10926 box.left = chart.chartArea.left;
10927 box.top = chart.chartArea.top;
10928 box.right = chart.chartArea.right;
10929 box.bottom = chart.chartArea.bottom;
10930
10931 box.update(maxChartAreaWidth, maxChartAreaHeight);
10932 });
10933 }
10934 };
10935 };
10936
10937 },{"45":45}],31:[function(require,module,exports){
10938 'use strict';
10939
10940 var defaults = require(25);
10941 var Element = require(26);
10942 var helpers = require(45);
10943
10944 defaults._set('global', {
10945 plugins: {}
10946 });
10947
10948 module.exports = function(Chart) {
10949
10950 /**
10951 * The plugin service singleton
10952 * @namespace Chart.plugins
10953 * @since 2.1.0
10954 */
10955 Chart.plugins = {
10956 /**
10957 * Globally registered plugins.
10958 * @private
10959 */
10960 _plugins: [],
10961
10962 /**
10963 * This identifier is used to invalidate the descriptors cache attached to each chart
10964 * when a global plugin is registered or unregistered. In this case, the cache ID is
10965 * incremented and descriptors are regenerated during following API calls.
10966 * @private
10967 */
10968 _cacheId: 0,
10969
10970 /**
10971 * Registers the given plugin(s) if not already registered.
10972 * @param {Array|Object} plugins plugin instance(s).
10973 */
10974 register: function(plugins) {
10975 var p = this._plugins;
10976 ([]).concat(plugins).forEach(function(plugin) {
10977 if (p.indexOf(plugin) === -1) {
10978 p.push(plugin);
10979 }
10980 });
10981
10982 this._cacheId++;
10983 },
10984
10985 /**
10986 * Unregisters the given plugin(s) only if registered.
10987 * @param {Array|Object} plugins plugin instance(s).
10988 */
10989 unregister: function(plugins) {
10990 var p = this._plugins;
10991 ([]).concat(plugins).forEach(function(plugin) {
10992 var idx = p.indexOf(plugin);
10993 if (idx !== -1) {
10994 p.splice(idx, 1);
10995 }
10996 });
10997
10998 this._cacheId++;
10999 },
11000
11001 /**
11002 * Remove all registered plugins.
11003 * @since 2.1.5
11004 */
11005 clear: function() {
11006 this._plugins = [];
11007 this._cacheId++;
11008 },
11009
11010 /**
11011 * Returns the number of registered plugins?
11012 * @returns {Number}
11013 * @since 2.1.5
11014 */
11015 count: function() {
11016 return this._plugins.length;
11017 },
11018
11019 /**
11020 * Returns all registered plugin instances.
11021 * @returns {Array} array of plugin objects.
11022 * @since 2.1.5
11023 */
11024 getAll: function() {
11025 return this._plugins;
11026 },
11027
11028 /**
11029 * Calls enabled plugins for `chart` on the specified hook and with the given args.
11030 * This method immediately returns as soon as a plugin explicitly returns false. The
11031 * returned value can be used, for instance, to interrupt the current action.
11032 * @param {Object} chart - The chart instance for which plugins should be called.
11033 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
11034 * @param {Array} [args] - Extra arguments to apply to the hook call.
11035 * @returns {Boolean} false if any of the plugins return false, else returns true.
11036 */
11037 notify: function(chart, hook, args) {
11038 var descriptors = this.descriptors(chart);
11039 var ilen = descriptors.length;
11040 var i, descriptor, plugin, params, method;
11041
11042 for (i = 0; i < ilen; ++i) {
11043 descriptor = descriptors[i];
11044 plugin = descriptor.plugin;
11045 method = plugin[hook];
11046 if (typeof method === 'function') {
11047 params = [chart].concat(args || []);
11048 params.push(descriptor.options);
11049 if (method.apply(plugin, params) === false) {
11050 return false;
11051 }
11052 }
11053 }
11054
11055 return true;
11056 },
11057
11058 /**
11059 * Returns descriptors of enabled plugins for the given chart.
11060 * @returns {Array} [{ plugin, options }]
11061 * @private
11062 */
11063 descriptors: function(chart) {
11064 var cache = chart._plugins || (chart._plugins = {});
11065 if (cache.id === this._cacheId) {
11066 return cache.descriptors;
11067 }
11068
11069 var plugins = [];
11070 var descriptors = [];
11071 var config = (chart && chart.config) || {};
11072 var options = (config.options && config.options.plugins) || {};
11073
11074 this._plugins.concat(config.plugins || []).forEach(function(plugin) {
11075 var idx = plugins.indexOf(plugin);
11076 if (idx !== -1) {
11077 return;
11078 }
11079
11080 var id = plugin.id;
11081 var opts = options[id];
11082 if (opts === false) {
11083 return;
11084 }
11085
11086 if (opts === true) {
11087 opts = helpers.clone(defaults.global.plugins[id]);
11088 }
11089
11090 plugins.push(plugin);
11091 descriptors.push({
11092 plugin: plugin,
11093 options: opts || {}
11094 });
11095 });
11096
11097 cache.descriptors = descriptors;
11098 cache.id = this._cacheId;
11099 return descriptors;
11100 }
11101 };
11102
11103 /**
11104 * Plugin extension hooks.
11105 * @interface IPlugin
11106 * @since 2.1.0
11107 */
11108 /**
11109 * @method IPlugin#beforeInit
11110 * @desc Called before initializing `chart`.
11111 * @param {Chart.Controller} chart - The chart instance.
11112 * @param {Object} options - The plugin options.
11113 */
11114 /**
11115 * @method IPlugin#afterInit
11116 * @desc Called after `chart` has been initialized and before the first update.
11117 * @param {Chart.Controller} chart - The chart instance.
11118 * @param {Object} options - The plugin options.
11119 */
11120 /**
11121 * @method IPlugin#beforeUpdate
11122 * @desc Called before updating `chart`. If any plugin returns `false`, the update
11123 * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
11124 * @param {Chart.Controller} chart - The chart instance.
11125 * @param {Object} options - The plugin options.
11126 * @returns {Boolean} `false` to cancel the chart update.
11127 */
11128 /**
11129 * @method IPlugin#afterUpdate
11130 * @desc Called after `chart` has been updated and before rendering. Note that this
11131 * hook will not be called if the chart update has been previously cancelled.
11132 * @param {Chart.Controller} chart - The chart instance.
11133 * @param {Object} options - The plugin options.
11134 */
11135 /**
11136 * @method IPlugin#beforeDatasetsUpdate
11137 * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
11138 * the datasets update is cancelled until another `update` is triggered.
11139 * @param {Chart.Controller} chart - The chart instance.
11140 * @param {Object} options - The plugin options.
11141 * @returns {Boolean} false to cancel the datasets update.
11142 * @since version 2.1.5
11143 */
11144 /**
11145 * @method IPlugin#afterDatasetsUpdate
11146 * @desc Called after the `chart` datasets have been updated. Note that this hook
11147 * will not be called if the datasets update has been previously cancelled.
11148 * @param {Chart.Controller} chart - The chart instance.
11149 * @param {Object} options - The plugin options.
11150 * @since version 2.1.5
11151 */
11152 /**
11153 * @method IPlugin#beforeDatasetUpdate
11154 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
11155 * returns `false`, the datasets update is cancelled until another `update` is triggered.
11156 * @param {Chart} chart - The chart instance.
11157 * @param {Object} args - The call arguments.
11158 * @param {Number} args.index - The dataset index.
11159 * @param {Object} args.meta - The dataset metadata.
11160 * @param {Object} options - The plugin options.
11161 * @returns {Boolean} `false` to cancel the chart datasets drawing.
11162 */
11163 /**
11164 * @method IPlugin#afterDatasetUpdate
11165 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
11166 * that this hook will not be called if the datasets update has been previously cancelled.
11167 * @param {Chart} chart - The chart instance.
11168 * @param {Object} args - The call arguments.
11169 * @param {Number} args.index - The dataset index.
11170 * @param {Object} args.meta - The dataset metadata.
11171 * @param {Object} options - The plugin options.
11172 */
11173 /**
11174 * @method IPlugin#beforeLayout
11175 * @desc Called before laying out `chart`. If any plugin returns `false`,
11176 * the layout update is cancelled until another `update` is triggered.
11177 * @param {Chart.Controller} chart - The chart instance.
11178 * @param {Object} options - The plugin options.
11179 * @returns {Boolean} `false` to cancel the chart layout.
11180 */
11181 /**
11182 * @method IPlugin#afterLayout
11183 * @desc Called after the `chart` has been layed out. Note that this hook will not
11184 * be called if the layout update has been previously cancelled.
11185 * @param {Chart.Controller} chart - The chart instance.
11186 * @param {Object} options - The plugin options.
11187 */
11188 /**
11189 * @method IPlugin#beforeRender
11190 * @desc Called before rendering `chart`. If any plugin returns `false`,
11191 * the rendering is cancelled until another `render` is triggered.
11192 * @param {Chart.Controller} chart - The chart instance.
11193 * @param {Object} options - The plugin options.
11194 * @returns {Boolean} `false` to cancel the chart rendering.
11195 */
11196 /**
11197 * @method IPlugin#afterRender
11198 * @desc Called after the `chart` has been fully rendered (and animation completed). Note
11199 * that this hook will not be called if the rendering has been previously cancelled.
11200 * @param {Chart.Controller} chart - The chart instance.
11201 * @param {Object} options - The plugin options.
11202 */
11203 /**
11204 * @method IPlugin#beforeDraw
11205 * @desc Called before drawing `chart` at every animation frame specified by the given
11206 * easing value. If any plugin returns `false`, the frame drawing is cancelled until
11207 * another `render` is triggered.
11208 * @param {Chart.Controller} chart - The chart instance.
11209 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
11210 * @param {Object} options - The plugin options.
11211 * @returns {Boolean} `false` to cancel the chart drawing.
11212 */
11213 /**
11214 * @method IPlugin#afterDraw
11215 * @desc Called after the `chart` has been drawn for the specific easing value. Note
11216 * that this hook will not be called if the drawing has been previously cancelled.
11217 * @param {Chart.Controller} chart - The chart instance.
11218 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
11219 * @param {Object} options - The plugin options.
11220 */
11221 /**
11222 * @method IPlugin#beforeDatasetsDraw
11223 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
11224 * the datasets drawing is cancelled until another `render` is triggered.
11225 * @param {Chart.Controller} chart - The chart instance.
11226 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
11227 * @param {Object} options - The plugin options.
11228 * @returns {Boolean} `false` to cancel the chart datasets drawing.
11229 */
11230 /**
11231 * @method IPlugin#afterDatasetsDraw
11232 * @desc Called after the `chart` datasets have been drawn. Note that this hook
11233 * will not be called if the datasets drawing has been previously cancelled.
11234 * @param {Chart.Controller} chart - The chart instance.
11235 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
11236 * @param {Object} options - The plugin options.
11237 */
11238 /**
11239 * @method IPlugin#beforeDatasetDraw
11240 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
11241 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
11242 * is cancelled until another `render` is triggered.
11243 * @param {Chart} chart - The chart instance.
11244 * @param {Object} args - The call arguments.
11245 * @param {Number} args.index - The dataset index.
11246 * @param {Object} args.meta - The dataset metadata.
11247 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
11248 * @param {Object} options - The plugin options.
11249 * @returns {Boolean} `false` to cancel the chart datasets drawing.
11250 */
11251 /**
11252 * @method IPlugin#afterDatasetDraw
11253 * @desc Called after the `chart` datasets at the given `args.index` have been drawn
11254 * (datasets are drawn in the reverse order). Note that this hook will not be called
11255 * if the datasets drawing has been previously cancelled.
11256 * @param {Chart} chart - The chart instance.
11257 * @param {Object} args - The call arguments.
11258 * @param {Number} args.index - The dataset index.
11259 * @param {Object} args.meta - The dataset metadata.
11260 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
11261 * @param {Object} options - The plugin options.
11262 */
11263 /**
11264 * @method IPlugin#beforeEvent
11265 * @desc Called before processing the specified `event`. If any plugin returns `false`,
11266 * the event will be discarded.
11267 * @param {Chart.Controller} chart - The chart instance.
11268 * @param {IEvent} event - The event object.
11269 * @param {Object} options - The plugin options.
11270 */
11271 /**
11272 * @method IPlugin#afterEvent
11273 * @desc Called after the `event` has been consumed. Note that this hook
11274 * will not be called if the `event` has been previously discarded.
11275 * @param {Chart.Controller} chart - The chart instance.
11276 * @param {IEvent} event - The event object.
11277 * @param {Object} options - The plugin options.
11278 */
11279 /**
11280 * @method IPlugin#resize
11281 * @desc Called after the chart as been resized.
11282 * @param {Chart.Controller} chart - The chart instance.
11283 * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
11284 * @param {Object} options - The plugin options.
11285 */
11286 /**
11287 * @method IPlugin#destroy
11288 * @desc Called after the chart as been destroyed.
11289 * @param {Chart.Controller} chart - The chart instance.
11290 * @param {Object} options - The plugin options.
11291 */
11292
11293 /**
11294 * Provided for backward compatibility, use Chart.plugins instead
11295 * @namespace Chart.pluginService
11296 * @deprecated since version 2.1.5
11297 * @todo remove at version 3
11298 * @private
11299 */
11300 Chart.pluginService = Chart.plugins;
11301
11302 /**
11303 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
11304 * effect, instead simply create/register plugins via plain JavaScript objects.
11305 * @interface Chart.PluginBase
11306 * @deprecated since version 2.5.0
11307 * @todo remove at version 3
11308 * @private
11309 */
11310 Chart.PluginBase = Element.extend({});
11311 };
11312
11313 },{"25":25,"26":26,"45":45}],32:[function(require,module,exports){
11314 'use strict';
11315
11316 var defaults = require(25);
11317 var Element = require(26);
11318 var helpers = require(45);
11319 var Ticks = require(34);
11320
11321 defaults._set('scale', {
11322 display: true,
11323 position: 'left',
11324 offset: false,
11325
11326 // grid line settings
11327 gridLines: {
11328 display: true,
11329 color: 'rgba(0, 0, 0, 0.1)',
11330 lineWidth: 1,
11331 drawBorder: true,
11332 drawOnChartArea: true,
11333 drawTicks: true,
11334 tickMarkLength: 10,
11335 zeroLineWidth: 1,
11336 zeroLineColor: 'rgba(0,0,0,0.25)',
11337 zeroLineBorderDash: [],
11338 zeroLineBorderDashOffset: 0.0,
11339 offsetGridLines: false,
11340 borderDash: [],
11341 borderDashOffset: 0.0
11342 },
11343
11344 // scale label
11345 scaleLabel: {
11346 // display property
11347 display: false,
11348
11349 // actual label
11350 labelString: '',
11351
11352 // line height
11353 lineHeight: 1.2,
11354
11355 // top/bottom padding
11356 padding: {
11357 top: 4,
11358 bottom: 4
11359 }
11360 },
11361
11362 // label settings
11363 ticks: {
11364 beginAtZero: false,
11365 minRotation: 0,
11366 maxRotation: 50,
11367 mirror: false,
11368 padding: 0,
11369 reverse: false,
11370 display: true,
11371 autoSkip: true,
11372 autoSkipPadding: 0,
11373 labelOffset: 0,
11374 // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
11375 callback: Ticks.formatters.values,
11376 minor: {},
11377 major: {}
11378 }
11379 });
11380
11381 function labelsFromTicks(ticks) {
11382 var labels = [];
11383 var i, ilen;
11384
11385 for (i = 0, ilen = ticks.length; i < ilen; ++i) {
11386 labels.push(ticks[i].label);
11387 }
11388
11389 return labels;
11390 }
11391
11392 function getLineValue(scale, index, offsetGridLines) {
11393 var lineValue = scale.getPixelForTick(index);
11394
11395 if (offsetGridLines) {
11396 if (index === 0) {
11397 lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
11398 } else {
11399 lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
11400 }
11401 }
11402 return lineValue;
11403 }
11404
11405 module.exports = function(Chart) {
11406
11407 function computeTextSize(context, tick, font) {
11408 return helpers.isArray(tick) ?
11409 helpers.longestText(context, font, tick) :
11410 context.measureText(tick).width;
11411 }
11412
11413 function parseFontOptions(options) {
11414 var valueOrDefault = helpers.valueOrDefault;
11415 var globalDefaults = defaults.global;
11416 var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
11417 var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
11418 var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
11419
11420 return {
11421 size: size,
11422 style: style,
11423 family: family,
11424 font: helpers.fontString(size, style, family)
11425 };
11426 }
11427
11428 function parseLineHeight(options) {
11429 return helpers.options.toLineHeight(
11430 helpers.valueOrDefault(options.lineHeight, 1.2),
11431 helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
11432 }
11433
11434 Chart.Scale = Element.extend({
11435 /**
11436 * Get the padding needed for the scale
11437 * @method getPadding
11438 * @private
11439 * @returns {Padding} the necessary padding
11440 */
11441 getPadding: function() {
11442 var me = this;
11443 return {
11444 left: me.paddingLeft || 0,
11445 top: me.paddingTop || 0,
11446 right: me.paddingRight || 0,
11447 bottom: me.paddingBottom || 0
11448 };
11449 },
11450
11451 /**
11452 * Returns the scale tick objects ({label, major})
11453 * @since 2.7
11454 */
11455 getTicks: function() {
11456 return this._ticks;
11457 },
11458
11459 // These methods are ordered by lifecyle. Utilities then follow.
11460 // Any function defined here is inherited by all scale types.
11461 // Any function can be extended by the scale type
11462
11463 mergeTicksOptions: function() {
11464 var ticks = this.options.ticks;
11465 if (ticks.minor === false) {
11466 ticks.minor = {
11467 display: false
11468 };
11469 }
11470 if (ticks.major === false) {
11471 ticks.major = {
11472 display: false
11473 };
11474 }
11475 for (var key in ticks) {
11476 if (key !== 'major' && key !== 'minor') {
11477 if (typeof ticks.minor[key] === 'undefined') {
11478 ticks.minor[key] = ticks[key];
11479 }
11480 if (typeof ticks.major[key] === 'undefined') {
11481 ticks.major[key] = ticks[key];
11482 }
11483 }
11484 }
11485 },
11486 beforeUpdate: function() {
11487 helpers.callback(this.options.beforeUpdate, [this]);
11488 },
11489 update: function(maxWidth, maxHeight, margins) {
11490 var me = this;
11491 var i, ilen, labels, label, ticks, tick;
11492
11493 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
11494 me.beforeUpdate();
11495
11496 // Absorb the master measurements
11497 me.maxWidth = maxWidth;
11498 me.maxHeight = maxHeight;
11499 me.margins = helpers.extend({
11500 left: 0,
11501 right: 0,
11502 top: 0,
11503 bottom: 0
11504 }, margins);
11505 me.longestTextCache = me.longestTextCache || {};
11506
11507 // Dimensions
11508 me.beforeSetDimensions();
11509 me.setDimensions();
11510 me.afterSetDimensions();
11511
11512 // Data min/max
11513 me.beforeDataLimits();
11514 me.determineDataLimits();
11515 me.afterDataLimits();
11516
11517 // Ticks - `this.ticks` is now DEPRECATED!
11518 // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
11519 // and must not be accessed directly from outside this class. `this.ticks` being
11520 // around for long time and not marked as private, we can't change its structure
11521 // without unexpected breaking changes. If you need to access the scale ticks,
11522 // use scale.getTicks() instead.
11523
11524 me.beforeBuildTicks();
11525
11526 // New implementations should return an array of objects but for BACKWARD COMPAT,
11527 // we still support no return (`this.ticks` internally set by calling this method).
11528 ticks = me.buildTicks() || [];
11529
11530 me.afterBuildTicks();
11531
11532 me.beforeTickToLabelConversion();
11533
11534 // New implementations should return the formatted tick labels but for BACKWARD
11535 // COMPAT, we still support no return (`this.ticks` internally changed by calling
11536 // this method and supposed to contain only string values).
11537 labels = me.convertTicksToLabels(ticks) || me.ticks;
11538
11539 me.afterTickToLabelConversion();
11540
11541 me.ticks = labels; // BACKWARD COMPATIBILITY
11542
11543 // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!
11544
11545 // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
11546 for (i = 0, ilen = labels.length; i < ilen; ++i) {
11547 label = labels[i];
11548 tick = ticks[i];
11549 if (!tick) {
11550 ticks.push(tick = {
11551 label: label,
11552 major: false
11553 });
11554 } else {
11555 tick.label = label;
11556 }
11557 }
11558
11559 me._ticks = ticks;
11560
11561 // Tick Rotation
11562 me.beforeCalculateTickRotation();
11563 me.calculateTickRotation();
11564 me.afterCalculateTickRotation();
11565 // Fit
11566 me.beforeFit();
11567 me.fit();
11568 me.afterFit();
11569 //
11570 me.afterUpdate();
11571
11572 return me.minSize;
11573
11574 },
11575 afterUpdate: function() {
11576 helpers.callback(this.options.afterUpdate, [this]);
11577 },
11578
11579 //
11580
11581 beforeSetDimensions: function() {
11582 helpers.callback(this.options.beforeSetDimensions, [this]);
11583 },
11584 setDimensions: function() {
11585 var me = this;
11586 // Set the unconstrained dimension before label rotation
11587 if (me.isHorizontal()) {
11588 // Reset position before calculating rotation
11589 me.width = me.maxWidth;
11590 me.left = 0;
11591 me.right = me.width;
11592 } else {
11593 me.height = me.maxHeight;
11594
11595 // Reset position before calculating rotation
11596 me.top = 0;
11597 me.bottom = me.height;
11598 }
11599
11600 // Reset padding
11601 me.paddingLeft = 0;
11602 me.paddingTop = 0;
11603 me.paddingRight = 0;
11604 me.paddingBottom = 0;
11605 },
11606 afterSetDimensions: function() {
11607 helpers.callback(this.options.afterSetDimensions, [this]);
11608 },
11609
11610 // Data limits
11611 beforeDataLimits: function() {
11612 helpers.callback(this.options.beforeDataLimits, [this]);
11613 },
11614 determineDataLimits: helpers.noop,
11615 afterDataLimits: function() {
11616 helpers.callback(this.options.afterDataLimits, [this]);
11617 },
11618
11619 //
11620 beforeBuildTicks: function() {
11621 helpers.callback(this.options.beforeBuildTicks, [this]);
11622 },
11623 buildTicks: helpers.noop,
11624 afterBuildTicks: function() {
11625 helpers.callback(this.options.afterBuildTicks, [this]);
11626 },
11627
11628 beforeTickToLabelConversion: function() {
11629 helpers.callback(this.options.beforeTickToLabelConversion, [this]);
11630 },
11631 convertTicksToLabels: function() {
11632 var me = this;
11633 // Convert ticks to strings
11634 var tickOpts = me.options.ticks;
11635 me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
11636 },
11637 afterTickToLabelConversion: function() {
11638 helpers.callback(this.options.afterTickToLabelConversion, [this]);
11639 },
11640
11641 //
11642
11643 beforeCalculateTickRotation: function() {
11644 helpers.callback(this.options.beforeCalculateTickRotation, [this]);
11645 },
11646 calculateTickRotation: function() {
11647 var me = this;
11648 var context = me.ctx;
11649 var tickOpts = me.options.ticks;
11650 var labels = labelsFromTicks(me._ticks);
11651
11652 // Get the width of each grid by calculating the difference
11653 // between x offsets between 0 and 1.
11654 var tickFont = parseFontOptions(tickOpts);
11655 context.font = tickFont.font;
11656
11657 var labelRotation = tickOpts.minRotation || 0;
11658
11659 if (labels.length && me.options.display && me.isHorizontal()) {
11660 var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
11661 var labelWidth = originalLabelWidth;
11662 var cosRotation, sinRotation;
11663
11664 // Allow 3 pixels x2 padding either side for label readability
11665 var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
11666
11667 // Max label rotation can be set or default to 90 - also act as a loop counter
11668 while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
11669 var angleRadians = helpers.toRadians(labelRotation);
11670 cosRotation = Math.cos(angleRadians);
11671 sinRotation = Math.sin(angleRadians);
11672
11673 if (sinRotation * originalLabelWidth > me.maxHeight) {
11674 // go back one step
11675 labelRotation--;
11676 break;
11677 }
11678
11679 labelRotation++;
11680 labelWidth = cosRotation * originalLabelWidth;
11681 }
11682 }
11683
11684 me.labelRotation = labelRotation;
11685 },
11686 afterCalculateTickRotation: function() {
11687 helpers.callback(this.options.afterCalculateTickRotation, [this]);
11688 },
11689
11690 //
11691
11692 beforeFit: function() {
11693 helpers.callback(this.options.beforeFit, [this]);
11694 },
11695 fit: function() {
11696 var me = this;
11697 // Reset
11698 var minSize = me.minSize = {
11699 width: 0,
11700 height: 0
11701 };
11702
11703 var labels = labelsFromTicks(me._ticks);
11704
11705 var opts = me.options;
11706 var tickOpts = opts.ticks;
11707 var scaleLabelOpts = opts.scaleLabel;
11708 var gridLineOpts = opts.gridLines;
11709 var display = opts.display;
11710 var isHorizontal = me.isHorizontal();
11711
11712 var tickFont = parseFontOptions(tickOpts);
11713 var tickMarkLength = opts.gridLines.tickMarkLength;
11714
11715 // Width
11716 if (isHorizontal) {
11717 // subtract the margins to line up with the chartArea if we are a full width scale
11718 minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
11719 } else {
11720 minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
11721 }
11722
11723 // height
11724 if (isHorizontal) {
11725 minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
11726 } else {
11727 minSize.height = me.maxHeight; // fill all the height
11728 }
11729
11730 // Are we showing a title for the scale?
11731 if (scaleLabelOpts.display && display) {
11732 var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
11733 var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
11734 var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;
11735
11736 if (isHorizontal) {
11737 minSize.height += deltaHeight;
11738 } else {
11739 minSize.width += deltaHeight;
11740 }
11741 }
11742
11743 // Don't bother fitting the ticks if we are not showing them
11744 if (tickOpts.display && display) {
11745 var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
11746 var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
11747 var lineSpace = tickFont.size * 0.5;
11748 var tickPadding = me.options.ticks.padding;
11749
11750 if (isHorizontal) {
11751 // A horizontal axis is more constrained by the height.
11752 me.longestLabelWidth = largestTextWidth;
11753
11754 var angleRadians = helpers.toRadians(me.labelRotation);
11755 var cosRotation = Math.cos(angleRadians);
11756 var sinRotation = Math.sin(angleRadians);
11757
11758 // TODO - improve this calculation
11759 var labelHeight = (sinRotation * largestTextWidth)
11760 + (tickFont.size * tallestLabelHeightInLines)
11761 + (lineSpace * (tallestLabelHeightInLines - 1))
11762 + lineSpace; // padding
11763
11764 minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
11765
11766 me.ctx.font = tickFont.font;
11767 var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
11768 var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);
11769
11770 // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
11771 // which means that the right padding is dominated by the font height
11772 if (me.labelRotation !== 0) {
11773 me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
11774 me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
11775 } else {
11776 me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
11777 me.paddingRight = lastLabelWidth / 2 + 3;
11778 }
11779 } else {
11780 // A vertical axis is more constrained by the width. Labels are the
11781 // dominant factor here, so get that length first and account for padding
11782 if (tickOpts.mirror) {
11783 largestTextWidth = 0;
11784 } else {
11785 // use lineSpace for consistency with horizontal axis
11786 // tickPadding is not implemented for horizontal
11787 largestTextWidth += tickPadding + lineSpace;
11788 }
11789
11790 minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);
11791
11792 me.paddingTop = tickFont.size / 2;
11793 me.paddingBottom = tickFont.size / 2;
11794 }
11795 }
11796
11797 me.handleMargins();
11798
11799 me.width = minSize.width;
11800 me.height = minSize.height;
11801 },
11802
11803 /**
11804 * Handle margins and padding interactions
11805 * @private
11806 */
11807 handleMargins: function() {
11808 var me = this;
11809 if (me.margins) {
11810 me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
11811 me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
11812 me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
11813 me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
11814 }
11815 },
11816
11817 afterFit: function() {
11818 helpers.callback(this.options.afterFit, [this]);
11819 },
11820
11821 // Shared Methods
11822 isHorizontal: function() {
11823 return this.options.position === 'top' || this.options.position === 'bottom';
11824 },
11825 isFullWidth: function() {
11826 return (this.options.fullWidth);
11827 },
11828
11829 // 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
11830 getRightValue: function(rawValue) {
11831 // Null and undefined values first
11832 if (helpers.isNullOrUndef(rawValue)) {
11833 return NaN;
11834 }
11835 // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
11836 if (typeof rawValue === 'number' && !isFinite(rawValue)) {
11837 return NaN;
11838 }
11839 // If it is in fact an object, dive in one more level
11840 if (rawValue) {
11841 if (this.isHorizontal()) {
11842 if (rawValue.x !== undefined) {
11843 return this.getRightValue(rawValue.x);
11844 }
11845 } else if (rawValue.y !== undefined) {
11846 return this.getRightValue(rawValue.y);
11847 }
11848 }
11849
11850 // Value is good, return it
11851 return rawValue;
11852 },
11853
11854 // Used to get the value to display in the tooltip for the data at the given index
11855 // function getLabelForIndex(index, datasetIndex)
11856 getLabelForIndex: helpers.noop,
11857
11858 // Used to get data value locations. Value can either be an index or a numerical value
11859 getPixelForValue: helpers.noop,
11860
11861 // Used to get the data value from a given pixel. This is the inverse of getPixelForValue
11862 getValueForPixel: helpers.noop,
11863
11864 // Used for tick location, should
11865 getPixelForTick: function(index) {
11866 var me = this;
11867 var offset = me.options.offset;
11868 if (me.isHorizontal()) {
11869 var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
11870 var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
11871 var pixel = (tickWidth * index) + me.paddingLeft;
11872
11873 if (offset) {
11874 pixel += tickWidth / 2;
11875 }
11876
11877 var finalVal = me.left + Math.round(pixel);
11878 finalVal += me.isFullWidth() ? me.margins.left : 0;
11879 return finalVal;
11880 }
11881 var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
11882 return me.top + (index * (innerHeight / (me._ticks.length - 1)));
11883 },
11884
11885 // Utility for getting the pixel location of a percentage of scale
11886 getPixelForDecimal: function(decimal) {
11887 var me = this;
11888 if (me.isHorizontal()) {
11889 var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
11890 var valueOffset = (innerWidth * decimal) + me.paddingLeft;
11891
11892 var finalVal = me.left + Math.round(valueOffset);
11893 finalVal += me.isFullWidth() ? me.margins.left : 0;
11894 return finalVal;
11895 }
11896 return me.top + (decimal * me.height);
11897 },
11898
11899 getBasePixel: function() {
11900 return this.getPixelForValue(this.getBaseValue());
11901 },
11902
11903 getBaseValue: function() {
11904 var me = this;
11905 var min = me.min;
11906 var max = me.max;
11907
11908 return me.beginAtZero ? 0 :
11909 min < 0 && max < 0 ? max :
11910 min > 0 && max > 0 ? min :
11911 0;
11912 },
11913
11914 /**
11915 * Returns a subset of ticks to be plotted to avoid overlapping labels.
11916 * @private
11917 */
11918 _autoSkip: function(ticks) {
11919 var skipRatio;
11920 var me = this;
11921 var isHorizontal = me.isHorizontal();
11922 var optionTicks = me.options.ticks.minor;
11923 var tickCount = ticks.length;
11924 var labelRotationRadians = helpers.toRadians(me.labelRotation);
11925 var cosRotation = Math.cos(labelRotationRadians);
11926 var longestRotatedLabel = me.longestLabelWidth * cosRotation;
11927 var result = [];
11928 var i, tick, shouldSkip;
11929
11930 // figure out the maximum number of gridlines to show
11931 var maxTicks;
11932 if (optionTicks.maxTicksLimit) {
11933 maxTicks = optionTicks.maxTicksLimit;
11934 }
11935
11936 if (isHorizontal) {
11937 skipRatio = false;
11938
11939 if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
11940 skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
11941 }
11942
11943 // if they defined a max number of optionTicks,
11944 // increase skipRatio until that number is met
11945 if (maxTicks && tickCount > maxTicks) {
11946 skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
11947 }
11948 }
11949
11950 for (i = 0; i < tickCount; i++) {
11951 tick = ticks[i];
11952
11953 // Since we always show the last tick,we need may need to hide the last shown one before
11954 shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
11955 if (shouldSkip && i !== tickCount - 1 || helpers.isNullOrUndef(tick.label)) {
11956 // leave tick in place but make sure it's not displayed (#4635)
11957 delete tick.label;
11958 }
11959 result.push(tick);
11960 }
11961 return result;
11962 },
11963
11964 // Actually draw the scale on the canvas
11965 // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
11966 draw: function(chartArea) {
11967 var me = this;
11968 var options = me.options;
11969 if (!options.display) {
11970 return;
11971 }
11972
11973 var context = me.ctx;
11974 var globalDefaults = defaults.global;
11975 var optionTicks = options.ticks.minor;
11976 var optionMajorTicks = options.ticks.major || optionTicks;
11977 var gridLines = options.gridLines;
11978 var scaleLabel = options.scaleLabel;
11979
11980 var isRotated = me.labelRotation !== 0;
11981 var isHorizontal = me.isHorizontal();
11982
11983 var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
11984 var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
11985 var tickFont = parseFontOptions(optionTicks);
11986 var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
11987 var majorTickFont = parseFontOptions(optionMajorTicks);
11988
11989 var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
11990
11991 var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
11992 var scaleLabelFont = parseFontOptions(scaleLabel);
11993 var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
11994 var labelRotationRadians = helpers.toRadians(me.labelRotation);
11995
11996 var itemsToDraw = [];
11997
11998 var xTickStart = options.position === 'right' ? me.left : me.right - tl;
11999 var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
12000 var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
12001 var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;
12002
12003 helpers.each(ticks, function(tick, index) {
12004 // autoskipper skipped this tick (#4635)
12005 if (tick.label === undefined) {
12006 return;
12007 }
12008
12009 var label = tick.label;
12010 var lineWidth, lineColor, borderDash, borderDashOffset;
12011 if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
12012 // Draw the first index specially
12013 lineWidth = gridLines.zeroLineWidth;
12014 lineColor = gridLines.zeroLineColor;
12015 borderDash = gridLines.zeroLineBorderDash;
12016 borderDashOffset = gridLines.zeroLineBorderDashOffset;
12017 } else {
12018 lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
12019 lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
12020 borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
12021 borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
12022 }
12023
12024 // Common properties
12025 var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
12026 var textAlign = 'middle';
12027 var textBaseline = 'middle';
12028 var tickPadding = optionTicks.padding;
12029
12030 if (isHorizontal) {
12031 var labelYOffset = tl + tickPadding;
12032
12033 if (options.position === 'bottom') {
12034 // bottom
12035 textBaseline = !isRotated ? 'top' : 'middle';
12036 textAlign = !isRotated ? 'center' : 'right';
12037 labelY = me.top + labelYOffset;
12038 } else {
12039 // top
12040 textBaseline = !isRotated ? 'bottom' : 'middle';
12041 textAlign = !isRotated ? 'center' : 'left';
12042 labelY = me.bottom - labelYOffset;
12043 }
12044
12045 var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
12046 if (xLineValue < me.left) {
12047 lineColor = 'rgba(0,0,0,0)';
12048 }
12049 xLineValue += helpers.aliasPixel(lineWidth);
12050
12051 labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
12052
12053 tx1 = tx2 = x1 = x2 = xLineValue;
12054 ty1 = yTickStart;
12055 ty2 = yTickEnd;
12056 y1 = chartArea.top;
12057 y2 = chartArea.bottom;
12058 } else {
12059 var isLeft = options.position === 'left';
12060 var labelXOffset;
12061
12062 if (optionTicks.mirror) {
12063 textAlign = isLeft ? 'left' : 'right';
12064 labelXOffset = tickPadding;
12065 } else {
12066 textAlign = isLeft ? 'right' : 'left';
12067 labelXOffset = tl + tickPadding;
12068 }
12069
12070 labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
12071
12072 var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
12073 if (yLineValue < me.top) {
12074 lineColor = 'rgba(0,0,0,0)';
12075 }
12076 yLineValue += helpers.aliasPixel(lineWidth);
12077
12078 labelY = me.getPixelForTick(index) + optionTicks.labelOffset;
12079
12080 tx1 = xTickStart;
12081 tx2 = xTickEnd;
12082 x1 = chartArea.left;
12083 x2 = chartArea.right;
12084 ty1 = ty2 = y1 = y2 = yLineValue;
12085 }
12086
12087 itemsToDraw.push({
12088 tx1: tx1,
12089 ty1: ty1,
12090 tx2: tx2,
12091 ty2: ty2,
12092 x1: x1,
12093 y1: y1,
12094 x2: x2,
12095 y2: y2,
12096 labelX: labelX,
12097 labelY: labelY,
12098 glWidth: lineWidth,
12099 glColor: lineColor,
12100 glBorderDash: borderDash,
12101 glBorderDashOffset: borderDashOffset,
12102 rotation: -1 * labelRotationRadians,
12103 label: label,
12104 major: tick.major,
12105 textBaseline: textBaseline,
12106 textAlign: textAlign
12107 });
12108 });
12109
12110 // Draw all of the tick labels, tick marks, and grid lines at the correct places
12111 helpers.each(itemsToDraw, function(itemToDraw) {
12112 if (gridLines.display) {
12113 context.save();
12114 context.lineWidth = itemToDraw.glWidth;
12115 context.strokeStyle = itemToDraw.glColor;
12116 if (context.setLineDash) {
12117 context.setLineDash(itemToDraw.glBorderDash);
12118 context.lineDashOffset = itemToDraw.glBorderDashOffset;
12119 }
12120
12121 context.beginPath();
12122
12123 if (gridLines.drawTicks) {
12124 context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
12125 context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
12126 }
12127
12128 if (gridLines.drawOnChartArea) {
12129 context.moveTo(itemToDraw.x1, itemToDraw.y1);
12130 context.lineTo(itemToDraw.x2, itemToDraw.y2);
12131 }
12132
12133 context.stroke();
12134 context.restore();
12135 }
12136
12137 if (optionTicks.display) {
12138 // Make sure we draw text in the correct color and font
12139 context.save();
12140 context.translate(itemToDraw.labelX, itemToDraw.labelY);
12141 context.rotate(itemToDraw.rotation);
12142 context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
12143 context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
12144 context.textBaseline = itemToDraw.textBaseline;
12145 context.textAlign = itemToDraw.textAlign;
12146
12147 var label = itemToDraw.label;
12148 if (helpers.isArray(label)) {
12149 for (var i = 0, y = 0; i < label.length; ++i) {
12150 // We just make sure the multiline element is a string here..
12151 context.fillText('' + label[i], 0, y);
12152 // apply same lineSpacing as calculated @ L#320
12153 y += (tickFont.size * 1.5);
12154 }
12155 } else {
12156 context.fillText(label, 0, 0);
12157 }
12158 context.restore();
12159 }
12160 });
12161
12162 if (scaleLabel.display) {
12163 // Draw the scale label
12164 var scaleLabelX;
12165 var scaleLabelY;
12166 var rotation = 0;
12167 var halfLineHeight = parseLineHeight(scaleLabel) / 2;
12168
12169 if (isHorizontal) {
12170 scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
12171 scaleLabelY = options.position === 'bottom'
12172 ? me.bottom - halfLineHeight - scaleLabelPadding.bottom
12173 : me.top + halfLineHeight + scaleLabelPadding.top;
12174 } else {
12175 var isLeft = options.position === 'left';
12176 scaleLabelX = isLeft
12177 ? me.left + halfLineHeight + scaleLabelPadding.top
12178 : me.right - halfLineHeight - scaleLabelPadding.top;
12179 scaleLabelY = me.top + ((me.bottom - me.top) / 2);
12180 rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
12181 }
12182
12183 context.save();
12184 context.translate(scaleLabelX, scaleLabelY);
12185 context.rotate(rotation);
12186 context.textAlign = 'center';
12187 context.textBaseline = 'middle';
12188 context.fillStyle = scaleLabelFontColor; // render in correct colour
12189 context.font = scaleLabelFont.font;
12190 context.fillText(scaleLabel.labelString, 0, 0);
12191 context.restore();
12192 }
12193
12194 if (gridLines.drawBorder) {
12195 // Draw the line at the edge of the axis
12196 context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
12197 context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
12198 var x1 = me.left;
12199 var x2 = me.right;
12200 var y1 = me.top;
12201 var y2 = me.bottom;
12202
12203 var aliasPixel = helpers.aliasPixel(context.lineWidth);
12204 if (isHorizontal) {
12205 y1 = y2 = options.position === 'top' ? me.bottom : me.top;
12206 y1 += aliasPixel;
12207 y2 += aliasPixel;
12208 } else {
12209 x1 = x2 = options.position === 'left' ? me.right : me.left;
12210 x1 += aliasPixel;
12211 x2 += aliasPixel;
12212 }
12213
12214 context.beginPath();
12215 context.moveTo(x1, y1);
12216 context.lineTo(x2, y2);
12217 context.stroke();
12218 }
12219 }
12220 });
12221 };
12222
12223 },{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){
12224 'use strict';
12225
12226 var defaults = require(25);
12227 var helpers = require(45);
12228
12229 module.exports = function(Chart) {
12230
12231 Chart.scaleService = {
12232 // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
12233 // use the new chart options to grab the correct scale
12234 constructors: {},
12235 // Use a registration function so that we can move to an ES6 map when we no longer need to support
12236 // old browsers
12237
12238 // Scale config defaults
12239 defaults: {},
12240 registerScaleType: function(type, scaleConstructor, scaleDefaults) {
12241 this.constructors[type] = scaleConstructor;
12242 this.defaults[type] = helpers.clone(scaleDefaults);
12243 },
12244 getScaleConstructor: function(type) {
12245 return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
12246 },
12247 getScaleDefaults: function(type) {
12248 // Return the scale defaults merged with the global settings so that we always use the latest ones
12249 return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
12250 },
12251 updateScaleDefaults: function(type, additions) {
12252 var me = this;
12253 if (me.defaults.hasOwnProperty(type)) {
12254 me.defaults[type] = helpers.extend(me.defaults[type], additions);
12255 }
12256 },
12257 addScalesToLayout: function(chart) {
12258 // Adds each scale to the chart.boxes array to be sized accordingly
12259 helpers.each(chart.scales, function(scale) {
12260 // Set ILayoutItem parameters for backwards compatibility
12261 scale.fullWidth = scale.options.fullWidth;
12262 scale.position = scale.options.position;
12263 scale.weight = scale.options.weight;
12264 Chart.layoutService.addBox(chart, scale);
12265 });
12266 }
12267 };
12268 };
12269
12270 },{"25":25,"45":45}],34:[function(require,module,exports){
12271 'use strict';
12272
12273 var helpers = require(45);
12274
12275 /**
12276 * Namespace to hold static tick generation functions
12277 * @namespace Chart.Ticks
12278 */
12279 module.exports = {
12280 /**
12281 * Namespace to hold generators for different types of ticks
12282 * @namespace Chart.Ticks.generators
12283 */
12284 generators: {
12285 /**
12286 * Interface for the options provided to the numeric tick generator
12287 * @interface INumericTickGenerationOptions
12288 */
12289 /**
12290 * The maximum number of ticks to display
12291 * @name INumericTickGenerationOptions#maxTicks
12292 * @type Number
12293 */
12294 /**
12295 * The distance between each tick.
12296 * @name INumericTickGenerationOptions#stepSize
12297 * @type Number
12298 * @optional
12299 */
12300 /**
12301 * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
12302 * @name INumericTickGenerationOptions#min
12303 * @type Number
12304 * @optional
12305 */
12306 /**
12307 * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
12308 * @name INumericTickGenerationOptions#max
12309 * @type Number
12310 * @optional
12311 */
12312
12313 /**
12314 * Generate a set of linear ticks
12315 * @method Chart.Ticks.generators.linear
12316 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
12317 * @param dataRange {IRange} the range of the data
12318 * @returns {Array<Number>} array of tick values
12319 */
12320 linear: function(generationOptions, dataRange) {
12321 var ticks = [];
12322 // To get a "nice" value for the tick spacing, we will use the appropriately named
12323 // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
12324 // for details.
12325
12326 var spacing;
12327 if (generationOptions.stepSize && generationOptions.stepSize > 0) {
12328 spacing = generationOptions.stepSize;
12329 } else {
12330 var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
12331 spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
12332 }
12333 var niceMin = Math.floor(dataRange.min / spacing) * spacing;
12334 var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
12335
12336 // If min, max and stepSize is set and they make an evenly spaced scale use it.
12337 if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
12338 // If very close to our whole number, use it.
12339 if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
12340 niceMin = generationOptions.min;
12341 niceMax = generationOptions.max;
12342 }
12343 }
12344
12345 var numSpaces = (niceMax - niceMin) / spacing;
12346 // If very close to our rounded value, use it.
12347 if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
12348 numSpaces = Math.round(numSpaces);
12349 } else {
12350 numSpaces = Math.ceil(numSpaces);
12351 }
12352
12353 // Put the values into the ticks array
12354 ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
12355 for (var j = 1; j < numSpaces; ++j) {
12356 ticks.push(niceMin + (j * spacing));
12357 }
12358 ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
12359
12360 return ticks;
12361 },
12362
12363 /**
12364 * Generate a set of logarithmic ticks
12365 * @method Chart.Ticks.generators.logarithmic
12366 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
12367 * @param dataRange {IRange} the range of the data
12368 * @returns {Array<Number>} array of tick values
12369 */
12370 logarithmic: function(generationOptions, dataRange) {
12371 var ticks = [];
12372 var valueOrDefault = helpers.valueOrDefault;
12373
12374 // Figure out what the max number of ticks we can support it is based on the size of
12375 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12376 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12377 // the graph
12378 var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
12379
12380 var endExp = Math.floor(helpers.log10(dataRange.max));
12381 var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
12382 var exp, significand;
12383
12384 if (tickVal === 0) {
12385 exp = Math.floor(helpers.log10(dataRange.minNotZero));
12386 significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
12387
12388 ticks.push(tickVal);
12389 tickVal = significand * Math.pow(10, exp);
12390 } else {
12391 exp = Math.floor(helpers.log10(tickVal));
12392 significand = Math.floor(tickVal / Math.pow(10, exp));
12393 }
12394
12395 do {
12396 ticks.push(tickVal);
12397
12398 ++significand;
12399 if (significand === 10) {
12400 significand = 1;
12401 ++exp;
12402 }
12403
12404 tickVal = significand * Math.pow(10, exp);
12405 } while (exp < endExp || (exp === endExp && significand < endSignificand));
12406
12407 var lastTick = valueOrDefault(generationOptions.max, tickVal);
12408 ticks.push(lastTick);
12409
12410 return ticks;
12411 }
12412 },
12413
12414 /**
12415 * Namespace to hold formatters for different types of ticks
12416 * @namespace Chart.Ticks.formatters
12417 */
12418 formatters: {
12419 /**
12420 * Formatter for value labels
12421 * @method Chart.Ticks.formatters.values
12422 * @param value the value to display
12423 * @return {String|Array} the label to display
12424 */
12425 values: function(value) {
12426 return helpers.isArray(value) ? value : '' + value;
12427 },
12428
12429 /**
12430 * Formatter for linear numeric ticks
12431 * @method Chart.Ticks.formatters.linear
12432 * @param tickValue {Number} the value to be formatted
12433 * @param index {Number} the position of the tickValue parameter in the ticks array
12434 * @param ticks {Array<Number>} the list of ticks being converted
12435 * @return {String} string representation of the tickValue parameter
12436 */
12437 linear: function(tickValue, index, ticks) {
12438 // If we have lots of ticks, don't use the ones
12439 var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
12440
12441 // If we have a number like 2.5 as the delta, figure out how many decimal places we need
12442 if (Math.abs(delta) > 1) {
12443 if (tickValue !== Math.floor(tickValue)) {
12444 // not an integer
12445 delta = tickValue - Math.floor(tickValue);
12446 }
12447 }
12448
12449 var logDelta = helpers.log10(Math.abs(delta));
12450 var tickString = '';
12451
12452 if (tickValue !== 0) {
12453 var numDecimal = -1 * Math.floor(logDelta);
12454 numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
12455 tickString = tickValue.toFixed(numDecimal);
12456 } else {
12457 tickString = '0'; // never show decimal places for 0
12458 }
12459
12460 return tickString;
12461 },
12462
12463 logarithmic: function(tickValue, index, ticks) {
12464 var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
12465
12466 if (tickValue === 0) {
12467 return '0';
12468 } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
12469 return tickValue.toExponential();
12470 }
12471 return '';
12472 }
12473 }
12474 };
12475
12476 },{"45":45}],35:[function(require,module,exports){
12477 'use strict';
12478
12479 var defaults = require(25);
12480 var Element = require(26);
12481 var helpers = require(45);
12482
12483 defaults._set('global', {
12484 tooltips: {
12485 enabled: true,
12486 custom: null,
12487 mode: 'nearest',
12488 position: 'average',
12489 intersect: true,
12490 backgroundColor: 'rgba(0,0,0,0.8)',
12491 titleFontStyle: 'bold',
12492 titleSpacing: 2,
12493 titleMarginBottom: 6,
12494 titleFontColor: '#fff',
12495 titleAlign: 'left',
12496 bodySpacing: 2,
12497 bodyFontColor: '#fff',
12498 bodyAlign: 'left',
12499 footerFontStyle: 'bold',
12500 footerSpacing: 2,
12501 footerMarginTop: 6,
12502 footerFontColor: '#fff',
12503 footerAlign: 'left',
12504 yPadding: 6,
12505 xPadding: 6,
12506 caretPadding: 2,
12507 caretSize: 5,
12508 cornerRadius: 6,
12509 multiKeyBackground: '#fff',
12510 displayColors: true,
12511 borderColor: 'rgba(0,0,0,0)',
12512 borderWidth: 0,
12513 callbacks: {
12514 // Args are: (tooltipItems, data)
12515 beforeTitle: helpers.noop,
12516 title: function(tooltipItems, data) {
12517 // Pick first xLabel for now
12518 var title = '';
12519 var labels = data.labels;
12520 var labelCount = labels ? labels.length : 0;
12521
12522 if (tooltipItems.length > 0) {
12523 var item = tooltipItems[0];
12524
12525 if (item.xLabel) {
12526 title = item.xLabel;
12527 } else if (labelCount > 0 && item.index < labelCount) {
12528 title = labels[item.index];
12529 }
12530 }
12531
12532 return title;
12533 },
12534 afterTitle: helpers.noop,
12535
12536 // Args are: (tooltipItems, data)
12537 beforeBody: helpers.noop,
12538
12539 // Args are: (tooltipItem, data)
12540 beforeLabel: helpers.noop,
12541 label: function(tooltipItem, data) {
12542 var label = data.datasets[tooltipItem.datasetIndex].label || '';
12543
12544 if (label) {
12545 label += ': ';
12546 }
12547 label += tooltipItem.yLabel;
12548 return label;
12549 },
12550 labelColor: function(tooltipItem, chart) {
12551 var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
12552 var activeElement = meta.data[tooltipItem.index];
12553 var view = activeElement._view;
12554 return {
12555 borderColor: view.borderColor,
12556 backgroundColor: view.backgroundColor
12557 };
12558 },
12559 labelTextColor: function() {
12560 return this._options.bodyFontColor;
12561 },
12562 afterLabel: helpers.noop,
12563
12564 // Args are: (tooltipItems, data)
12565 afterBody: helpers.noop,
12566
12567 // Args are: (tooltipItems, data)
12568 beforeFooter: helpers.noop,
12569 footer: helpers.noop,
12570 afterFooter: helpers.noop
12571 }
12572 }
12573 });
12574
12575 module.exports = function(Chart) {
12576
12577 /**
12578 * Helper method to merge the opacity into a color
12579 */
12580 function mergeOpacity(colorString, opacity) {
12581 var color = helpers.color(colorString);
12582 return color.alpha(opacity * color.alpha()).rgbaString();
12583 }
12584
12585 // Helper to push or concat based on if the 2nd parameter is an array or not
12586 function pushOrConcat(base, toPush) {
12587 if (toPush) {
12588 if (helpers.isArray(toPush)) {
12589 // base = base.concat(toPush);
12590 Array.prototype.push.apply(base, toPush);
12591 } else {
12592 base.push(toPush);
12593 }
12594 }
12595
12596 return base;
12597 }
12598
12599 // Private helper to create a tooltip item model
12600 // @param element : the chart element (point, arc, bar) to create the tooltip item for
12601 // @return : new tooltip item
12602 function createTooltipItem(element) {
12603 var xScale = element._xScale;
12604 var yScale = element._yScale || element._scale; // handle radar || polarArea charts
12605 var index = element._index;
12606 var datasetIndex = element._datasetIndex;
12607
12608 return {
12609 xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
12610 yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
12611 index: index,
12612 datasetIndex: datasetIndex,
12613 x: element._model.x,
12614 y: element._model.y
12615 };
12616 }
12617
12618 /**
12619 * Helper to get the reset model for the tooltip
12620 * @param tooltipOpts {Object} the tooltip options
12621 */
12622 function getBaseModel(tooltipOpts) {
12623 var globalDefaults = defaults.global;
12624 var valueOrDefault = helpers.valueOrDefault;
12625
12626 return {
12627 // Positioning
12628 xPadding: tooltipOpts.xPadding,
12629 yPadding: tooltipOpts.yPadding,
12630 xAlign: tooltipOpts.xAlign,
12631 yAlign: tooltipOpts.yAlign,
12632
12633 // Body
12634 bodyFontColor: tooltipOpts.bodyFontColor,
12635 _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
12636 _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
12637 _bodyAlign: tooltipOpts.bodyAlign,
12638 bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
12639 bodySpacing: tooltipOpts.bodySpacing,
12640
12641 // Title
12642 titleFontColor: tooltipOpts.titleFontColor,
12643 _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
12644 _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
12645 titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
12646 _titleAlign: tooltipOpts.titleAlign,
12647 titleSpacing: tooltipOpts.titleSpacing,
12648 titleMarginBottom: tooltipOpts.titleMarginBottom,
12649
12650 // Footer
12651 footerFontColor: tooltipOpts.footerFontColor,
12652 _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
12653 _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
12654 footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
12655 _footerAlign: tooltipOpts.footerAlign,
12656 footerSpacing: tooltipOpts.footerSpacing,
12657 footerMarginTop: tooltipOpts.footerMarginTop,
12658
12659 // Appearance
12660 caretSize: tooltipOpts.caretSize,
12661 cornerRadius: tooltipOpts.cornerRadius,
12662 backgroundColor: tooltipOpts.backgroundColor,
12663 opacity: 0,
12664 legendColorBackground: tooltipOpts.multiKeyBackground,
12665 displayColors: tooltipOpts.displayColors,
12666 borderColor: tooltipOpts.borderColor,
12667 borderWidth: tooltipOpts.borderWidth
12668 };
12669 }
12670
12671 /**
12672 * Get the size of the tooltip
12673 */
12674 function getTooltipSize(tooltip, model) {
12675 var ctx = tooltip._chart.ctx;
12676
12677 var height = model.yPadding * 2; // Tooltip Padding
12678 var width = 0;
12679
12680 // Count of all lines in the body
12681 var body = model.body;
12682 var combinedBodyLength = body.reduce(function(count, bodyItem) {
12683 return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
12684 }, 0);
12685 combinedBodyLength += model.beforeBody.length + model.afterBody.length;
12686
12687 var titleLineCount = model.title.length;
12688 var footerLineCount = model.footer.length;
12689 var titleFontSize = model.titleFontSize;
12690 var bodyFontSize = model.bodyFontSize;
12691 var footerFontSize = model.footerFontSize;
12692
12693 height += titleLineCount * titleFontSize; // Title Lines
12694 height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
12695 height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
12696 height += combinedBodyLength * bodyFontSize; // Body Lines
12697 height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
12698 height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
12699 height += footerLineCount * (footerFontSize); // Footer Lines
12700 height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
12701
12702 // Title width
12703 var widthPadding = 0;
12704 var maxLineWidth = function(line) {
12705 width = Math.max(width, ctx.measureText(line).width + widthPadding);
12706 };
12707
12708 ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
12709 helpers.each(model.title, maxLineWidth);
12710
12711 // Body width
12712 ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
12713 helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
12714
12715 // Body lines may include some extra width due to the color box
12716 widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
12717 helpers.each(body, function(bodyItem) {
12718 helpers.each(bodyItem.before, maxLineWidth);
12719 helpers.each(bodyItem.lines, maxLineWidth);
12720 helpers.each(bodyItem.after, maxLineWidth);
12721 });
12722
12723 // Reset back to 0
12724 widthPadding = 0;
12725
12726 // Footer width
12727 ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
12728 helpers.each(model.footer, maxLineWidth);
12729
12730 // Add padding
12731 width += 2 * model.xPadding;
12732
12733 return {
12734 width: width,
12735 height: height
12736 };
12737 }
12738
12739 /**
12740 * Helper to get the alignment of a tooltip given the size
12741 */
12742 function determineAlignment(tooltip, size) {
12743 var model = tooltip._model;
12744 var chart = tooltip._chart;
12745 var chartArea = tooltip._chart.chartArea;
12746 var xAlign = 'center';
12747 var yAlign = 'center';
12748
12749 if (model.y < size.height) {
12750 yAlign = 'top';
12751 } else if (model.y > (chart.height - size.height)) {
12752 yAlign = 'bottom';
12753 }
12754
12755 var lf, rf; // functions to determine left, right alignment
12756 var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
12757 var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
12758 var midX = (chartArea.left + chartArea.right) / 2;
12759 var midY = (chartArea.top + chartArea.bottom) / 2;
12760
12761 if (yAlign === 'center') {
12762 lf = function(x) {
12763 return x <= midX;
12764 };
12765 rf = function(x) {
12766 return x > midX;
12767 };
12768 } else {
12769 lf = function(x) {
12770 return x <= (size.width / 2);
12771 };
12772 rf = function(x) {
12773 return x >= (chart.width - (size.width / 2));
12774 };
12775 }
12776
12777 olf = function(x) {
12778 return x + size.width > chart.width;
12779 };
12780 orf = function(x) {
12781 return x - size.width < 0;
12782 };
12783 yf = function(y) {
12784 return y <= midY ? 'top' : 'bottom';
12785 };
12786
12787 if (lf(model.x)) {
12788 xAlign = 'left';
12789
12790 // Is tooltip too wide and goes over the right side of the chart.?
12791 if (olf(model.x)) {
12792 xAlign = 'center';
12793 yAlign = yf(model.y);
12794 }
12795 } else if (rf(model.x)) {
12796 xAlign = 'right';
12797
12798 // Is tooltip too wide and goes outside left edge of canvas?
12799 if (orf(model.x)) {
12800 xAlign = 'center';
12801 yAlign = yf(model.y);
12802 }
12803 }
12804
12805 var opts = tooltip._options;
12806 return {
12807 xAlign: opts.xAlign ? opts.xAlign : xAlign,
12808 yAlign: opts.yAlign ? opts.yAlign : yAlign
12809 };
12810 }
12811
12812 /**
12813 * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
12814 */
12815 function getBackgroundPoint(vm, size, alignment) {
12816 // Background Position
12817 var x = vm.x;
12818 var y = vm.y;
12819
12820 var caretSize = vm.caretSize;
12821 var caretPadding = vm.caretPadding;
12822 var cornerRadius = vm.cornerRadius;
12823 var xAlign = alignment.xAlign;
12824 var yAlign = alignment.yAlign;
12825 var paddingAndSize = caretSize + caretPadding;
12826 var radiusAndPadding = cornerRadius + caretPadding;
12827
12828 if (xAlign === 'right') {
12829 x -= size.width;
12830 } else if (xAlign === 'center') {
12831 x -= (size.width / 2);
12832 }
12833
12834 if (yAlign === 'top') {
12835 y += paddingAndSize;
12836 } else if (yAlign === 'bottom') {
12837 y -= size.height + paddingAndSize;
12838 } else {
12839 y -= (size.height / 2);
12840 }
12841
12842 if (yAlign === 'center') {
12843 if (xAlign === 'left') {
12844 x += paddingAndSize;
12845 } else if (xAlign === 'right') {
12846 x -= paddingAndSize;
12847 }
12848 } else if (xAlign === 'left') {
12849 x -= radiusAndPadding;
12850 } else if (xAlign === 'right') {
12851 x += radiusAndPadding;
12852 }
12853
12854 return {
12855 x: x,
12856 y: y
12857 };
12858 }
12859
12860 Chart.Tooltip = Element.extend({
12861 initialize: function() {
12862 this._model = getBaseModel(this._options);
12863 },
12864
12865 // Get the title
12866 // Args are: (tooltipItem, data)
12867 getTitle: function() {
12868 var me = this;
12869 var opts = me._options;
12870 var callbacks = opts.callbacks;
12871
12872 var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
12873 var title = callbacks.title.apply(me, arguments);
12874 var afterTitle = callbacks.afterTitle.apply(me, arguments);
12875
12876 var lines = [];
12877 lines = pushOrConcat(lines, beforeTitle);
12878 lines = pushOrConcat(lines, title);
12879 lines = pushOrConcat(lines, afterTitle);
12880
12881 return lines;
12882 },
12883
12884 // Args are: (tooltipItem, data)
12885 getBeforeBody: function() {
12886 var lines = this._options.callbacks.beforeBody.apply(this, arguments);
12887 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
12888 },
12889
12890 // Args are: (tooltipItem, data)
12891 getBody: function(tooltipItems, data) {
12892 var me = this;
12893 var callbacks = me._options.callbacks;
12894 var bodyItems = [];
12895
12896 helpers.each(tooltipItems, function(tooltipItem) {
12897 var bodyItem = {
12898 before: [],
12899 lines: [],
12900 after: []
12901 };
12902 pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
12903 pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
12904 pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));
12905
12906 bodyItems.push(bodyItem);
12907 });
12908
12909 return bodyItems;
12910 },
12911
12912 // Args are: (tooltipItem, data)
12913 getAfterBody: function() {
12914 var lines = this._options.callbacks.afterBody.apply(this, arguments);
12915 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
12916 },
12917
12918 // Get the footer and beforeFooter and afterFooter lines
12919 // Args are: (tooltipItem, data)
12920 getFooter: function() {
12921 var me = this;
12922 var callbacks = me._options.callbacks;
12923
12924 var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
12925 var footer = callbacks.footer.apply(me, arguments);
12926 var afterFooter = callbacks.afterFooter.apply(me, arguments);
12927
12928 var lines = [];
12929 lines = pushOrConcat(lines, beforeFooter);
12930 lines = pushOrConcat(lines, footer);
12931 lines = pushOrConcat(lines, afterFooter);
12932
12933 return lines;
12934 },
12935
12936 update: function(changed) {
12937 var me = this;
12938 var opts = me._options;
12939
12940 // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
12941 // 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
12942 // which breaks any animations.
12943 var existingModel = me._model;
12944 var model = me._model = getBaseModel(opts);
12945 var active = me._active;
12946
12947 var data = me._data;
12948
12949 // In the case where active.length === 0 we need to keep these at existing values for good animations
12950 var alignment = {
12951 xAlign: existingModel.xAlign,
12952 yAlign: existingModel.yAlign
12953 };
12954 var backgroundPoint = {
12955 x: existingModel.x,
12956 y: existingModel.y
12957 };
12958 var tooltipSize = {
12959 width: existingModel.width,
12960 height: existingModel.height
12961 };
12962 var tooltipPosition = {
12963 x: existingModel.caretX,
12964 y: existingModel.caretY
12965 };
12966
12967 var i, len;
12968
12969 if (active.length) {
12970 model.opacity = 1;
12971
12972 var labelColors = [];
12973 var labelTextColors = [];
12974 tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);
12975
12976 var tooltipItems = [];
12977 for (i = 0, len = active.length; i < len; ++i) {
12978 tooltipItems.push(createTooltipItem(active[i]));
12979 }
12980
12981 // If the user provided a filter function, use it to modify the tooltip items
12982 if (opts.filter) {
12983 tooltipItems = tooltipItems.filter(function(a) {
12984 return opts.filter(a, data);
12985 });
12986 }
12987
12988 // If the user provided a sorting function, use it to modify the tooltip items
12989 if (opts.itemSort) {
12990 tooltipItems = tooltipItems.sort(function(a, b) {
12991 return opts.itemSort(a, b, data);
12992 });
12993 }
12994
12995 // Determine colors for boxes
12996 helpers.each(tooltipItems, function(tooltipItem) {
12997 labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
12998 labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
12999 });
13000
13001
13002 // Build the Text Lines
13003 model.title = me.getTitle(tooltipItems, data);
13004 model.beforeBody = me.getBeforeBody(tooltipItems, data);
13005 model.body = me.getBody(tooltipItems, data);
13006 model.afterBody = me.getAfterBody(tooltipItems, data);
13007 model.footer = me.getFooter(tooltipItems, data);
13008
13009 // Initial positioning and colors
13010 model.x = Math.round(tooltipPosition.x);
13011 model.y = Math.round(tooltipPosition.y);
13012 model.caretPadding = opts.caretPadding;
13013 model.labelColors = labelColors;
13014 model.labelTextColors = labelTextColors;
13015
13016 // data points
13017 model.dataPoints = tooltipItems;
13018
13019 // We need to determine alignment of the tooltip
13020 tooltipSize = getTooltipSize(this, model);
13021 alignment = determineAlignment(this, tooltipSize);
13022 // Final Size and Position
13023 backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
13024 } else {
13025 model.opacity = 0;
13026 }
13027
13028 model.xAlign = alignment.xAlign;
13029 model.yAlign = alignment.yAlign;
13030 model.x = backgroundPoint.x;
13031 model.y = backgroundPoint.y;
13032 model.width = tooltipSize.width;
13033 model.height = tooltipSize.height;
13034
13035 // Point where the caret on the tooltip points to
13036 model.caretX = tooltipPosition.x;
13037 model.caretY = tooltipPosition.y;
13038
13039 me._model = model;
13040
13041 if (changed && opts.custom) {
13042 opts.custom.call(me, model);
13043 }
13044
13045 return me;
13046 },
13047 drawCaret: function(tooltipPoint, size) {
13048 var ctx = this._chart.ctx;
13049 var vm = this._view;
13050 var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
13051
13052 ctx.lineTo(caretPosition.x1, caretPosition.y1);
13053 ctx.lineTo(caretPosition.x2, caretPosition.y2);
13054 ctx.lineTo(caretPosition.x3, caretPosition.y3);
13055 },
13056 getCaretPosition: function(tooltipPoint, size, vm) {
13057 var x1, x2, x3, y1, y2, y3;
13058 var caretSize = vm.caretSize;
13059 var cornerRadius = vm.cornerRadius;
13060 var xAlign = vm.xAlign;
13061 var yAlign = vm.yAlign;
13062 var ptX = tooltipPoint.x;
13063 var ptY = tooltipPoint.y;
13064 var width = size.width;
13065 var height = size.height;
13066
13067 if (yAlign === 'center') {
13068 y2 = ptY + (height / 2);
13069
13070 if (xAlign === 'left') {
13071 x1 = ptX;
13072 x2 = x1 - caretSize;
13073 x3 = x1;
13074
13075 y1 = y2 + caretSize;
13076 y3 = y2 - caretSize;
13077 } else {
13078 x1 = ptX + width;
13079 x2 = x1 + caretSize;
13080 x3 = x1;
13081
13082 y1 = y2 - caretSize;
13083 y3 = y2 + caretSize;
13084 }
13085 } else {
13086 if (xAlign === 'left') {
13087 x2 = ptX + cornerRadius + (caretSize);
13088 x1 = x2 - caretSize;
13089 x3 = x2 + caretSize;
13090 } else if (xAlign === 'right') {
13091 x2 = ptX + width - cornerRadius - caretSize;
13092 x1 = x2 - caretSize;
13093 x3 = x2 + caretSize;
13094 } else {
13095 x2 = ptX + (width / 2);
13096 x1 = x2 - caretSize;
13097 x3 = x2 + caretSize;
13098 }
13099 if (yAlign === 'top') {
13100 y1 = ptY;
13101 y2 = y1 - caretSize;
13102 y3 = y1;
13103 } else {
13104 y1 = ptY + height;
13105 y2 = y1 + caretSize;
13106 y3 = y1;
13107 // invert drawing order
13108 var tmp = x3;
13109 x3 = x1;
13110 x1 = tmp;
13111 }
13112 }
13113 return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
13114 },
13115 drawTitle: function(pt, vm, ctx, opacity) {
13116 var title = vm.title;
13117
13118 if (title.length) {
13119 ctx.textAlign = vm._titleAlign;
13120 ctx.textBaseline = 'top';
13121
13122 var titleFontSize = vm.titleFontSize;
13123 var titleSpacing = vm.titleSpacing;
13124
13125 ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
13126 ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
13127
13128 var i, len;
13129 for (i = 0, len = title.length; i < len; ++i) {
13130 ctx.fillText(title[i], pt.x, pt.y);
13131 pt.y += titleFontSize + titleSpacing; // Line Height and spacing
13132
13133 if (i + 1 === title.length) {
13134 pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
13135 }
13136 }
13137 }
13138 },
13139 drawBody: function(pt, vm, ctx, opacity) {
13140 var bodyFontSize = vm.bodyFontSize;
13141 var bodySpacing = vm.bodySpacing;
13142 var body = vm.body;
13143
13144 ctx.textAlign = vm._bodyAlign;
13145 ctx.textBaseline = 'top';
13146 ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
13147
13148 // Before Body
13149 var xLinePadding = 0;
13150 var fillLineOfText = function(line) {
13151 ctx.fillText(line, pt.x + xLinePadding, pt.y);
13152 pt.y += bodyFontSize + bodySpacing;
13153 };
13154
13155 // Before body lines
13156 helpers.each(vm.beforeBody, fillLineOfText);
13157
13158 var drawColorBoxes = vm.displayColors;
13159 xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
13160
13161 // Draw body lines now
13162 helpers.each(body, function(bodyItem, i) {
13163 helpers.each(bodyItem.before, fillLineOfText);
13164
13165 helpers.each(bodyItem.lines, function(line) {
13166 // Draw Legend-like boxes if needed
13167 if (drawColorBoxes) {
13168 // Fill a white rect so that colours merge nicely if the opacity is < 1
13169 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
13170 ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
13171
13172 // Border
13173 ctx.lineWidth = 1;
13174 ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
13175 ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
13176
13177 // Inner square
13178 ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
13179 ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
13180 var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
13181 ctx.fillStyle = textColor;
13182 }
13183
13184 fillLineOfText(line);
13185 });
13186
13187 helpers.each(bodyItem.after, fillLineOfText);
13188 });
13189
13190 // Reset back to 0 for after body
13191 xLinePadding = 0;
13192
13193 // After body lines
13194 helpers.each(vm.afterBody, fillLineOfText);
13195 pt.y -= bodySpacing; // Remove last body spacing
13196 },
13197 drawFooter: function(pt, vm, ctx, opacity) {
13198 var footer = vm.footer;
13199
13200 if (footer.length) {
13201 pt.y += vm.footerMarginTop;
13202
13203 ctx.textAlign = vm._footerAlign;
13204 ctx.textBaseline = 'top';
13205
13206 ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
13207 ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
13208
13209 helpers.each(footer, function(line) {
13210 ctx.fillText(line, pt.x, pt.y);
13211 pt.y += vm.footerFontSize + vm.footerSpacing;
13212 });
13213 }
13214 },
13215 drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
13216 ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
13217 ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
13218 ctx.lineWidth = vm.borderWidth;
13219 var xAlign = vm.xAlign;
13220 var yAlign = vm.yAlign;
13221 var x = pt.x;
13222 var y = pt.y;
13223 var width = tooltipSize.width;
13224 var height = tooltipSize.height;
13225 var radius = vm.cornerRadius;
13226
13227 ctx.beginPath();
13228 ctx.moveTo(x + radius, y);
13229 if (yAlign === 'top') {
13230 this.drawCaret(pt, tooltipSize);
13231 }
13232 ctx.lineTo(x + width - radius, y);
13233 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
13234 if (yAlign === 'center' && xAlign === 'right') {
13235 this.drawCaret(pt, tooltipSize);
13236 }
13237 ctx.lineTo(x + width, y + height - radius);
13238 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
13239 if (yAlign === 'bottom') {
13240 this.drawCaret(pt, tooltipSize);
13241 }
13242 ctx.lineTo(x + radius, y + height);
13243 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
13244 if (yAlign === 'center' && xAlign === 'left') {
13245 this.drawCaret(pt, tooltipSize);
13246 }
13247 ctx.lineTo(x, y + radius);
13248 ctx.quadraticCurveTo(x, y, x + radius, y);
13249 ctx.closePath();
13250
13251 ctx.fill();
13252
13253 if (vm.borderWidth > 0) {
13254 ctx.stroke();
13255 }
13256 },
13257 draw: function() {
13258 var ctx = this._chart.ctx;
13259 var vm = this._view;
13260
13261 if (vm.opacity === 0) {
13262 return;
13263 }
13264
13265 var tooltipSize = {
13266 width: vm.width,
13267 height: vm.height
13268 };
13269 var pt = {
13270 x: vm.x,
13271 y: vm.y
13272 };
13273
13274 // IE11/Edge does not like very small opacities, so snap to 0
13275 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
13276
13277 // Truthy/falsey value for empty tooltip
13278 var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
13279
13280 if (this._options.enabled && hasTooltipContent) {
13281 // Draw Background
13282 this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
13283
13284 // Draw Title, Body, and Footer
13285 pt.x += vm.xPadding;
13286 pt.y += vm.yPadding;
13287
13288 // Titles
13289 this.drawTitle(pt, vm, ctx, opacity);
13290
13291 // Body
13292 this.drawBody(pt, vm, ctx, opacity);
13293
13294 // Footer
13295 this.drawFooter(pt, vm, ctx, opacity);
13296 }
13297 },
13298
13299 /**
13300 * Handle an event
13301 * @private
13302 * @param {IEvent} event - The event to handle
13303 * @returns {Boolean} true if the tooltip changed
13304 */
13305 handleEvent: function(e) {
13306 var me = this;
13307 var options = me._options;
13308 var changed = false;
13309
13310 me._lastActive = me._lastActive || [];
13311
13312 // Find Active Elements for tooltips
13313 if (e.type === 'mouseout') {
13314 me._active = [];
13315 } else {
13316 me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
13317 }
13318
13319 // Remember Last Actives
13320 changed = !helpers.arrayEquals(me._active, me._lastActive);
13321
13322 // If tooltip didn't change, do not handle the target event
13323 if (!changed) {
13324 return false;
13325 }
13326
13327 me._lastActive = me._active;
13328
13329 if (options.enabled || options.custom) {
13330 me._eventPosition = {
13331 x: e.x,
13332 y: e.y
13333 };
13334
13335 var model = me._model;
13336 me.update(true);
13337 me.pivot();
13338
13339 // See if our tooltip position changed
13340 changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
13341 }
13342
13343 return changed;
13344 }
13345 });
13346
13347 /**
13348 * @namespace Chart.Tooltip.positioners
13349 */
13350 Chart.Tooltip.positioners = {
13351 /**
13352 * Average mode places the tooltip at the average position of the elements shown
13353 * @function Chart.Tooltip.positioners.average
13354 * @param elements {ChartElement[]} the elements being displayed in the tooltip
13355 * @returns {Point} tooltip position
13356 */
13357 average: function(elements) {
13358 if (!elements.length) {
13359 return false;
13360 }
13361
13362 var i, len;
13363 var x = 0;
13364 var y = 0;
13365 var count = 0;
13366
13367 for (i = 0, len = elements.length; i < len; ++i) {
13368 var el = elements[i];
13369 if (el && el.hasValue()) {
13370 var pos = el.tooltipPosition();
13371 x += pos.x;
13372 y += pos.y;
13373 ++count;
13374 }
13375 }
13376
13377 return {
13378 x: Math.round(x / count),
13379 y: Math.round(y / count)
13380 };
13381 },
13382
13383 /**
13384 * Gets the tooltip position nearest of the item nearest to the event position
13385 * @function Chart.Tooltip.positioners.nearest
13386 * @param elements {Chart.Element[]} the tooltip elements
13387 * @param eventPosition {Point} the position of the event in canvas coordinates
13388 * @returns {Point} the tooltip position
13389 */
13390 nearest: function(elements, eventPosition) {
13391 var x = eventPosition.x;
13392 var y = eventPosition.y;
13393 var minDistance = Number.POSITIVE_INFINITY;
13394 var i, len, nearestElement;
13395
13396 for (i = 0, len = elements.length; i < len; ++i) {
13397 var el = elements[i];
13398 if (el && el.hasValue()) {
13399 var center = el.getCenterPoint();
13400 var d = helpers.distanceBetweenPoints(eventPosition, center);
13401
13402 if (d < minDistance) {
13403 minDistance = d;
13404 nearestElement = el;
13405 }
13406 }
13407 }
13408
13409 if (nearestElement) {
13410 var tp = nearestElement.tooltipPosition();
13411 x = tp.x;
13412 y = tp.y;
13413 }
13414
13415 return {
13416 x: x,
13417 y: y
13418 };
13419 }
13420 };
13421 };
13422
13423 },{"25":25,"26":26,"45":45}],36:[function(require,module,exports){
13424 'use strict';
13425
13426 var defaults = require(25);
13427 var Element = require(26);
13428 var helpers = require(45);
13429
13430 defaults._set('global', {
13431 elements: {
13432 arc: {
13433 backgroundColor: defaults.global.defaultColor,
13434 borderColor: '#fff',
13435 borderWidth: 2
13436 }
13437 }
13438 });
13439
13440 module.exports = Element.extend({
13441 inLabelRange: function(mouseX) {
13442 var vm = this._view;
13443
13444 if (vm) {
13445 return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
13446 }
13447 return false;
13448 },
13449
13450 inRange: function(chartX, chartY) {
13451 var vm = this._view;
13452
13453 if (vm) {
13454 var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
13455 var angle = pointRelativePosition.angle;
13456 var distance = pointRelativePosition.distance;
13457
13458 // Sanitise angle range
13459 var startAngle = vm.startAngle;
13460 var endAngle = vm.endAngle;
13461 while (endAngle < startAngle) {
13462 endAngle += 2.0 * Math.PI;
13463 }
13464 while (angle > endAngle) {
13465 angle -= 2.0 * Math.PI;
13466 }
13467 while (angle < startAngle) {
13468 angle += 2.0 * Math.PI;
13469 }
13470
13471 // Check if within the range of the open/close angle
13472 var betweenAngles = (angle >= startAngle && angle <= endAngle);
13473 var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
13474
13475 return (betweenAngles && withinRadius);
13476 }
13477 return false;
13478 },
13479
13480 getCenterPoint: function() {
13481 var vm = this._view;
13482 var halfAngle = (vm.startAngle + vm.endAngle) / 2;
13483 var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
13484 return {
13485 x: vm.x + Math.cos(halfAngle) * halfRadius,
13486 y: vm.y + Math.sin(halfAngle) * halfRadius
13487 };
13488 },
13489
13490 getArea: function() {
13491 var vm = this._view;
13492 return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
13493 },
13494
13495 tooltipPosition: function() {
13496 var vm = this._view;
13497 var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
13498 var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
13499
13500 return {
13501 x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
13502 y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
13503 };
13504 },
13505
13506 draw: function() {
13507 var ctx = this._chart.ctx;
13508 var vm = this._view;
13509 var sA = vm.startAngle;
13510 var eA = vm.endAngle;
13511
13512 ctx.beginPath();
13513
13514 ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
13515 ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
13516
13517 ctx.closePath();
13518 ctx.strokeStyle = vm.borderColor;
13519 ctx.lineWidth = vm.borderWidth;
13520
13521 ctx.fillStyle = vm.backgroundColor;
13522
13523 ctx.fill();
13524 ctx.lineJoin = 'bevel';
13525
13526 if (vm.borderWidth) {
13527 ctx.stroke();
13528 }
13529 }
13530 });
13531
13532 },{"25":25,"26":26,"45":45}],37:[function(require,module,exports){
13533 'use strict';
13534
13535 var defaults = require(25);
13536 var Element = require(26);
13537 var helpers = require(45);
13538
13539 var globalDefaults = defaults.global;
13540
13541 defaults._set('global', {
13542 elements: {
13543 line: {
13544 tension: 0.4,
13545 backgroundColor: globalDefaults.defaultColor,
13546 borderWidth: 3,
13547 borderColor: globalDefaults.defaultColor,
13548 borderCapStyle: 'butt',
13549 borderDash: [],
13550 borderDashOffset: 0.0,
13551 borderJoinStyle: 'miter',
13552 capBezierPoints: true,
13553 fill: true, // do we fill in the area between the line and its base axis
13554 }
13555 }
13556 });
13557
13558 module.exports = Element.extend({
13559 draw: function() {
13560 var me = this;
13561 var vm = me._view;
13562 var ctx = me._chart.ctx;
13563 var spanGaps = vm.spanGaps;
13564 var points = me._children.slice(); // clone array
13565 var globalOptionLineElements = globalDefaults.elements.line;
13566 var lastDrawnIndex = -1;
13567 var index, current, previous, currentVM;
13568
13569 // If we are looping, adding the first point again
13570 if (me._loop && points.length) {
13571 points.push(points[0]);
13572 }
13573
13574 ctx.save();
13575
13576 // Stroke Line Options
13577 ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
13578
13579 // IE 9 and 10 do not support line dash
13580 if (ctx.setLineDash) {
13581 ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
13582 }
13583
13584 ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
13585 ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
13586 ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
13587 ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
13588
13589 // Stroke Line
13590 ctx.beginPath();
13591 lastDrawnIndex = -1;
13592
13593 for (index = 0; index < points.length; ++index) {
13594 current = points[index];
13595 previous = helpers.previousItem(points, index);
13596 currentVM = current._view;
13597
13598 // First point moves to it's starting position no matter what
13599 if (index === 0) {
13600 if (!currentVM.skip) {
13601 ctx.moveTo(currentVM.x, currentVM.y);
13602 lastDrawnIndex = index;
13603 }
13604 } else {
13605 previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
13606
13607 if (!currentVM.skip) {
13608 if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
13609 // There was a gap and this is the first point after the gap
13610 ctx.moveTo(currentVM.x, currentVM.y);
13611 } else {
13612 // Line to next point
13613 helpers.canvas.lineTo(ctx, previous._view, current._view);
13614 }
13615 lastDrawnIndex = index;
13616 }
13617 }
13618 }
13619
13620 ctx.stroke();
13621 ctx.restore();
13622 }
13623 });
13624
13625 },{"25":25,"26":26,"45":45}],38:[function(require,module,exports){
13626 'use strict';
13627
13628 var defaults = require(25);
13629 var Element = require(26);
13630 var helpers = require(45);
13631
13632 var defaultColor = defaults.global.defaultColor;
13633
13634 defaults._set('global', {
13635 elements: {
13636 point: {
13637 radius: 3,
13638 pointStyle: 'circle',
13639 backgroundColor: defaultColor,
13640 borderColor: defaultColor,
13641 borderWidth: 1,
13642 // Hover
13643 hitRadius: 1,
13644 hoverRadius: 4,
13645 hoverBorderWidth: 1
13646 }
13647 }
13648 });
13649
13650 function xRange(mouseX) {
13651 var vm = this._view;
13652 return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
13653 }
13654
13655 function yRange(mouseY) {
13656 var vm = this._view;
13657 return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
13658 }
13659
13660 module.exports = Element.extend({
13661 inRange: function(mouseX, mouseY) {
13662 var vm = this._view;
13663 return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
13664 },
13665
13666 inLabelRange: xRange,
13667 inXRange: xRange,
13668 inYRange: yRange,
13669
13670 getCenterPoint: function() {
13671 var vm = this._view;
13672 return {
13673 x: vm.x,
13674 y: vm.y
13675 };
13676 },
13677
13678 getArea: function() {
13679 return Math.PI * Math.pow(this._view.radius, 2);
13680 },
13681
13682 tooltipPosition: function() {
13683 var vm = this._view;
13684 return {
13685 x: vm.x,
13686 y: vm.y,
13687 padding: vm.radius + vm.borderWidth
13688 };
13689 },
13690
13691 draw: function(chartArea) {
13692 var vm = this._view;
13693 var model = this._model;
13694 var ctx = this._chart.ctx;
13695 var pointStyle = vm.pointStyle;
13696 var radius = vm.radius;
13697 var x = vm.x;
13698 var y = vm.y;
13699 var color = helpers.color;
13700 var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
13701 var ratio = 0;
13702
13703 if (vm.skip) {
13704 return;
13705 }
13706
13707 ctx.strokeStyle = vm.borderColor || defaultColor;
13708 ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
13709 ctx.fillStyle = vm.backgroundColor || defaultColor;
13710
13711 // Cliping for Points.
13712 // going out from inner charArea?
13713 if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) {
13714 // Point fade out
13715 if (model.x < chartArea.left) {
13716 ratio = (x - model.x) / (chartArea.left - model.x);
13717 } else if (chartArea.right * errMargin < model.x) {
13718 ratio = (model.x - x) / (model.x - chartArea.right);
13719 } else if (model.y < chartArea.top) {
13720 ratio = (y - model.y) / (chartArea.top - model.y);
13721 } else if (chartArea.bottom * errMargin < model.y) {
13722 ratio = (model.y - y) / (model.y - chartArea.bottom);
13723 }
13724 ratio = Math.round(ratio * 100) / 100;
13725 ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
13726 ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
13727 }
13728
13729 helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
13730 }
13731 });
13732
13733 },{"25":25,"26":26,"45":45}],39:[function(require,module,exports){
13734 'use strict';
13735
13736 var defaults = require(25);
13737 var Element = require(26);
13738
13739 defaults._set('global', {
13740 elements: {
13741 rectangle: {
13742 backgroundColor: defaults.global.defaultColor,
13743 borderColor: defaults.global.defaultColor,
13744 borderSkipped: 'bottom',
13745 borderWidth: 0
13746 }
13747 }
13748 });
13749
13750 function isVertical(bar) {
13751 return bar._view.width !== undefined;
13752 }
13753
13754 /**
13755 * Helper function to get the bounds of the bar regardless of the orientation
13756 * @param bar {Chart.Element.Rectangle} the bar
13757 * @return {Bounds} bounds of the bar
13758 * @private
13759 */
13760 function getBarBounds(bar) {
13761 var vm = bar._view;
13762 var x1, x2, y1, y2;
13763
13764 if (isVertical(bar)) {
13765 // vertical
13766 var halfWidth = vm.width / 2;
13767 x1 = vm.x - halfWidth;
13768 x2 = vm.x + halfWidth;
13769 y1 = Math.min(vm.y, vm.base);
13770 y2 = Math.max(vm.y, vm.base);
13771 } else {
13772 // horizontal bar
13773 var halfHeight = vm.height / 2;
13774 x1 = Math.min(vm.x, vm.base);
13775 x2 = Math.max(vm.x, vm.base);
13776 y1 = vm.y - halfHeight;
13777 y2 = vm.y + halfHeight;
13778 }
13779
13780 return {
13781 left: x1,
13782 top: y1,
13783 right: x2,
13784 bottom: y2
13785 };
13786 }
13787
13788 module.exports = Element.extend({
13789 draw: function() {
13790 var ctx = this._chart.ctx;
13791 var vm = this._view;
13792 var left, right, top, bottom, signX, signY, borderSkipped;
13793 var borderWidth = vm.borderWidth;
13794
13795 if (!vm.horizontal) {
13796 // bar
13797 left = vm.x - vm.width / 2;
13798 right = vm.x + vm.width / 2;
13799 top = vm.y;
13800 bottom = vm.base;
13801 signX = 1;
13802 signY = bottom > top ? 1 : -1;
13803 borderSkipped = vm.borderSkipped || 'bottom';
13804 } else {
13805 // horizontal bar
13806 left = vm.base;
13807 right = vm.x;
13808 top = vm.y - vm.height / 2;
13809 bottom = vm.y + vm.height / 2;
13810 signX = right > left ? 1 : -1;
13811 signY = 1;
13812 borderSkipped = vm.borderSkipped || 'left';
13813 }
13814
13815 // Canvas doesn't allow us to stroke inside the width so we can
13816 // adjust the sizes to fit if we're setting a stroke on the line
13817 if (borderWidth) {
13818 // borderWidth shold be less than bar width and bar height.
13819 var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
13820 borderWidth = borderWidth > barSize ? barSize : borderWidth;
13821 var halfStroke = borderWidth / 2;
13822 // Adjust borderWidth when bar top position is near vm.base(zero).
13823 var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
13824 var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
13825 var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
13826 var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
13827 // not become a vertical line?
13828 if (borderLeft !== borderRight) {
13829 top = borderTop;
13830 bottom = borderBottom;
13831 }
13832 // not become a horizontal line?
13833 if (borderTop !== borderBottom) {
13834 left = borderLeft;
13835 right = borderRight;
13836 }
13837 }
13838
13839 ctx.beginPath();
13840 ctx.fillStyle = vm.backgroundColor;
13841 ctx.strokeStyle = vm.borderColor;
13842 ctx.lineWidth = borderWidth;
13843
13844 // Corner points, from bottom-left to bottom-right clockwise
13845 // | 1 2 |
13846 // | 0 3 |
13847 var corners = [
13848 [left, bottom],
13849 [left, top],
13850 [right, top],
13851 [right, bottom]
13852 ];
13853
13854 // Find first (starting) corner with fallback to 'bottom'
13855 var borders = ['bottom', 'left', 'top', 'right'];
13856 var startCorner = borders.indexOf(borderSkipped, 0);
13857 if (startCorner === -1) {
13858 startCorner = 0;
13859 }
13860
13861 function cornerAt(index) {
13862 return corners[(startCorner + index) % 4];
13863 }
13864
13865 // Draw rectangle from 'startCorner'
13866 var corner = cornerAt(0);
13867 ctx.moveTo(corner[0], corner[1]);
13868
13869 for (var i = 1; i < 4; i++) {
13870 corner = cornerAt(i);
13871 ctx.lineTo(corner[0], corner[1]);
13872 }
13873
13874 ctx.fill();
13875 if (borderWidth) {
13876 ctx.stroke();
13877 }
13878 },
13879
13880 height: function() {
13881 var vm = this._view;
13882 return vm.base - vm.y;
13883 },
13884
13885 inRange: function(mouseX, mouseY) {
13886 var inRange = false;
13887
13888 if (this._view) {
13889 var bounds = getBarBounds(this);
13890 inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
13891 }
13892
13893 return inRange;
13894 },
13895
13896 inLabelRange: function(mouseX, mouseY) {
13897 var me = this;
13898 if (!me._view) {
13899 return false;
13900 }
13901
13902 var inRange = false;
13903 var bounds = getBarBounds(me);
13904
13905 if (isVertical(me)) {
13906 inRange = mouseX >= bounds.left && mouseX <= bounds.right;
13907 } else {
13908 inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
13909 }
13910
13911 return inRange;
13912 },
13913
13914 inXRange: function(mouseX) {
13915 var bounds = getBarBounds(this);
13916 return mouseX >= bounds.left && mouseX <= bounds.right;
13917 },
13918
13919 inYRange: function(mouseY) {
13920 var bounds = getBarBounds(this);
13921 return mouseY >= bounds.top && mouseY <= bounds.bottom;
13922 },
13923
13924 getCenterPoint: function() {
13925 var vm = this._view;
13926 var x, y;
13927 if (isVertical(this)) {
13928 x = vm.x;
13929 y = (vm.y + vm.base) / 2;
13930 } else {
13931 x = (vm.x + vm.base) / 2;
13932 y = vm.y;
13933 }
13934
13935 return {x: x, y: y};
13936 },
13937
13938 getArea: function() {
13939 var vm = this._view;
13940 return vm.width * Math.abs(vm.y - vm.base);
13941 },
13942
13943 tooltipPosition: function() {
13944 var vm = this._view;
13945 return {
13946 x: vm.x,
13947 y: vm.y
13948 };
13949 }
13950 });
13951
13952 },{"25":25,"26":26}],40:[function(require,module,exports){
13953 'use strict';
13954
13955 module.exports = {};
13956 module.exports.Arc = require(36);
13957 module.exports.Line = require(37);
13958 module.exports.Point = require(38);
13959 module.exports.Rectangle = require(39);
13960
13961 },{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){
13962 'use strict';
13963
13964 var helpers = require(42);
13965
13966 /**
13967 * @namespace Chart.helpers.canvas
13968 */
13969 var exports = module.exports = {
13970 /**
13971 * Clears the entire canvas associated to the given `chart`.
13972 * @param {Chart} chart - The chart for which to clear the canvas.
13973 */
13974 clear: function(chart) {
13975 chart.ctx.clearRect(0, 0, chart.width, chart.height);
13976 },
13977
13978 /**
13979 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
13980 * given size (width, height) and the same `radius` for all corners.
13981 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
13982 * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
13983 * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
13984 * @param {Number} width - The rectangle's width.
13985 * @param {Number} height - The rectangle's height.
13986 * @param {Number} radius - The rounded amount (in pixels) for the four corners.
13987 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
13988 */
13989 roundedRect: function(ctx, x, y, width, height, radius) {
13990 if (radius) {
13991 var rx = Math.min(radius, width / 2);
13992 var ry = Math.min(radius, height / 2);
13993
13994 ctx.moveTo(x + rx, y);
13995 ctx.lineTo(x + width - rx, y);
13996 ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
13997 ctx.lineTo(x + width, y + height - ry);
13998 ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
13999 ctx.lineTo(x + rx, y + height);
14000 ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
14001 ctx.lineTo(x, y + ry);
14002 ctx.quadraticCurveTo(x, y, x + rx, y);
14003 } else {
14004 ctx.rect(x, y, width, height);
14005 }
14006 },
14007
14008 drawPoint: function(ctx, style, radius, x, y) {
14009 var type, edgeLength, xOffset, yOffset, height, size;
14010
14011 if (typeof style === 'object') {
14012 type = style.toString();
14013 if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
14014 ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
14015 return;
14016 }
14017 }
14018
14019 if (isNaN(radius) || radius <= 0) {
14020 return;
14021 }
14022
14023 switch (style) {
14024 // Default includes circle
14025 default:
14026 ctx.beginPath();
14027 ctx.arc(x, y, radius, 0, Math.PI * 2);
14028 ctx.closePath();
14029 ctx.fill();
14030 break;
14031 case 'triangle':
14032 ctx.beginPath();
14033 edgeLength = 3 * radius / Math.sqrt(3);
14034 height = edgeLength * Math.sqrt(3) / 2;
14035 ctx.moveTo(x - edgeLength / 2, y + height / 3);
14036 ctx.lineTo(x + edgeLength / 2, y + height / 3);
14037 ctx.lineTo(x, y - 2 * height / 3);
14038 ctx.closePath();
14039 ctx.fill();
14040 break;
14041 case 'rect':
14042 size = 1 / Math.SQRT2 * radius;
14043 ctx.beginPath();
14044 ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
14045 ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
14046 break;
14047 case 'rectRounded':
14048 var offset = radius / Math.SQRT2;
14049 var leftX = x - offset;
14050 var topY = y - offset;
14051 var sideSize = Math.SQRT2 * radius;
14052 ctx.beginPath();
14053 this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
14054 ctx.closePath();
14055 ctx.fill();
14056 break;
14057 case 'rectRot':
14058 size = 1 / Math.SQRT2 * radius;
14059 ctx.beginPath();
14060 ctx.moveTo(x - size, y);
14061 ctx.lineTo(x, y + size);
14062 ctx.lineTo(x + size, y);
14063 ctx.lineTo(x, y - size);
14064 ctx.closePath();
14065 ctx.fill();
14066 break;
14067 case 'cross':
14068 ctx.beginPath();
14069 ctx.moveTo(x, y + radius);
14070 ctx.lineTo(x, y - radius);
14071 ctx.moveTo(x - radius, y);
14072 ctx.lineTo(x + radius, y);
14073 ctx.closePath();
14074 break;
14075 case 'crossRot':
14076 ctx.beginPath();
14077 xOffset = Math.cos(Math.PI / 4) * radius;
14078 yOffset = Math.sin(Math.PI / 4) * radius;
14079 ctx.moveTo(x - xOffset, y - yOffset);
14080 ctx.lineTo(x + xOffset, y + yOffset);
14081 ctx.moveTo(x - xOffset, y + yOffset);
14082 ctx.lineTo(x + xOffset, y - yOffset);
14083 ctx.closePath();
14084 break;
14085 case 'star':
14086 ctx.beginPath();
14087 ctx.moveTo(x, y + radius);
14088 ctx.lineTo(x, y - radius);
14089 ctx.moveTo(x - radius, y);
14090 ctx.lineTo(x + radius, y);
14091 xOffset = Math.cos(Math.PI / 4) * radius;
14092 yOffset = Math.sin(Math.PI / 4) * radius;
14093 ctx.moveTo(x - xOffset, y - yOffset);
14094 ctx.lineTo(x + xOffset, y + yOffset);
14095 ctx.moveTo(x - xOffset, y + yOffset);
14096 ctx.lineTo(x + xOffset, y - yOffset);
14097 ctx.closePath();
14098 break;
14099 case 'line':
14100 ctx.beginPath();
14101 ctx.moveTo(x - radius, y);
14102 ctx.lineTo(x + radius, y);
14103 ctx.closePath();
14104 break;
14105 case 'dash':
14106 ctx.beginPath();
14107 ctx.moveTo(x, y);
14108 ctx.lineTo(x + radius, y);
14109 ctx.closePath();
14110 break;
14111 }
14112
14113 ctx.stroke();
14114 },
14115
14116 clipArea: function(ctx, area) {
14117 ctx.save();
14118 ctx.beginPath();
14119 ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
14120 ctx.clip();
14121 },
14122
14123 unclipArea: function(ctx) {
14124 ctx.restore();
14125 },
14126
14127 lineTo: function(ctx, previous, target, flip) {
14128 if (target.steppedLine) {
14129 if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
14130 ctx.lineTo(previous.x, target.y);
14131 } else {
14132 ctx.lineTo(target.x, previous.y);
14133 }
14134 ctx.lineTo(target.x, target.y);
14135 return;
14136 }
14137
14138 if (!target.tension) {
14139 ctx.lineTo(target.x, target.y);
14140 return;
14141 }
14142
14143 ctx.bezierCurveTo(
14144 flip ? previous.controlPointPreviousX : previous.controlPointNextX,
14145 flip ? previous.controlPointPreviousY : previous.controlPointNextY,
14146 flip ? target.controlPointNextX : target.controlPointPreviousX,
14147 flip ? target.controlPointNextY : target.controlPointPreviousY,
14148 target.x,
14149 target.y);
14150 }
14151 };
14152
14153 // DEPRECATIONS
14154
14155 /**
14156 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
14157 * @namespace Chart.helpers.clear
14158 * @deprecated since version 2.7.0
14159 * @todo remove at version 3
14160 * @private
14161 */
14162 helpers.clear = exports.clear;
14163
14164 /**
14165 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
14166 * @namespace Chart.helpers.drawRoundedRectangle
14167 * @deprecated since version 2.7.0
14168 * @todo remove at version 3
14169 * @private
14170 */
14171 helpers.drawRoundedRectangle = function(ctx) {
14172 ctx.beginPath();
14173 exports.roundedRect.apply(exports, arguments);
14174 ctx.closePath();
14175 };
14176
14177 },{"42":42}],42:[function(require,module,exports){
14178 'use strict';
14179
14180 /**
14181 * @namespace Chart.helpers
14182 */
14183 var helpers = {
14184 /**
14185 * An empty function that can be used, for example, for optional callback.
14186 */
14187 noop: function() {},
14188
14189 /**
14190 * Returns a unique id, sequentially generated from a global variable.
14191 * @returns {Number}
14192 * @function
14193 */
14194 uid: (function() {
14195 var id = 0;
14196 return function() {
14197 return id++;
14198 };
14199 }()),
14200
14201 /**
14202 * Returns true if `value` is neither null nor undefined, else returns false.
14203 * @param {*} value - The value to test.
14204 * @returns {Boolean}
14205 * @since 2.7.0
14206 */
14207 isNullOrUndef: function(value) {
14208 return value === null || typeof value === 'undefined';
14209 },
14210
14211 /**
14212 * Returns true if `value` is an array, else returns false.
14213 * @param {*} value - The value to test.
14214 * @returns {Boolean}
14215 * @function
14216 */
14217 isArray: Array.isArray ? Array.isArray : function(value) {
14218 return Object.prototype.toString.call(value) === '[object Array]';
14219 },
14220
14221 /**
14222 * Returns true if `value` is an object (excluding null), else returns false.
14223 * @param {*} value - The value to test.
14224 * @returns {Boolean}
14225 * @since 2.7.0
14226 */
14227 isObject: function(value) {
14228 return value !== null && Object.prototype.toString.call(value) === '[object Object]';
14229 },
14230
14231 /**
14232 * Returns `value` if defined, else returns `defaultValue`.
14233 * @param {*} value - The value to return if defined.
14234 * @param {*} defaultValue - The value to return if `value` is undefined.
14235 * @returns {*}
14236 */
14237 valueOrDefault: function(value, defaultValue) {
14238 return typeof value === 'undefined' ? defaultValue : value;
14239 },
14240
14241 /**
14242 * Returns value at the given `index` in array if defined, else returns `defaultValue`.
14243 * @param {Array} value - The array to lookup for value at `index`.
14244 * @param {Number} index - The index in `value` to lookup for value.
14245 * @param {*} defaultValue - The value to return if `value[index]` is undefined.
14246 * @returns {*}
14247 */
14248 valueAtIndexOrDefault: function(value, index, defaultValue) {
14249 return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
14250 },
14251
14252 /**
14253 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
14254 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
14255 * @param {Function} fn - The function to call.
14256 * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
14257 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
14258 * @returns {*}
14259 */
14260 callback: function(fn, args, thisArg) {
14261 if (fn && typeof fn.call === 'function') {
14262 return fn.apply(thisArg, args);
14263 }
14264 },
14265
14266 /**
14267 * Note(SB) for performance sake, this method should only be used when loopable type
14268 * is unknown or in none intensive code (not called often and small loopable). Else
14269 * it's preferable to use a regular for() loop and save extra function calls.
14270 * @param {Object|Array} loopable - The object or array to be iterated.
14271 * @param {Function} fn - The function to call for each item.
14272 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
14273 * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
14274 */
14275 each: function(loopable, fn, thisArg, reverse) {
14276 var i, len, keys;
14277 if (helpers.isArray(loopable)) {
14278 len = loopable.length;
14279 if (reverse) {
14280 for (i = len - 1; i >= 0; i--) {
14281 fn.call(thisArg, loopable[i], i);
14282 }
14283 } else {
14284 for (i = 0; i < len; i++) {
14285 fn.call(thisArg, loopable[i], i);
14286 }
14287 }
14288 } else if (helpers.isObject(loopable)) {
14289 keys = Object.keys(loopable);
14290 len = keys.length;
14291 for (i = 0; i < len; i++) {
14292 fn.call(thisArg, loopable[keys[i]], keys[i]);
14293 }
14294 }
14295 },
14296
14297 /**
14298 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
14299 * @see http://stackoverflow.com/a/14853974
14300 * @param {Array} a0 - The array to compare
14301 * @param {Array} a1 - The array to compare
14302 * @returns {Boolean}
14303 */
14304 arrayEquals: function(a0, a1) {
14305 var i, ilen, v0, v1;
14306
14307 if (!a0 || !a1 || a0.length !== a1.length) {
14308 return false;
14309 }
14310
14311 for (i = 0, ilen = a0.length; i < ilen; ++i) {
14312 v0 = a0[i];
14313 v1 = a1[i];
14314
14315 if (v0 instanceof Array && v1 instanceof Array) {
14316 if (!helpers.arrayEquals(v0, v1)) {
14317 return false;
14318 }
14319 } else if (v0 !== v1) {
14320 // NOTE: two different object instances will never be equal: {x:20} != {x:20}
14321 return false;
14322 }
14323 }
14324
14325 return true;
14326 },
14327
14328 /**
14329 * Returns a deep copy of `source` without keeping references on objects and arrays.
14330 * @param {*} source - The value to clone.
14331 * @returns {*}
14332 */
14333 clone: function(source) {
14334 if (helpers.isArray(source)) {
14335 return source.map(helpers.clone);
14336 }
14337
14338 if (helpers.isObject(source)) {
14339 var target = {};
14340 var keys = Object.keys(source);
14341 var klen = keys.length;
14342 var k = 0;
14343
14344 for (; k < klen; ++k) {
14345 target[keys[k]] = helpers.clone(source[keys[k]]);
14346 }
14347
14348 return target;
14349 }
14350
14351 return source;
14352 },
14353
14354 /**
14355 * The default merger when Chart.helpers.merge is called without merger option.
14356 * Note(SB): this method is also used by configMerge and scaleMerge as fallback.
14357 * @private
14358 */
14359 _merger: function(key, target, source, options) {
14360 var tval = target[key];
14361 var sval = source[key];
14362
14363 if (helpers.isObject(tval) && helpers.isObject(sval)) {
14364 helpers.merge(tval, sval, options);
14365 } else {
14366 target[key] = helpers.clone(sval);
14367 }
14368 },
14369
14370 /**
14371 * Merges source[key] in target[key] only if target[key] is undefined.
14372 * @private
14373 */
14374 _mergerIf: function(key, target, source) {
14375 var tval = target[key];
14376 var sval = source[key];
14377
14378 if (helpers.isObject(tval) && helpers.isObject(sval)) {
14379 helpers.mergeIf(tval, sval);
14380 } else if (!target.hasOwnProperty(key)) {
14381 target[key] = helpers.clone(sval);
14382 }
14383 },
14384
14385 /**
14386 * Recursively deep copies `source` properties into `target` with the given `options`.
14387 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
14388 * @param {Object} target - The target object in which all sources are merged into.
14389 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
14390 * @param {Object} [options] - Merging options:
14391 * @param {Function} [options.merger] - The merge method (key, target, source, options)
14392 * @returns {Object} The `target` object.
14393 */
14394 merge: function(target, source, options) {
14395 var sources = helpers.isArray(source) ? source : [source];
14396 var ilen = sources.length;
14397 var merge, i, keys, klen, k;
14398
14399 if (!helpers.isObject(target)) {
14400 return target;
14401 }
14402
14403 options = options || {};
14404 merge = options.merger || helpers._merger;
14405
14406 for (i = 0; i < ilen; ++i) {
14407 source = sources[i];
14408 if (!helpers.isObject(source)) {
14409 continue;
14410 }
14411
14412 keys = Object.keys(source);
14413 for (k = 0, klen = keys.length; k < klen; ++k) {
14414 merge(keys[k], target, source, options);
14415 }
14416 }
14417
14418 return target;
14419 },
14420
14421 /**
14422 * Recursively deep copies `source` properties into `target` *only* if not defined in target.
14423 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
14424 * @param {Object} target - The target object in which all sources are merged into.
14425 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
14426 * @returns {Object} The `target` object.
14427 */
14428 mergeIf: function(target, source) {
14429 return helpers.merge(target, source, {merger: helpers._mergerIf});
14430 }
14431 };
14432
14433 module.exports = helpers;
14434
14435 // DEPRECATIONS
14436
14437 /**
14438 * Provided for backward compatibility, use Chart.helpers.callback instead.
14439 * @function Chart.helpers.callCallback
14440 * @deprecated since version 2.6.0
14441 * @todo remove at version 3
14442 * @private
14443 */
14444 helpers.callCallback = helpers.callback;
14445
14446 /**
14447 * Provided for backward compatibility, use Array.prototype.indexOf instead.
14448 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
14449 * @function Chart.helpers.indexOf
14450 * @deprecated since version 2.7.0
14451 * @todo remove at version 3
14452 * @private
14453 */
14454 helpers.indexOf = function(array, item, fromIndex) {
14455 return Array.prototype.indexOf.call(array, item, fromIndex);
14456 };
14457
14458 /**
14459 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
14460 * @function Chart.helpers.getValueOrDefault
14461 * @deprecated since version 2.7.0
14462 * @todo remove at version 3
14463 * @private
14464 */
14465 helpers.getValueOrDefault = helpers.valueOrDefault;
14466
14467 /**
14468 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
14469 * @function Chart.helpers.getValueAtIndexOrDefault
14470 * @deprecated since version 2.7.0
14471 * @todo remove at version 3
14472 * @private
14473 */
14474 helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
14475
14476 },{}],43:[function(require,module,exports){
14477 'use strict';
14478
14479 var helpers = require(42);
14480
14481 /**
14482 * Easing functions adapted from Robert Penner's easing equations.
14483 * @namespace Chart.helpers.easingEffects
14484 * @see http://www.robertpenner.com/easing/
14485 */
14486 var effects = {
14487 linear: function(t) {
14488 return t;
14489 },
14490
14491 easeInQuad: function(t) {
14492 return t * t;
14493 },
14494
14495 easeOutQuad: function(t) {
14496 return -t * (t - 2);
14497 },
14498
14499 easeInOutQuad: function(t) {
14500 if ((t /= 0.5) < 1) {
14501 return 0.5 * t * t;
14502 }
14503 return -0.5 * ((--t) * (t - 2) - 1);
14504 },
14505
14506 easeInCubic: function(t) {
14507 return t * t * t;
14508 },
14509
14510 easeOutCubic: function(t) {
14511 return (t = t - 1) * t * t + 1;
14512 },
14513
14514 easeInOutCubic: function(t) {
14515 if ((t /= 0.5) < 1) {
14516 return 0.5 * t * t * t;
14517 }
14518 return 0.5 * ((t -= 2) * t * t + 2);
14519 },
14520
14521 easeInQuart: function(t) {
14522 return t * t * t * t;
14523 },
14524
14525 easeOutQuart: function(t) {
14526 return -((t = t - 1) * t * t * t - 1);
14527 },
14528
14529 easeInOutQuart: function(t) {
14530 if ((t /= 0.5) < 1) {
14531 return 0.5 * t * t * t * t;
14532 }
14533 return -0.5 * ((t -= 2) * t * t * t - 2);
14534 },
14535
14536 easeInQuint: function(t) {
14537 return t * t * t * t * t;
14538 },
14539
14540 easeOutQuint: function(t) {
14541 return (t = t - 1) * t * t * t * t + 1;
14542 },
14543
14544 easeInOutQuint: function(t) {
14545 if ((t /= 0.5) < 1) {
14546 return 0.5 * t * t * t * t * t;
14547 }
14548 return 0.5 * ((t -= 2) * t * t * t * t + 2);
14549 },
14550
14551 easeInSine: function(t) {
14552 return -Math.cos(t * (Math.PI / 2)) + 1;
14553 },
14554
14555 easeOutSine: function(t) {
14556 return Math.sin(t * (Math.PI / 2));
14557 },
14558
14559 easeInOutSine: function(t) {
14560 return -0.5 * (Math.cos(Math.PI * t) - 1);
14561 },
14562
14563 easeInExpo: function(t) {
14564 return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
14565 },
14566
14567 easeOutExpo: function(t) {
14568 return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
14569 },
14570
14571 easeInOutExpo: function(t) {
14572 if (t === 0) {
14573 return 0;
14574 }
14575 if (t === 1) {
14576 return 1;
14577 }
14578 if ((t /= 0.5) < 1) {
14579 return 0.5 * Math.pow(2, 10 * (t - 1));
14580 }
14581 return 0.5 * (-Math.pow(2, -10 * --t) + 2);
14582 },
14583
14584 easeInCirc: function(t) {
14585 if (t >= 1) {
14586 return t;
14587 }
14588 return -(Math.sqrt(1 - t * t) - 1);
14589 },
14590
14591 easeOutCirc: function(t) {
14592 return Math.sqrt(1 - (t = t - 1) * t);
14593 },
14594
14595 easeInOutCirc: function(t) {
14596 if ((t /= 0.5) < 1) {
14597 return -0.5 * (Math.sqrt(1 - t * t) - 1);
14598 }
14599 return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
14600 },
14601
14602 easeInElastic: function(t) {
14603 var s = 1.70158;
14604 var p = 0;
14605 var a = 1;
14606 if (t === 0) {
14607 return 0;
14608 }
14609 if (t === 1) {
14610 return 1;
14611 }
14612 if (!p) {
14613 p = 0.3;
14614 }
14615 if (a < 1) {
14616 a = 1;
14617 s = p / 4;
14618 } else {
14619 s = p / (2 * Math.PI) * Math.asin(1 / a);
14620 }
14621 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
14622 },
14623
14624 easeOutElastic: function(t) {
14625 var s = 1.70158;
14626 var p = 0;
14627 var a = 1;
14628 if (t === 0) {
14629 return 0;
14630 }
14631 if (t === 1) {
14632 return 1;
14633 }
14634 if (!p) {
14635 p = 0.3;
14636 }
14637 if (a < 1) {
14638 a = 1;
14639 s = p / 4;
14640 } else {
14641 s = p / (2 * Math.PI) * Math.asin(1 / a);
14642 }
14643 return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
14644 },
14645
14646 easeInOutElastic: function(t) {
14647 var s = 1.70158;
14648 var p = 0;
14649 var a = 1;
14650 if (t === 0) {
14651 return 0;
14652 }
14653 if ((t /= 0.5) === 2) {
14654 return 1;
14655 }
14656 if (!p) {
14657 p = 0.45;
14658 }
14659 if (a < 1) {
14660 a = 1;
14661 s = p / 4;
14662 } else {
14663 s = p / (2 * Math.PI) * Math.asin(1 / a);
14664 }
14665 if (t < 1) {
14666 return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
14667 }
14668 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
14669 },
14670 easeInBack: function(t) {
14671 var s = 1.70158;
14672 return t * t * ((s + 1) * t - s);
14673 },
14674
14675 easeOutBack: function(t) {
14676 var s = 1.70158;
14677 return (t = t - 1) * t * ((s + 1) * t + s) + 1;
14678 },
14679
14680 easeInOutBack: function(t) {
14681 var s = 1.70158;
14682 if ((t /= 0.5) < 1) {
14683 return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
14684 }
14685 return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
14686 },
14687
14688 easeInBounce: function(t) {
14689 return 1 - effects.easeOutBounce(1 - t);
14690 },
14691
14692 easeOutBounce: function(t) {
14693 if (t < (1 / 2.75)) {
14694 return 7.5625 * t * t;
14695 }
14696 if (t < (2 / 2.75)) {
14697 return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
14698 }
14699 if (t < (2.5 / 2.75)) {
14700 return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
14701 }
14702 return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
14703 },
14704
14705 easeInOutBounce: function(t) {
14706 if (t < 0.5) {
14707 return effects.easeInBounce(t * 2) * 0.5;
14708 }
14709 return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
14710 }
14711 };
14712
14713 module.exports = {
14714 effects: effects
14715 };
14716
14717 // DEPRECATIONS
14718
14719 /**
14720 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
14721 * @function Chart.helpers.easingEffects
14722 * @deprecated since version 2.7.0
14723 * @todo remove at version 3
14724 * @private
14725 */
14726 helpers.easingEffects = effects;
14727
14728 },{"42":42}],44:[function(require,module,exports){
14729 'use strict';
14730
14731 var helpers = require(42);
14732
14733 /**
14734 * @alias Chart.helpers.options
14735 * @namespace
14736 */
14737 module.exports = {
14738 /**
14739 * Converts the given line height `value` in pixels for a specific font `size`.
14740 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
14741 * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
14742 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
14743 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
14744 * @since 2.7.0
14745 */
14746 toLineHeight: function(value, size) {
14747 var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
14748 if (!matches || matches[1] === 'normal') {
14749 return size * 1.2;
14750 }
14751
14752 value = +matches[2];
14753
14754 switch (matches[3]) {
14755 case 'px':
14756 return value;
14757 case '%':
14758 value /= 100;
14759 break;
14760 default:
14761 break;
14762 }
14763
14764 return size * value;
14765 },
14766
14767 /**
14768 * Converts the given value into a padding object with pre-computed width/height.
14769 * @param {Number|Object} value - If a number, set the value to all TRBL component,
14770 * else, if and object, use defined properties and sets undefined ones to 0.
14771 * @returns {Object} The padding values (top, right, bottom, left, width, height)
14772 * @since 2.7.0
14773 */
14774 toPadding: function(value) {
14775 var t, r, b, l;
14776
14777 if (helpers.isObject(value)) {
14778 t = +value.top || 0;
14779 r = +value.right || 0;
14780 b = +value.bottom || 0;
14781 l = +value.left || 0;
14782 } else {
14783 t = r = b = l = +value || 0;
14784 }
14785
14786 return {
14787 top: t,
14788 right: r,
14789 bottom: b,
14790 left: l,
14791 height: t + b,
14792 width: l + r
14793 };
14794 },
14795
14796 /**
14797 * Evaluates the given `inputs` sequentially and returns the first defined value.
14798 * @param {Array[]} inputs - An array of values, falling back to the last value.
14799 * @param {Object} [context] - If defined and the current value is a function, the value
14800 * is called with `context` as first argument and the result becomes the new input.
14801 * @param {Number} [index] - If defined and the current value is an array, the value
14802 * at `index` become the new input.
14803 * @since 2.7.0
14804 */
14805 resolve: function(inputs, context, index) {
14806 var i, ilen, value;
14807
14808 for (i = 0, ilen = inputs.length; i < ilen; ++i) {
14809 value = inputs[i];
14810 if (value === undefined) {
14811 continue;
14812 }
14813 if (context !== undefined && typeof value === 'function') {
14814 value = value(context);
14815 }
14816 if (index !== undefined && helpers.isArray(value)) {
14817 value = value[index];
14818 }
14819 if (value !== undefined) {
14820 return value;
14821 }
14822 }
14823 }
14824 };
14825
14826 },{"42":42}],45:[function(require,module,exports){
14827 'use strict';
14828
14829 module.exports = require(42);
14830 module.exports.easing = require(43);
14831 module.exports.canvas = require(41);
14832 module.exports.options = require(44);
14833
14834 },{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){
14835 /**
14836 * Platform fallback implementation (minimal).
14837 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
14838 */
14839
14840 module.exports = {
14841 acquireContext: function(item) {
14842 if (item && item.canvas) {
14843 // Support for any object associated to a canvas (including a context2d)
14844 item = item.canvas;
14845 }
14846
14847 return item && item.getContext('2d') || null;
14848 }
14849 };
14850
14851 },{}],47:[function(require,module,exports){
14852 /**
14853 * Chart.Platform implementation for targeting a web browser
14854 */
14855
14856 'use strict';
14857
14858 var helpers = require(45);
14859
14860 var EXPANDO_KEY = '$chartjs';
14861 var CSS_PREFIX = 'chartjs-';
14862 var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
14863 var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
14864 var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
14865
14866 /**
14867 * DOM event types -> Chart.js event types.
14868 * Note: only events with different types are mapped.
14869 * @see https://developer.mozilla.org/en-US/docs/Web/Events
14870 */
14871 var EVENT_TYPES = {
14872 touchstart: 'mousedown',
14873 touchmove: 'mousemove',
14874 touchend: 'mouseup',
14875 pointerenter: 'mouseenter',
14876 pointerdown: 'mousedown',
14877 pointermove: 'mousemove',
14878 pointerup: 'mouseup',
14879 pointerleave: 'mouseout',
14880 pointerout: 'mouseout'
14881 };
14882
14883 /**
14884 * The "used" size is the final value of a dimension property after all calculations have
14885 * been performed. This method uses the computed style of `element` but returns undefined
14886 * if the computed style is not expressed in pixels. That can happen in some cases where
14887 * `element` has a size relative to its parent and this last one is not yet displayed,
14888 * for example because of `display: none` on a parent node.
14889 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
14890 * @returns {Number} Size in pixels or undefined if unknown.
14891 */
14892 function readUsedSize(element, property) {
14893 var value = helpers.getStyle(element, property);
14894 var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
14895 return matches ? Number(matches[1]) : undefined;
14896 }
14897
14898 /**
14899 * Initializes the canvas style and render size without modifying the canvas display size,
14900 * since responsiveness is handled by the controller.resize() method. The config is used
14901 * to determine the aspect ratio to apply in case no explicit height has been specified.
14902 */
14903 function initCanvas(canvas, config) {
14904 var style = canvas.style;
14905
14906 // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
14907 // returns null or '' if no explicit value has been set to the canvas attribute.
14908 var renderHeight = canvas.getAttribute('height');
14909 var renderWidth = canvas.getAttribute('width');
14910
14911 // Chart.js modifies some canvas values that we want to restore on destroy
14912 canvas[EXPANDO_KEY] = {
14913 initial: {
14914 height: renderHeight,
14915 width: renderWidth,
14916 style: {
14917 display: style.display,
14918 height: style.height,
14919 width: style.width
14920 }
14921 }
14922 };
14923
14924 // Force canvas to display as block to avoid extra space caused by inline
14925 // elements, which would interfere with the responsive resize process.
14926 // https://github.com/chartjs/Chart.js/issues/2538
14927 style.display = style.display || 'block';
14928
14929 if (renderWidth === null || renderWidth === '') {
14930 var displayWidth = readUsedSize(canvas, 'width');
14931 if (displayWidth !== undefined) {
14932 canvas.width = displayWidth;
14933 }
14934 }
14935
14936 if (renderHeight === null || renderHeight === '') {
14937 if (canvas.style.height === '') {
14938 // If no explicit render height and style height, let's apply the aspect ratio,
14939 // which one can be specified by the user but also by charts as default option
14940 // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
14941 canvas.height = canvas.width / (config.options.aspectRatio || 2);
14942 } else {
14943 var displayHeight = readUsedSize(canvas, 'height');
14944 if (displayWidth !== undefined) {
14945 canvas.height = displayHeight;
14946 }
14947 }
14948 }
14949
14950 return canvas;
14951 }
14952
14953 /**
14954 * Detects support for options object argument in addEventListener.
14955 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
14956 * @private
14957 */
14958 var supportsEventListenerOptions = (function() {
14959 var supports = false;
14960 try {
14961 var options = Object.defineProperty({}, 'passive', {
14962 get: function() {
14963 supports = true;
14964 }
14965 });
14966 window.addEventListener('e', null, options);
14967 } catch (e) {
14968 // continue regardless of error
14969 }
14970 return supports;
14971 }());
14972
14973 // Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
14974 // https://github.com/chartjs/Chart.js/issues/4287
14975 var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
14976
14977 function addEventListener(node, type, listener) {
14978 node.addEventListener(type, listener, eventListenerOptions);
14979 }
14980
14981 function removeEventListener(node, type, listener) {
14982 node.removeEventListener(type, listener, eventListenerOptions);
14983 }
14984
14985 function createEvent(type, chart, x, y, nativeEvent) {
14986 return {
14987 type: type,
14988 chart: chart,
14989 native: nativeEvent || null,
14990 x: x !== undefined ? x : null,
14991 y: y !== undefined ? y : null,
14992 };
14993 }
14994
14995 function fromNativeEvent(event, chart) {
14996 var type = EVENT_TYPES[event.type] || event.type;
14997 var pos = helpers.getRelativePosition(event, chart);
14998 return createEvent(type, chart, pos.x, pos.y, event);
14999 }
15000
15001 function throttled(fn, thisArg) {
15002 var ticking = false;
15003 var args = [];
15004
15005 return function() {
15006 args = Array.prototype.slice.call(arguments);
15007 thisArg = thisArg || this;
15008
15009 if (!ticking) {
15010 ticking = true;
15011 helpers.requestAnimFrame.call(window, function() {
15012 ticking = false;
15013 fn.apply(thisArg, args);
15014 });
15015 }
15016 };
15017 }
15018
15019 // Implementation based on https://github.com/marcj/css-element-queries
15020 function createResizer(handler) {
15021 var resizer = document.createElement('div');
15022 var cls = CSS_PREFIX + 'size-monitor';
15023 var maxSize = 1000000;
15024 var style =
15025 'position:absolute;' +
15026 'left:0;' +
15027 'top:0;' +
15028 'right:0;' +
15029 'bottom:0;' +
15030 'overflow:hidden;' +
15031 'pointer-events:none;' +
15032 'visibility:hidden;' +
15033 'z-index:-1;';
15034
15035 resizer.style.cssText = style;
15036 resizer.className = cls;
15037 resizer.innerHTML =
15038 '<div class="' + cls + '-expand" style="' + style + '">' +
15039 '<div style="' +
15040 'position:absolute;' +
15041 'width:' + maxSize + 'px;' +
15042 'height:' + maxSize + 'px;' +
15043 'left:0;' +
15044 'top:0">' +
15045 '</div>' +
15046 '</div>' +
15047 '<div class="' + cls + '-shrink" style="' + style + '">' +
15048 '<div style="' +
15049 'position:absolute;' +
15050 'width:200%;' +
15051 'height:200%;' +
15052 'left:0; ' +
15053 'top:0">' +
15054 '</div>' +
15055 '</div>';
15056
15057 var expand = resizer.childNodes[0];
15058 var shrink = resizer.childNodes[1];
15059
15060 resizer._reset = function() {
15061 expand.scrollLeft = maxSize;
15062 expand.scrollTop = maxSize;
15063 shrink.scrollLeft = maxSize;
15064 shrink.scrollTop = maxSize;
15065 };
15066 var onScroll = function() {
15067 resizer._reset();
15068 handler();
15069 };
15070
15071 addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
15072 addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
15073
15074 return resizer;
15075 }
15076
15077 // https://davidwalsh.name/detect-node-insertion
15078 function watchForRender(node, handler) {
15079 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
15080 var proxy = expando.renderProxy = function(e) {
15081 if (e.animationName === CSS_RENDER_ANIMATION) {
15082 handler();
15083 }
15084 };
15085
15086 helpers.each(ANIMATION_START_EVENTS, function(type) {
15087 addEventListener(node, type, proxy);
15088 });
15089
15090 node.classList.add(CSS_RENDER_MONITOR);
15091 }
15092
15093 function unwatchForRender(node) {
15094 var expando = node[EXPANDO_KEY] || {};
15095 var proxy = expando.renderProxy;
15096
15097 if (proxy) {
15098 helpers.each(ANIMATION_START_EVENTS, function(type) {
15099 removeEventListener(node, type, proxy);
15100 });
15101
15102 delete expando.renderProxy;
15103 }
15104
15105 node.classList.remove(CSS_RENDER_MONITOR);
15106 }
15107
15108 function addResizeListener(node, listener, chart) {
15109 var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
15110
15111 // Let's keep track of this added resizer and thus avoid DOM query when removing it.
15112 var resizer = expando.resizer = createResizer(throttled(function() {
15113 if (expando.resizer) {
15114 return listener(createEvent('resize', chart));
15115 }
15116 }));
15117
15118 // The resizer needs to be attached to the node parent, so we first need to be
15119 // sure that `node` is attached to the DOM before injecting the resizer element.
15120 watchForRender(node, function() {
15121 if (expando.resizer) {
15122 var container = node.parentNode;
15123 if (container && container !== resizer.parentNode) {
15124 container.insertBefore(resizer, container.firstChild);
15125 }
15126
15127 // The container size might have changed, let's reset the resizer state.
15128 resizer._reset();
15129 }
15130 });
15131 }
15132
15133 function removeResizeListener(node) {
15134 var expando = node[EXPANDO_KEY] || {};
15135 var resizer = expando.resizer;
15136
15137 delete expando.resizer;
15138 unwatchForRender(node);
15139
15140 if (resizer && resizer.parentNode) {
15141 resizer.parentNode.removeChild(resizer);
15142 }
15143 }
15144
15145 function injectCSS(platform, css) {
15146 // http://stackoverflow.com/q/3922139
15147 var style = platform._style || document.createElement('style');
15148 if (!platform._style) {
15149 platform._style = style;
15150 css = '/* Chart.js */\n' + css;
15151 style.setAttribute('type', 'text/css');
15152 document.getElementsByTagName('head')[0].appendChild(style);
15153 }
15154
15155 style.appendChild(document.createTextNode(css));
15156 }
15157
15158 module.exports = {
15159 /**
15160 * This property holds whether this platform is enabled for the current environment.
15161 * Currently used by platform.js to select the proper implementation.
15162 * @private
15163 */
15164 _enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
15165
15166 initialize: function() {
15167 var keyframes = 'from{opacity:0.99}to{opacity:1}';
15168
15169 injectCSS(this,
15170 // DOM rendering detection
15171 // https://davidwalsh.name/detect-node-insertion
15172 '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
15173 '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
15174 '.' + CSS_RENDER_MONITOR + '{' +
15175 '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
15176 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
15177 '}'
15178 );
15179 },
15180
15181 acquireContext: function(item, config) {
15182 if (typeof item === 'string') {
15183 item = document.getElementById(item);
15184 } else if (item.length) {
15185 // Support for array based queries (such as jQuery)
15186 item = item[0];
15187 }
15188
15189 if (item && item.canvas) {
15190 // Support for any object associated to a canvas (including a context2d)
15191 item = item.canvas;
15192 }
15193
15194 // To prevent canvas fingerprinting, some add-ons undefine the getContext
15195 // method, for example: https://github.com/kkapsner/CanvasBlocker
15196 // https://github.com/chartjs/Chart.js/issues/2807
15197 var context = item && item.getContext && item.getContext('2d');
15198
15199 // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
15200 // inside an iframe or when running in a protected environment. We could guess the
15201 // types from their toString() value but let's keep things flexible and assume it's
15202 // a sufficient condition if the item has a context2D which has item as `canvas`.
15203 // https://github.com/chartjs/Chart.js/issues/3887
15204 // https://github.com/chartjs/Chart.js/issues/4102
15205 // https://github.com/chartjs/Chart.js/issues/4152
15206 if (context && context.canvas === item) {
15207 initCanvas(item, config);
15208 return context;
15209 }
15210
15211 return null;
15212 },
15213
15214 releaseContext: function(context) {
15215 var canvas = context.canvas;
15216 if (!canvas[EXPANDO_KEY]) {
15217 return;
15218 }
15219
15220 var initial = canvas[EXPANDO_KEY].initial;
15221 ['height', 'width'].forEach(function(prop) {
15222 var value = initial[prop];
15223 if (helpers.isNullOrUndef(value)) {
15224 canvas.removeAttribute(prop);
15225 } else {
15226 canvas.setAttribute(prop, value);
15227 }
15228 });
15229
15230 helpers.each(initial.style || {}, function(value, key) {
15231 canvas.style[key] = value;
15232 });
15233
15234 // The canvas render size might have been changed (and thus the state stack discarded),
15235 // we can't use save() and restore() to restore the initial state. So make sure that at
15236 // least the canvas context is reset to the default state by setting the canvas width.
15237 // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
15238 canvas.width = canvas.width;
15239
15240 delete canvas[EXPANDO_KEY];
15241 },
15242
15243 addEventListener: function(chart, type, listener) {
15244 var canvas = chart.canvas;
15245 if (type === 'resize') {
15246 // Note: the resize event is not supported on all browsers.
15247 addResizeListener(canvas, listener, chart);
15248 return;
15249 }
15250
15251 var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
15252 var proxies = expando.proxies || (expando.proxies = {});
15253 var proxy = proxies[chart.id + '_' + type] = function(event) {
15254 listener(fromNativeEvent(event, chart));
15255 };
15256
15257 addEventListener(canvas, type, proxy);
15258 },
15259
15260 removeEventListener: function(chart, type, listener) {
15261 var canvas = chart.canvas;
15262 if (type === 'resize') {
15263 // Note: the resize event is not supported on all browsers.
15264 removeResizeListener(canvas, listener);
15265 return;
15266 }
15267
15268 var expando = listener[EXPANDO_KEY] || {};
15269 var proxies = expando.proxies || {};
15270 var proxy = proxies[chart.id + '_' + type];
15271 if (!proxy) {
15272 return;
15273 }
15274
15275 removeEventListener(canvas, type, proxy);
15276 }
15277 };
15278
15279 // DEPRECATIONS
15280
15281 /**
15282 * Provided for backward compatibility, use EventTarget.addEventListener instead.
15283 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
15284 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
15285 * @function Chart.helpers.addEvent
15286 * @deprecated since version 2.7.0
15287 * @todo remove at version 3
15288 * @private
15289 */
15290 helpers.addEvent = addEventListener;
15291
15292 /**
15293 * Provided for backward compatibility, use EventTarget.removeEventListener instead.
15294 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
15295 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
15296 * @function Chart.helpers.removeEvent
15297 * @deprecated since version 2.7.0
15298 * @todo remove at version 3
15299 * @private
15300 */
15301 helpers.removeEvent = removeEventListener;
15302
15303 },{"45":45}],48:[function(require,module,exports){
15304 'use strict';
15305
15306 var helpers = require(45);
15307 var basic = require(46);
15308 var dom = require(47);
15309
15310 // @TODO Make possible to select another platform at build time.
15311 var implementation = dom._enabled ? dom : basic;
15312
15313 /**
15314 * @namespace Chart.platform
15315 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
15316 * @since 2.4.0
15317 */
15318 module.exports = helpers.extend({
15319 /**
15320 * @since 2.7.0
15321 */
15322 initialize: function() {},
15323
15324 /**
15325 * Called at chart construction time, returns a context2d instance implementing
15326 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
15327 * @param {*} item - The native item from which to acquire context (platform specific)
15328 * @param {Object} options - The chart options
15329 * @returns {CanvasRenderingContext2D} context2d instance
15330 */
15331 acquireContext: function() {},
15332
15333 /**
15334 * Called at chart destruction time, releases any resources associated to the context
15335 * previously returned by the acquireContext() method.
15336 * @param {CanvasRenderingContext2D} context - The context2d instance
15337 * @returns {Boolean} true if the method succeeded, else false
15338 */
15339 releaseContext: function() {},
15340
15341 /**
15342 * Registers the specified listener on the given chart.
15343 * @param {Chart} chart - Chart from which to listen for event
15344 * @param {String} type - The ({@link IEvent}) type to listen for
15345 * @param {Function} listener - Receives a notification (an object that implements
15346 * the {@link IEvent} interface) when an event of the specified type occurs.
15347 */
15348 addEventListener: function() {},
15349
15350 /**
15351 * Removes the specified listener previously registered with addEventListener.
15352 * @param {Chart} chart -Chart from which to remove the listener
15353 * @param {String} type - The ({@link IEvent}) type to remove
15354 * @param {Function} listener - The listener function to remove from the event target.
15355 */
15356 removeEventListener: function() {}
15357
15358 }, implementation);
15359
15360 /**
15361 * @interface IPlatform
15362 * Allows abstracting platform dependencies away from the chart
15363 * @borrows Chart.platform.acquireContext as acquireContext
15364 * @borrows Chart.platform.releaseContext as releaseContext
15365 * @borrows Chart.platform.addEventListener as addEventListener
15366 * @borrows Chart.platform.removeEventListener as removeEventListener
15367 */
15368
15369 /**
15370 * @interface IEvent
15371 * @prop {String} type - The event type name, possible values are:
15372 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
15373 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
15374 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
15375 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
15376 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
15377 */
15378
15379 },{"45":45,"46":46,"47":47}],49:[function(require,module,exports){
15380 /**
15381 * Plugin based on discussion from the following Chart.js issues:
15382 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
15383 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
15384 */
15385
15386 'use strict';
15387
15388 var defaults = require(25);
15389 var elements = require(40);
15390 var helpers = require(45);
15391
15392 defaults._set('global', {
15393 plugins: {
15394 filler: {
15395 propagate: true
15396 }
15397 }
15398 });
15399
15400 module.exports = function() {
15401
15402 var mappers = {
15403 dataset: function(source) {
15404 var index = source.fill;
15405 var chart = source.chart;
15406 var meta = chart.getDatasetMeta(index);
15407 var visible = meta && chart.isDatasetVisible(index);
15408 var points = (visible && meta.dataset._children) || [];
15409 var length = points.length || 0;
15410
15411 return !length ? null : function(point, i) {
15412 return (i < length && points[i]._view) || null;
15413 };
15414 },
15415
15416 boundary: function(source) {
15417 var boundary = source.boundary;
15418 var x = boundary ? boundary.x : null;
15419 var y = boundary ? boundary.y : null;
15420
15421 return function(point) {
15422 return {
15423 x: x === null ? point.x : x,
15424 y: y === null ? point.y : y,
15425 };
15426 };
15427 }
15428 };
15429
15430 // @todo if (fill[0] === '#')
15431 function decodeFill(el, index, count) {
15432 var model = el._model || {};
15433 var fill = model.fill;
15434 var target;
15435
15436 if (fill === undefined) {
15437 fill = !!model.backgroundColor;
15438 }
15439
15440 if (fill === false || fill === null) {
15441 return false;
15442 }
15443
15444 if (fill === true) {
15445 return 'origin';
15446 }
15447
15448 target = parseFloat(fill, 10);
15449 if (isFinite(target) && Math.floor(target) === target) {
15450 if (fill[0] === '-' || fill[0] === '+') {
15451 target = index + target;
15452 }
15453
15454 if (target === index || target < 0 || target >= count) {
15455 return false;
15456 }
15457
15458 return target;
15459 }
15460
15461 switch (fill) {
15462 // compatibility
15463 case 'bottom':
15464 return 'start';
15465 case 'top':
15466 return 'end';
15467 case 'zero':
15468 return 'origin';
15469 // supported boundaries
15470 case 'origin':
15471 case 'start':
15472 case 'end':
15473 return fill;
15474 // invalid fill values
15475 default:
15476 return false;
15477 }
15478 }
15479
15480 function computeBoundary(source) {
15481 var model = source.el._model || {};
15482 var scale = source.el._scale || {};
15483 var fill = source.fill;
15484 var target = null;
15485 var horizontal;
15486
15487 if (isFinite(fill)) {
15488 return null;
15489 }
15490
15491 // Backward compatibility: until v3, we still need to support boundary values set on
15492 // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
15493 // controllers might still use it (e.g. the Smith chart).
15494
15495 if (fill === 'start') {
15496 target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
15497 } else if (fill === 'end') {
15498 target = model.scaleTop === undefined ? scale.top : model.scaleTop;
15499 } else if (model.scaleZero !== undefined) {
15500 target = model.scaleZero;
15501 } else if (scale.getBasePosition) {
15502 target = scale.getBasePosition();
15503 } else if (scale.getBasePixel) {
15504 target = scale.getBasePixel();
15505 }
15506
15507 if (target !== undefined && target !== null) {
15508 if (target.x !== undefined && target.y !== undefined) {
15509 return target;
15510 }
15511
15512 if (typeof target === 'number' && isFinite(target)) {
15513 horizontal = scale.isHorizontal();
15514 return {
15515 x: horizontal ? target : null,
15516 y: horizontal ? null : target
15517 };
15518 }
15519 }
15520
15521 return null;
15522 }
15523
15524 function resolveTarget(sources, index, propagate) {
15525 var source = sources[index];
15526 var fill = source.fill;
15527 var visited = [index];
15528 var target;
15529
15530 if (!propagate) {
15531 return fill;
15532 }
15533
15534 while (fill !== false && visited.indexOf(fill) === -1) {
15535 if (!isFinite(fill)) {
15536 return fill;
15537 }
15538
15539 target = sources[fill];
15540 if (!target) {
15541 return false;
15542 }
15543
15544 if (target.visible) {
15545 return fill;
15546 }
15547
15548 visited.push(fill);
15549 fill = target.fill;
15550 }
15551
15552 return false;
15553 }
15554
15555 function createMapper(source) {
15556 var fill = source.fill;
15557 var type = 'dataset';
15558
15559 if (fill === false) {
15560 return null;
15561 }
15562
15563 if (!isFinite(fill)) {
15564 type = 'boundary';
15565 }
15566
15567 return mappers[type](source);
15568 }
15569
15570 function isDrawable(point) {
15571 return point && !point.skip;
15572 }
15573
15574 function drawArea(ctx, curve0, curve1, len0, len1) {
15575 var i;
15576
15577 if (!len0 || !len1) {
15578 return;
15579 }
15580
15581 // building first area curve (normal)
15582 ctx.moveTo(curve0[0].x, curve0[0].y);
15583 for (i = 1; i < len0; ++i) {
15584 helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
15585 }
15586
15587 // joining the two area curves
15588 ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
15589
15590 // building opposite area curve (reverse)
15591 for (i = len1 - 1; i > 0; --i) {
15592 helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
15593 }
15594 }
15595
15596 function doFill(ctx, points, mapper, view, color, loop) {
15597 var count = points.length;
15598 var span = view.spanGaps;
15599 var curve0 = [];
15600 var curve1 = [];
15601 var len0 = 0;
15602 var len1 = 0;
15603 var i, ilen, index, p0, p1, d0, d1;
15604
15605 ctx.beginPath();
15606
15607 for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
15608 index = i % count;
15609 p0 = points[index]._view;
15610 p1 = mapper(p0, index, view);
15611 d0 = isDrawable(p0);
15612 d1 = isDrawable(p1);
15613
15614 if (d0 && d1) {
15615 len0 = curve0.push(p0);
15616 len1 = curve1.push(p1);
15617 } else if (len0 && len1) {
15618 if (!span) {
15619 drawArea(ctx, curve0, curve1, len0, len1);
15620 len0 = len1 = 0;
15621 curve0 = [];
15622 curve1 = [];
15623 } else {
15624 if (d0) {
15625 curve0.push(p0);
15626 }
15627 if (d1) {
15628 curve1.push(p1);
15629 }
15630 }
15631 }
15632 }
15633
15634 drawArea(ctx, curve0, curve1, len0, len1);
15635
15636 ctx.closePath();
15637 ctx.fillStyle = color;
15638 ctx.fill();
15639 }
15640
15641 return {
15642 id: 'filler',
15643
15644 afterDatasetsUpdate: function(chart, options) {
15645 var count = (chart.data.datasets || []).length;
15646 var propagate = options.propagate;
15647 var sources = [];
15648 var meta, i, el, source;
15649
15650 for (i = 0; i < count; ++i) {
15651 meta = chart.getDatasetMeta(i);
15652 el = meta.dataset;
15653 source = null;
15654
15655 if (el && el._model && el instanceof elements.Line) {
15656 source = {
15657 visible: chart.isDatasetVisible(i),
15658 fill: decodeFill(el, i, count),
15659 chart: chart,
15660 el: el
15661 };
15662 }
15663
15664 meta.$filler = source;
15665 sources.push(source);
15666 }
15667
15668 for (i = 0; i < count; ++i) {
15669 source = sources[i];
15670 if (!source) {
15671 continue;
15672 }
15673
15674 source.fill = resolveTarget(sources, i, propagate);
15675 source.boundary = computeBoundary(source);
15676 source.mapper = createMapper(source);
15677 }
15678 },
15679
15680 beforeDatasetDraw: function(chart, args) {
15681 var meta = args.meta.$filler;
15682 if (!meta) {
15683 return;
15684 }
15685
15686 var ctx = chart.ctx;
15687 var el = meta.el;
15688 var view = el._view;
15689 var points = el._children || [];
15690 var mapper = meta.mapper;
15691 var color = view.backgroundColor || defaults.global.defaultColor;
15692
15693 if (mapper && color && points.length) {
15694 helpers.canvas.clipArea(ctx, chart.chartArea);
15695 doFill(ctx, points, mapper, view, color, el._loop);
15696 helpers.canvas.unclipArea(ctx);
15697 }
15698 }
15699 };
15700 };
15701
15702 },{"25":25,"40":40,"45":45}],50:[function(require,module,exports){
15703 'use strict';
15704
15705 var defaults = require(25);
15706 var Element = require(26);
15707 var helpers = require(45);
15708
15709 defaults._set('global', {
15710 legend: {
15711 display: true,
15712 position: 'top',
15713 fullWidth: true,
15714 reverse: false,
15715 weight: 1000,
15716
15717 // a callback that will handle
15718 onClick: function(e, legendItem) {
15719 var index = legendItem.datasetIndex;
15720 var ci = this.chart;
15721 var meta = ci.getDatasetMeta(index);
15722
15723 // See controller.isDatasetVisible comment
15724 meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
15725
15726 // We hid a dataset ... rerender the chart
15727 ci.update();
15728 },
15729
15730 onHover: null,
15731
15732 labels: {
15733 boxWidth: 40,
15734 padding: 10,
15735 // Generates labels shown in the legend
15736 // Valid properties to return:
15737 // text : text to display
15738 // fillStyle : fill of coloured box
15739 // strokeStyle: stroke of coloured box
15740 // hidden : if this legend item refers to a hidden item
15741 // lineCap : cap style for line
15742 // lineDash
15743 // lineDashOffset :
15744 // lineJoin :
15745 // lineWidth :
15746 generateLabels: function(chart) {
15747 var data = chart.data;
15748 return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
15749 return {
15750 text: dataset.label,
15751 fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
15752 hidden: !chart.isDatasetVisible(i),
15753 lineCap: dataset.borderCapStyle,
15754 lineDash: dataset.borderDash,
15755 lineDashOffset: dataset.borderDashOffset,
15756 lineJoin: dataset.borderJoinStyle,
15757 lineWidth: dataset.borderWidth,
15758 strokeStyle: dataset.borderColor,
15759 pointStyle: dataset.pointStyle,
15760
15761 // Below is extra data used for toggling the datasets
15762 datasetIndex: i
15763 };
15764 }, this) : [];
15765 }
15766 }
15767 },
15768
15769 legendCallback: function(chart) {
15770 var text = [];
15771 text.push('<ul class="' + chart.id + '-legend">');
15772 for (var i = 0; i < chart.data.datasets.length; i++) {
15773 text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
15774 if (chart.data.datasets[i].label) {
15775 text.push(chart.data.datasets[i].label);
15776 }
15777 text.push('</li>');
15778 }
15779 text.push('</ul>');
15780 return text.join('');
15781 }
15782 });
15783
15784 module.exports = function(Chart) {
15785
15786 var layout = Chart.layoutService;
15787 var noop = helpers.noop;
15788
15789 /**
15790 * Helper function to get the box width based on the usePointStyle option
15791 * @param labelopts {Object} the label options on the legend
15792 * @param fontSize {Number} the label font size
15793 * @return {Number} width of the color box area
15794 */
15795 function getBoxWidth(labelOpts, fontSize) {
15796 return labelOpts.usePointStyle ?
15797 fontSize * Math.SQRT2 :
15798 labelOpts.boxWidth;
15799 }
15800
15801 Chart.Legend = Element.extend({
15802
15803 initialize: function(config) {
15804 helpers.extend(this, config);
15805
15806 // Contains hit boxes for each dataset (in dataset order)
15807 this.legendHitBoxes = [];
15808
15809 // Are we in doughnut mode which has a different data type
15810 this.doughnutMode = false;
15811 },
15812
15813 // These methods are ordered by lifecycle. Utilities then follow.
15814 // Any function defined here is inherited by all legend types.
15815 // Any function can be extended by the legend type
15816
15817 beforeUpdate: noop,
15818 update: function(maxWidth, maxHeight, margins) {
15819 var me = this;
15820
15821 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
15822 me.beforeUpdate();
15823
15824 // Absorb the master measurements
15825 me.maxWidth = maxWidth;
15826 me.maxHeight = maxHeight;
15827 me.margins = margins;
15828
15829 // Dimensions
15830 me.beforeSetDimensions();
15831 me.setDimensions();
15832 me.afterSetDimensions();
15833 // Labels
15834 me.beforeBuildLabels();
15835 me.buildLabels();
15836 me.afterBuildLabels();
15837
15838 // Fit
15839 me.beforeFit();
15840 me.fit();
15841 me.afterFit();
15842 //
15843 me.afterUpdate();
15844
15845 return me.minSize;
15846 },
15847 afterUpdate: noop,
15848
15849 //
15850
15851 beforeSetDimensions: noop,
15852 setDimensions: function() {
15853 var me = this;
15854 // Set the unconstrained dimension before label rotation
15855 if (me.isHorizontal()) {
15856 // Reset position before calculating rotation
15857 me.width = me.maxWidth;
15858 me.left = 0;
15859 me.right = me.width;
15860 } else {
15861 me.height = me.maxHeight;
15862
15863 // Reset position before calculating rotation
15864 me.top = 0;
15865 me.bottom = me.height;
15866 }
15867
15868 // Reset padding
15869 me.paddingLeft = 0;
15870 me.paddingTop = 0;
15871 me.paddingRight = 0;
15872 me.paddingBottom = 0;
15873
15874 // Reset minSize
15875 me.minSize = {
15876 width: 0,
15877 height: 0
15878 };
15879 },
15880 afterSetDimensions: noop,
15881
15882 //
15883
15884 beforeBuildLabels: noop,
15885 buildLabels: function() {
15886 var me = this;
15887 var labelOpts = me.options.labels || {};
15888 var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
15889
15890 if (labelOpts.filter) {
15891 legendItems = legendItems.filter(function(item) {
15892 return labelOpts.filter(item, me.chart.data);
15893 });
15894 }
15895
15896 if (me.options.reverse) {
15897 legendItems.reverse();
15898 }
15899
15900 me.legendItems = legendItems;
15901 },
15902 afterBuildLabels: noop,
15903
15904 //
15905
15906 beforeFit: noop,
15907 fit: function() {
15908 var me = this;
15909 var opts = me.options;
15910 var labelOpts = opts.labels;
15911 var display = opts.display;
15912
15913 var ctx = me.ctx;
15914
15915 var globalDefault = defaults.global;
15916 var valueOrDefault = helpers.valueOrDefault;
15917 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
15918 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
15919 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
15920 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
15921
15922 // Reset hit boxes
15923 var hitboxes = me.legendHitBoxes = [];
15924
15925 var minSize = me.minSize;
15926 var isHorizontal = me.isHorizontal();
15927
15928 if (isHorizontal) {
15929 minSize.width = me.maxWidth; // fill all the width
15930 minSize.height = display ? 10 : 0;
15931 } else {
15932 minSize.width = display ? 10 : 0;
15933 minSize.height = me.maxHeight; // fill all the height
15934 }
15935
15936 // Increase sizes here
15937 if (display) {
15938 ctx.font = labelFont;
15939
15940 if (isHorizontal) {
15941 // Labels
15942
15943 // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
15944 var lineWidths = me.lineWidths = [0];
15945 var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
15946
15947 ctx.textAlign = 'left';
15948 ctx.textBaseline = 'top';
15949
15950 helpers.each(me.legendItems, function(legendItem, i) {
15951 var boxWidth = getBoxWidth(labelOpts, fontSize);
15952 var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
15953
15954 if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
15955 totalHeight += fontSize + (labelOpts.padding);
15956 lineWidths[lineWidths.length] = me.left;
15957 }
15958
15959 // Store the hitbox width and height here. Final position will be updated in `draw`
15960 hitboxes[i] = {
15961 left: 0,
15962 top: 0,
15963 width: width,
15964 height: fontSize
15965 };
15966
15967 lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
15968 });
15969
15970 minSize.height += totalHeight;
15971
15972 } else {
15973 var vPadding = labelOpts.padding;
15974 var columnWidths = me.columnWidths = [];
15975 var totalWidth = labelOpts.padding;
15976 var currentColWidth = 0;
15977 var currentColHeight = 0;
15978 var itemHeight = fontSize + vPadding;
15979
15980 helpers.each(me.legendItems, function(legendItem, i) {
15981 var boxWidth = getBoxWidth(labelOpts, fontSize);
15982 var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
15983
15984 // If too tall, go to new column
15985 if (currentColHeight + itemHeight > minSize.height) {
15986 totalWidth += currentColWidth + labelOpts.padding;
15987 columnWidths.push(currentColWidth); // previous column width
15988
15989 currentColWidth = 0;
15990 currentColHeight = 0;
15991 }
15992
15993 // Get max width
15994 currentColWidth = Math.max(currentColWidth, itemWidth);
15995 currentColHeight += itemHeight;
15996
15997 // Store the hitbox width and height here. Final position will be updated in `draw`
15998 hitboxes[i] = {
15999 left: 0,
16000 top: 0,
16001 width: itemWidth,
16002 height: fontSize
16003 };
16004 });
16005
16006 totalWidth += currentColWidth;
16007 columnWidths.push(currentColWidth);
16008 minSize.width += totalWidth;
16009 }
16010 }
16011
16012 me.width = minSize.width;
16013 me.height = minSize.height;
16014 },
16015 afterFit: noop,
16016
16017 // Shared Methods
16018 isHorizontal: function() {
16019 return this.options.position === 'top' || this.options.position === 'bottom';
16020 },
16021
16022 // Actually draw the legend on the canvas
16023 draw: function() {
16024 var me = this;
16025 var opts = me.options;
16026 var labelOpts = opts.labels;
16027 var globalDefault = defaults.global;
16028 var lineDefault = globalDefault.elements.line;
16029 var legendWidth = me.width;
16030 var lineWidths = me.lineWidths;
16031
16032 if (opts.display) {
16033 var ctx = me.ctx;
16034 var valueOrDefault = helpers.valueOrDefault;
16035 var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
16036 var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
16037 var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
16038 var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
16039 var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
16040 var cursor;
16041
16042 // Canvas setup
16043 ctx.textAlign = 'left';
16044 ctx.textBaseline = 'middle';
16045 ctx.lineWidth = 0.5;
16046 ctx.strokeStyle = fontColor; // for strikethrough effect
16047 ctx.fillStyle = fontColor; // render in correct colour
16048 ctx.font = labelFont;
16049
16050 var boxWidth = getBoxWidth(labelOpts, fontSize);
16051 var hitboxes = me.legendHitBoxes;
16052
16053 // current position
16054 var drawLegendBox = function(x, y, legendItem) {
16055 if (isNaN(boxWidth) || boxWidth <= 0) {
16056 return;
16057 }
16058
16059 // Set the ctx for the box
16060 ctx.save();
16061
16062 ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
16063 ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
16064 ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
16065 ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
16066 ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
16067 ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
16068 var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
16069
16070 if (ctx.setLineDash) {
16071 // IE 9 and 10 do not support line dash
16072 ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
16073 }
16074
16075 if (opts.labels && opts.labels.usePointStyle) {
16076 // Recalculate x and y for drawPoint() because its expecting
16077 // x and y to be center of figure (instead of top left)
16078 var radius = fontSize * Math.SQRT2 / 2;
16079 var offSet = radius / Math.SQRT2;
16080 var centerX = x + offSet;
16081 var centerY = y + offSet;
16082
16083 // Draw pointStyle as legend symbol
16084 helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
16085 } else {
16086 // Draw box as legend symbol
16087 if (!isLineWidthZero) {
16088 ctx.strokeRect(x, y, boxWidth, fontSize);
16089 }
16090 ctx.fillRect(x, y, boxWidth, fontSize);
16091 }
16092
16093 ctx.restore();
16094 };
16095 var fillText = function(x, y, legendItem, textWidth) {
16096 var halfFontSize = fontSize / 2;
16097 var xLeft = boxWidth + halfFontSize + x;
16098 var yMiddle = y + halfFontSize;
16099
16100 ctx.fillText(legendItem.text, xLeft, yMiddle);
16101
16102 if (legendItem.hidden) {
16103 // Strikethrough the text if hidden
16104 ctx.beginPath();
16105 ctx.lineWidth = 2;
16106 ctx.moveTo(xLeft, yMiddle);
16107 ctx.lineTo(xLeft + textWidth, yMiddle);
16108 ctx.stroke();
16109 }
16110 };
16111
16112 // Horizontal
16113 var isHorizontal = me.isHorizontal();
16114 if (isHorizontal) {
16115 cursor = {
16116 x: me.left + ((legendWidth - lineWidths[0]) / 2),
16117 y: me.top + labelOpts.padding,
16118 line: 0
16119 };
16120 } else {
16121 cursor = {
16122 x: me.left + labelOpts.padding,
16123 y: me.top + labelOpts.padding,
16124 line: 0
16125 };
16126 }
16127
16128 var itemHeight = fontSize + labelOpts.padding;
16129 helpers.each(me.legendItems, function(legendItem, i) {
16130 var textWidth = ctx.measureText(legendItem.text).width;
16131 var width = boxWidth + (fontSize / 2) + textWidth;
16132 var x = cursor.x;
16133 var y = cursor.y;
16134
16135 if (isHorizontal) {
16136 if (x + width >= legendWidth) {
16137 y = cursor.y += itemHeight;
16138 cursor.line++;
16139 x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
16140 }
16141 } else if (y + itemHeight > me.bottom) {
16142 x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
16143 y = cursor.y = me.top + labelOpts.padding;
16144 cursor.line++;
16145 }
16146
16147 drawLegendBox(x, y, legendItem);
16148
16149 hitboxes[i].left = x;
16150 hitboxes[i].top = y;
16151
16152 // Fill the actual label
16153 fillText(x, y, legendItem, textWidth);
16154
16155 if (isHorizontal) {
16156 cursor.x += width + (labelOpts.padding);
16157 } else {
16158 cursor.y += itemHeight;
16159 }
16160
16161 });
16162 }
16163 },
16164
16165 /**
16166 * Handle an event
16167 * @private
16168 * @param {IEvent} event - The event to handle
16169 * @return {Boolean} true if a change occured
16170 */
16171 handleEvent: function(e) {
16172 var me = this;
16173 var opts = me.options;
16174 var type = e.type === 'mouseup' ? 'click' : e.type;
16175 var changed = false;
16176
16177 if (type === 'mousemove') {
16178 if (!opts.onHover) {
16179 return;
16180 }
16181 } else if (type === 'click') {
16182 if (!opts.onClick) {
16183 return;
16184 }
16185 } else {
16186 return;
16187 }
16188
16189 // Chart event already has relative position in it
16190 var x = e.x;
16191 var y = e.y;
16192
16193 if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
16194 // See if we are touching one of the dataset boxes
16195 var lh = me.legendHitBoxes;
16196 for (var i = 0; i < lh.length; ++i) {
16197 var hitBox = lh[i];
16198
16199 if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
16200 // Touching an element
16201 if (type === 'click') {
16202 // use e.native for backwards compatibility
16203 opts.onClick.call(me, e.native, me.legendItems[i]);
16204 changed = true;
16205 break;
16206 } else if (type === 'mousemove') {
16207 // use e.native for backwards compatibility
16208 opts.onHover.call(me, e.native, me.legendItems[i]);
16209 changed = true;
16210 break;
16211 }
16212 }
16213 }
16214 }
16215
16216 return changed;
16217 }
16218 });
16219
16220 function createNewLegendAndAttach(chart, legendOpts) {
16221 var legend = new Chart.Legend({
16222 ctx: chart.ctx,
16223 options: legendOpts,
16224 chart: chart
16225 });
16226
16227 layout.configure(chart, legend, legendOpts);
16228 layout.addBox(chart, legend);
16229 chart.legend = legend;
16230 }
16231
16232 return {
16233 id: 'legend',
16234
16235 beforeInit: function(chart) {
16236 var legendOpts = chart.options.legend;
16237
16238 if (legendOpts) {
16239 createNewLegendAndAttach(chart, legendOpts);
16240 }
16241 },
16242
16243 beforeUpdate: function(chart) {
16244 var legendOpts = chart.options.legend;
16245 var legend = chart.legend;
16246
16247 if (legendOpts) {
16248 helpers.mergeIf(legendOpts, defaults.global.legend);
16249
16250 if (legend) {
16251 layout.configure(chart, legend, legendOpts);
16252 legend.options = legendOpts;
16253 } else {
16254 createNewLegendAndAttach(chart, legendOpts);
16255 }
16256 } else if (legend) {
16257 layout.removeBox(chart, legend);
16258 delete chart.legend;
16259 }
16260 },
16261
16262 afterEvent: function(chart, e) {
16263 var legend = chart.legend;
16264 if (legend) {
16265 legend.handleEvent(e);
16266 }
16267 }
16268 };
16269 };
16270
16271 },{"25":25,"26":26,"45":45}],51:[function(require,module,exports){
16272 'use strict';
16273
16274 var defaults = require(25);
16275 var Element = require(26);
16276 var helpers = require(45);
16277
16278 defaults._set('global', {
16279 title: {
16280 display: false,
16281 fontStyle: 'bold',
16282 fullWidth: true,
16283 lineHeight: 1.2,
16284 padding: 10,
16285 position: 'top',
16286 text: '',
16287 weight: 2000 // by default greater than legend (1000) to be above
16288 }
16289 });
16290
16291 module.exports = function(Chart) {
16292
16293 var layout = Chart.layoutService;
16294 var noop = helpers.noop;
16295
16296 Chart.Title = Element.extend({
16297 initialize: function(config) {
16298 var me = this;
16299 helpers.extend(me, config);
16300
16301 // Contains hit boxes for each dataset (in dataset order)
16302 me.legendHitBoxes = [];
16303 },
16304
16305 // These methods are ordered by lifecycle. Utilities then follow.
16306
16307 beforeUpdate: noop,
16308 update: function(maxWidth, maxHeight, margins) {
16309 var me = this;
16310
16311 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
16312 me.beforeUpdate();
16313
16314 // Absorb the master measurements
16315 me.maxWidth = maxWidth;
16316 me.maxHeight = maxHeight;
16317 me.margins = margins;
16318
16319 // Dimensions
16320 me.beforeSetDimensions();
16321 me.setDimensions();
16322 me.afterSetDimensions();
16323 // Labels
16324 me.beforeBuildLabels();
16325 me.buildLabels();
16326 me.afterBuildLabels();
16327
16328 // Fit
16329 me.beforeFit();
16330 me.fit();
16331 me.afterFit();
16332 //
16333 me.afterUpdate();
16334
16335 return me.minSize;
16336
16337 },
16338 afterUpdate: noop,
16339
16340 //
16341
16342 beforeSetDimensions: noop,
16343 setDimensions: function() {
16344 var me = this;
16345 // Set the unconstrained dimension before label rotation
16346 if (me.isHorizontal()) {
16347 // Reset position before calculating rotation
16348 me.width = me.maxWidth;
16349 me.left = 0;
16350 me.right = me.width;
16351 } else {
16352 me.height = me.maxHeight;
16353
16354 // Reset position before calculating rotation
16355 me.top = 0;
16356 me.bottom = me.height;
16357 }
16358
16359 // Reset padding
16360 me.paddingLeft = 0;
16361 me.paddingTop = 0;
16362 me.paddingRight = 0;
16363 me.paddingBottom = 0;
16364
16365 // Reset minSize
16366 me.minSize = {
16367 width: 0,
16368 height: 0
16369 };
16370 },
16371 afterSetDimensions: noop,
16372
16373 //
16374
16375 beforeBuildLabels: noop,
16376 buildLabels: noop,
16377 afterBuildLabels: noop,
16378
16379 //
16380
16381 beforeFit: noop,
16382 fit: function() {
16383 var me = this;
16384 var valueOrDefault = helpers.valueOrDefault;
16385 var opts = me.options;
16386 var display = opts.display;
16387 var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
16388 var minSize = me.minSize;
16389 var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
16390 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
16391 var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
16392
16393 if (me.isHorizontal()) {
16394 minSize.width = me.maxWidth; // fill all the width
16395 minSize.height = textSize;
16396 } else {
16397 minSize.width = textSize;
16398 minSize.height = me.maxHeight; // fill all the height
16399 }
16400
16401 me.width = minSize.width;
16402 me.height = minSize.height;
16403
16404 },
16405 afterFit: noop,
16406
16407 // Shared Methods
16408 isHorizontal: function() {
16409 var pos = this.options.position;
16410 return pos === 'top' || pos === 'bottom';
16411 },
16412
16413 // Actually draw the title block on the canvas
16414 draw: function() {
16415 var me = this;
16416 var ctx = me.ctx;
16417 var valueOrDefault = helpers.valueOrDefault;
16418 var opts = me.options;
16419 var globalDefaults = defaults.global;
16420
16421 if (opts.display) {
16422 var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
16423 var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
16424 var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
16425 var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
16426 var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
16427 var offset = lineHeight / 2 + opts.padding;
16428 var rotation = 0;
16429 var top = me.top;
16430 var left = me.left;
16431 var bottom = me.bottom;
16432 var right = me.right;
16433 var maxWidth, titleX, titleY;
16434
16435 ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
16436 ctx.font = titleFont;
16437
16438 // Horizontal
16439 if (me.isHorizontal()) {
16440 titleX = left + ((right - left) / 2); // midpoint of the width
16441 titleY = top + offset;
16442 maxWidth = right - left;
16443 } else {
16444 titleX = opts.position === 'left' ? left + offset : right - offset;
16445 titleY = top + ((bottom - top) / 2);
16446 maxWidth = bottom - top;
16447 rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
16448 }
16449
16450 ctx.save();
16451 ctx.translate(titleX, titleY);
16452 ctx.rotate(rotation);
16453 ctx.textAlign = 'center';
16454 ctx.textBaseline = 'middle';
16455
16456 var text = opts.text;
16457 if (helpers.isArray(text)) {
16458 var y = 0;
16459 for (var i = 0; i < text.length; ++i) {
16460 ctx.fillText(text[i], 0, y, maxWidth);
16461 y += lineHeight;
16462 }
16463 } else {
16464 ctx.fillText(text, 0, 0, maxWidth);
16465 }
16466
16467 ctx.restore();
16468 }
16469 }
16470 });
16471
16472 function createNewTitleBlockAndAttach(chart, titleOpts) {
16473 var title = new Chart.Title({
16474 ctx: chart.ctx,
16475 options: titleOpts,
16476 chart: chart
16477 });
16478
16479 layout.configure(chart, title, titleOpts);
16480 layout.addBox(chart, title);
16481 chart.titleBlock = title;
16482 }
16483
16484 return {
16485 id: 'title',
16486
16487 beforeInit: function(chart) {
16488 var titleOpts = chart.options.title;
16489
16490 if (titleOpts) {
16491 createNewTitleBlockAndAttach(chart, titleOpts);
16492 }
16493 },
16494
16495 beforeUpdate: function(chart) {
16496 var titleOpts = chart.options.title;
16497 var titleBlock = chart.titleBlock;
16498
16499 if (titleOpts) {
16500 helpers.mergeIf(titleOpts, defaults.global.title);
16501
16502 if (titleBlock) {
16503 layout.configure(chart, titleBlock, titleOpts);
16504 titleBlock.options = titleOpts;
16505 } else {
16506 createNewTitleBlockAndAttach(chart, titleOpts);
16507 }
16508 } else if (titleBlock) {
16509 Chart.layoutService.removeBox(chart, titleBlock);
16510 delete chart.titleBlock;
16511 }
16512 }
16513 };
16514 };
16515
16516 },{"25":25,"26":26,"45":45}],52:[function(require,module,exports){
16517 'use strict';
16518
16519 module.exports = function(Chart) {
16520
16521 // Default config for a category scale
16522 var defaultConfig = {
16523 position: 'bottom'
16524 };
16525
16526 var DatasetScale = Chart.Scale.extend({
16527 /**
16528 * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
16529 * else fall back to data.labels
16530 * @private
16531 */
16532 getLabels: function() {
16533 var data = this.chart.data;
16534 return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
16535 },
16536
16537 determineDataLimits: function() {
16538 var me = this;
16539 var labels = me.getLabels();
16540 me.minIndex = 0;
16541 me.maxIndex = labels.length - 1;
16542 var findIndex;
16543
16544 if (me.options.ticks.min !== undefined) {
16545 // user specified min value
16546 findIndex = labels.indexOf(me.options.ticks.min);
16547 me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
16548 }
16549
16550 if (me.options.ticks.max !== undefined) {
16551 // user specified max value
16552 findIndex = labels.indexOf(me.options.ticks.max);
16553 me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
16554 }
16555
16556 me.min = labels[me.minIndex];
16557 me.max = labels[me.maxIndex];
16558 },
16559
16560 buildTicks: function() {
16561 var me = this;
16562 var labels = me.getLabels();
16563 // If we are viewing some subset of labels, slice the original array
16564 me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
16565 },
16566
16567 getLabelForIndex: function(index, datasetIndex) {
16568 var me = this;
16569 var data = me.chart.data;
16570 var isHorizontal = me.isHorizontal();
16571
16572 if (data.yLabels && !isHorizontal) {
16573 return me.getRightValue(data.datasets[datasetIndex].data[index]);
16574 }
16575 return me.ticks[index - me.minIndex];
16576 },
16577
16578 // Used to get data value locations. Value can either be an index or a numerical value
16579 getPixelForValue: function(value, index) {
16580 var me = this;
16581 var offset = me.options.offset;
16582 // 1 is added because we need the length but we have the indexes
16583 var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
16584
16585 // If value is a data object, then index is the index in the data array,
16586 // not the index of the scale. We need to change that.
16587 var valueCategory;
16588 if (value !== undefined && value !== null) {
16589 valueCategory = me.isHorizontal() ? value.x : value.y;
16590 }
16591 if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
16592 var labels = me.getLabels();
16593 value = valueCategory || value;
16594 var idx = labels.indexOf(value);
16595 index = idx !== -1 ? idx : index;
16596 }
16597
16598 if (me.isHorizontal()) {
16599 var valueWidth = me.width / offsetAmt;
16600 var widthOffset = (valueWidth * (index - me.minIndex));
16601
16602 if (offset) {
16603 widthOffset += (valueWidth / 2);
16604 }
16605
16606 return me.left + Math.round(widthOffset);
16607 }
16608 var valueHeight = me.height / offsetAmt;
16609 var heightOffset = (valueHeight * (index - me.minIndex));
16610
16611 if (offset) {
16612 heightOffset += (valueHeight / 2);
16613 }
16614
16615 return me.top + Math.round(heightOffset);
16616 },
16617 getPixelForTick: function(index) {
16618 return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
16619 },
16620 getValueForPixel: function(pixel) {
16621 var me = this;
16622 var offset = me.options.offset;
16623 var value;
16624 var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
16625 var horz = me.isHorizontal();
16626 var valueDimension = (horz ? me.width : me.height) / offsetAmt;
16627
16628 pixel -= horz ? me.left : me.top;
16629
16630 if (offset) {
16631 pixel -= (valueDimension / 2);
16632 }
16633
16634 if (pixel <= 0) {
16635 value = 0;
16636 } else {
16637 value = Math.round(pixel / valueDimension);
16638 }
16639
16640 return value + me.minIndex;
16641 },
16642 getBasePixel: function() {
16643 return this.bottom;
16644 }
16645 });
16646
16647 Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);
16648
16649 };
16650
16651 },{}],53:[function(require,module,exports){
16652 'use strict';
16653
16654 var defaults = require(25);
16655 var helpers = require(45);
16656 var Ticks = require(34);
16657
16658 module.exports = function(Chart) {
16659
16660 var defaultConfig = {
16661 position: 'left',
16662 ticks: {
16663 callback: Ticks.formatters.linear
16664 }
16665 };
16666
16667 var LinearScale = Chart.LinearScaleBase.extend({
16668
16669 determineDataLimits: function() {
16670 var me = this;
16671 var opts = me.options;
16672 var chart = me.chart;
16673 var data = chart.data;
16674 var datasets = data.datasets;
16675 var isHorizontal = me.isHorizontal();
16676 var DEFAULT_MIN = 0;
16677 var DEFAULT_MAX = 1;
16678
16679 function IDMatches(meta) {
16680 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
16681 }
16682
16683 // First Calculate the range
16684 me.min = null;
16685 me.max = null;
16686
16687 var hasStacks = opts.stacked;
16688 if (hasStacks === undefined) {
16689 helpers.each(datasets, function(dataset, datasetIndex) {
16690 if (hasStacks) {
16691 return;
16692 }
16693
16694 var meta = chart.getDatasetMeta(datasetIndex);
16695 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
16696 meta.stack !== undefined) {
16697 hasStacks = true;
16698 }
16699 });
16700 }
16701
16702 if (opts.stacked || hasStacks) {
16703 var valuesPerStack = {};
16704
16705 helpers.each(datasets, function(dataset, datasetIndex) {
16706 var meta = chart.getDatasetMeta(datasetIndex);
16707 var key = [
16708 meta.type,
16709 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
16710 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
16711 meta.stack
16712 ].join('.');
16713
16714 if (valuesPerStack[key] === undefined) {
16715 valuesPerStack[key] = {
16716 positiveValues: [],
16717 negativeValues: []
16718 };
16719 }
16720
16721 // Store these per type
16722 var positiveValues = valuesPerStack[key].positiveValues;
16723 var negativeValues = valuesPerStack[key].negativeValues;
16724
16725 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
16726 helpers.each(dataset.data, function(rawValue, index) {
16727 var value = +me.getRightValue(rawValue);
16728 if (isNaN(value) || meta.data[index].hidden) {
16729 return;
16730 }
16731
16732 positiveValues[index] = positiveValues[index] || 0;
16733 negativeValues[index] = negativeValues[index] || 0;
16734
16735 if (opts.relativePoints) {
16736 positiveValues[index] = 100;
16737 } else if (value < 0) {
16738 negativeValues[index] += value;
16739 } else {
16740 positiveValues[index] += value;
16741 }
16742 });
16743 }
16744 });
16745
16746 helpers.each(valuesPerStack, function(valuesForType) {
16747 var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
16748 var minVal = helpers.min(values);
16749 var maxVal = helpers.max(values);
16750 me.min = me.min === null ? minVal : Math.min(me.min, minVal);
16751 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
16752 });
16753
16754 } else {
16755 helpers.each(datasets, function(dataset, datasetIndex) {
16756 var meta = chart.getDatasetMeta(datasetIndex);
16757 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
16758 helpers.each(dataset.data, function(rawValue, index) {
16759 var value = +me.getRightValue(rawValue);
16760 if (isNaN(value) || meta.data[index].hidden) {
16761 return;
16762 }
16763
16764 if (me.min === null) {
16765 me.min = value;
16766 } else if (value < me.min) {
16767 me.min = value;
16768 }
16769
16770 if (me.max === null) {
16771 me.max = value;
16772 } else if (value > me.max) {
16773 me.max = value;
16774 }
16775 });
16776 }
16777 });
16778 }
16779
16780 me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
16781 me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
16782
16783 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
16784 this.handleTickRangeOptions();
16785 },
16786 getTickLimit: function() {
16787 var maxTicks;
16788 var me = this;
16789 var tickOpts = me.options.ticks;
16790
16791 if (me.isHorizontal()) {
16792 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
16793 } else {
16794 // The factor of 2 used to scale the font size has been experimentally determined.
16795 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
16796 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
16797 }
16798
16799 return maxTicks;
16800 },
16801 // Called after the ticks are built. We need
16802 handleDirectionalChanges: function() {
16803 if (!this.isHorizontal()) {
16804 // We are in a vertical orientation. The top value is the highest. So reverse the array
16805 this.ticks.reverse();
16806 }
16807 },
16808 getLabelForIndex: function(index, datasetIndex) {
16809 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
16810 },
16811 // Utils
16812 getPixelForValue: function(value) {
16813 // This must be called after fit has been run so that
16814 // this.left, this.top, this.right, and this.bottom have been defined
16815 var me = this;
16816 var start = me.start;
16817
16818 var rightValue = +me.getRightValue(value);
16819 var pixel;
16820 var range = me.end - start;
16821
16822 if (me.isHorizontal()) {
16823 pixel = me.left + (me.width / range * (rightValue - start));
16824 return Math.round(pixel);
16825 }
16826
16827 pixel = me.bottom - (me.height / range * (rightValue - start));
16828 return Math.round(pixel);
16829 },
16830 getValueForPixel: function(pixel) {
16831 var me = this;
16832 var isHorizontal = me.isHorizontal();
16833 var innerDimension = isHorizontal ? me.width : me.height;
16834 var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
16835 return me.start + ((me.end - me.start) * offset);
16836 },
16837 getPixelForTick: function(index) {
16838 return this.getPixelForValue(this.ticksAsNumbers[index]);
16839 }
16840 });
16841 Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);
16842
16843 };
16844
16845 },{"25":25,"34":34,"45":45}],54:[function(require,module,exports){
16846 'use strict';
16847
16848 var helpers = require(45);
16849 var Ticks = require(34);
16850
16851 module.exports = function(Chart) {
16852
16853 var noop = helpers.noop;
16854
16855 Chart.LinearScaleBase = Chart.Scale.extend({
16856 getRightValue: function(value) {
16857 if (typeof value === 'string') {
16858 return +value;
16859 }
16860 return Chart.Scale.prototype.getRightValue.call(this, value);
16861 },
16862
16863 handleTickRangeOptions: function() {
16864 var me = this;
16865 var opts = me.options;
16866 var tickOpts = opts.ticks;
16867
16868 // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
16869 // do nothing since that would make the chart weird. If the user really wants a weird chart
16870 // axis, they can manually override it
16871 if (tickOpts.beginAtZero) {
16872 var minSign = helpers.sign(me.min);
16873 var maxSign = helpers.sign(me.max);
16874
16875 if (minSign < 0 && maxSign < 0) {
16876 // move the top up to 0
16877 me.max = 0;
16878 } else if (minSign > 0 && maxSign > 0) {
16879 // move the bottom down to 0
16880 me.min = 0;
16881 }
16882 }
16883
16884 var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
16885 var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
16886
16887 if (tickOpts.min !== undefined) {
16888 me.min = tickOpts.min;
16889 } else if (tickOpts.suggestedMin !== undefined) {
16890 if (me.min === null) {
16891 me.min = tickOpts.suggestedMin;
16892 } else {
16893 me.min = Math.min(me.min, tickOpts.suggestedMin);
16894 }
16895 }
16896
16897 if (tickOpts.max !== undefined) {
16898 me.max = tickOpts.max;
16899 } else if (tickOpts.suggestedMax !== undefined) {
16900 if (me.max === null) {
16901 me.max = tickOpts.suggestedMax;
16902 } else {
16903 me.max = Math.max(me.max, tickOpts.suggestedMax);
16904 }
16905 }
16906
16907 if (setMin !== setMax) {
16908 // We set the min or the max but not both.
16909 // So ensure that our range is good
16910 // Inverted or 0 length range can happen when
16911 // ticks.min is set, and no datasets are visible
16912 if (me.min >= me.max) {
16913 if (setMin) {
16914 me.max = me.min + 1;
16915 } else {
16916 me.min = me.max - 1;
16917 }
16918 }
16919 }
16920
16921 if (me.min === me.max) {
16922 me.max++;
16923
16924 if (!tickOpts.beginAtZero) {
16925 me.min--;
16926 }
16927 }
16928 },
16929 getTickLimit: noop,
16930 handleDirectionalChanges: noop,
16931
16932 buildTicks: function() {
16933 var me = this;
16934 var opts = me.options;
16935 var tickOpts = opts.ticks;
16936
16937 // Figure out what the max number of ticks we can support it is based on the size of
16938 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
16939 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
16940 // the graph. Make sure we always have at least 2 ticks
16941 var maxTicks = me.getTickLimit();
16942 maxTicks = Math.max(2, maxTicks);
16943
16944 var numericGeneratorOptions = {
16945 maxTicks: maxTicks,
16946 min: tickOpts.min,
16947 max: tickOpts.max,
16948 stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
16949 };
16950 var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me);
16951
16952 me.handleDirectionalChanges();
16953
16954 // At this point, we need to update our max and min given the tick values since we have expanded the
16955 // range of the scale
16956 me.max = helpers.max(ticks);
16957 me.min = helpers.min(ticks);
16958
16959 if (tickOpts.reverse) {
16960 ticks.reverse();
16961
16962 me.start = me.max;
16963 me.end = me.min;
16964 } else {
16965 me.start = me.min;
16966 me.end = me.max;
16967 }
16968 },
16969 convertTicksToLabels: function() {
16970 var me = this;
16971 me.ticksAsNumbers = me.ticks.slice();
16972 me.zeroLineIndex = me.ticks.indexOf(0);
16973
16974 Chart.Scale.prototype.convertTicksToLabels.call(me);
16975 }
16976 });
16977 };
16978
16979 },{"34":34,"45":45}],55:[function(require,module,exports){
16980 'use strict';
16981
16982 var helpers = require(45);
16983 var Ticks = require(34);
16984
16985 module.exports = function(Chart) {
16986
16987 var defaultConfig = {
16988 position: 'left',
16989
16990 // label settings
16991 ticks: {
16992 callback: Ticks.formatters.logarithmic
16993 }
16994 };
16995
16996 var LogarithmicScale = Chart.Scale.extend({
16997 determineDataLimits: function() {
16998 var me = this;
16999 var opts = me.options;
17000 var tickOpts = opts.ticks;
17001 var chart = me.chart;
17002 var data = chart.data;
17003 var datasets = data.datasets;
17004 var valueOrDefault = helpers.valueOrDefault;
17005 var isHorizontal = me.isHorizontal();
17006 function IDMatches(meta) {
17007 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
17008 }
17009
17010 // Calculate Range
17011 me.min = null;
17012 me.max = null;
17013 me.minNotZero = null;
17014
17015 var hasStacks = opts.stacked;
17016 if (hasStacks === undefined) {
17017 helpers.each(datasets, function(dataset, datasetIndex) {
17018 if (hasStacks) {
17019 return;
17020 }
17021
17022 var meta = chart.getDatasetMeta(datasetIndex);
17023 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
17024 meta.stack !== undefined) {
17025 hasStacks = true;
17026 }
17027 });
17028 }
17029
17030 if (opts.stacked || hasStacks) {
17031 var valuesPerStack = {};
17032
17033 helpers.each(datasets, function(dataset, datasetIndex) {
17034 var meta = chart.getDatasetMeta(datasetIndex);
17035 var key = [
17036 meta.type,
17037 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
17038 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
17039 meta.stack
17040 ].join('.');
17041
17042 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
17043 if (valuesPerStack[key] === undefined) {
17044 valuesPerStack[key] = [];
17045 }
17046
17047 helpers.each(dataset.data, function(rawValue, index) {
17048 var values = valuesPerStack[key];
17049 var value = +me.getRightValue(rawValue);
17050 if (isNaN(value) || meta.data[index].hidden) {
17051 return;
17052 }
17053
17054 values[index] = values[index] || 0;
17055
17056 if (opts.relativePoints) {
17057 values[index] = 100;
17058 } else {
17059 // Don't need to split positive and negative since the log scale can't handle a 0 crossing
17060 values[index] += value;
17061 }
17062 });
17063 }
17064 });
17065
17066 helpers.each(valuesPerStack, function(valuesForType) {
17067 var minVal = helpers.min(valuesForType);
17068 var maxVal = helpers.max(valuesForType);
17069 me.min = me.min === null ? minVal : Math.min(me.min, minVal);
17070 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
17071 });
17072
17073 } else {
17074 helpers.each(datasets, function(dataset, datasetIndex) {
17075 var meta = chart.getDatasetMeta(datasetIndex);
17076 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
17077 helpers.each(dataset.data, function(rawValue, index) {
17078 var value = +me.getRightValue(rawValue);
17079 if (isNaN(value) || meta.data[index].hidden) {
17080 return;
17081 }
17082
17083 if (me.min === null) {
17084 me.min = value;
17085 } else if (value < me.min) {
17086 me.min = value;
17087 }
17088
17089 if (me.max === null) {
17090 me.max = value;
17091 } else if (value > me.max) {
17092 me.max = value;
17093 }
17094
17095 if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
17096 me.minNotZero = value;
17097 }
17098 });
17099 }
17100 });
17101 }
17102
17103 me.min = valueOrDefault(tickOpts.min, me.min);
17104 me.max = valueOrDefault(tickOpts.max, me.max);
17105
17106 if (me.min === me.max) {
17107 if (me.min !== 0 && me.min !== null) {
17108 me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
17109 me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
17110 } else {
17111 me.min = 1;
17112 me.max = 10;
17113 }
17114 }
17115 },
17116 buildTicks: function() {
17117 var me = this;
17118 var opts = me.options;
17119 var tickOpts = opts.ticks;
17120
17121 var generationOptions = {
17122 min: tickOpts.min,
17123 max: tickOpts.max
17124 };
17125 var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me);
17126
17127 if (!me.isHorizontal()) {
17128 // We are in a vertical orientation. The top value is the highest. So reverse the array
17129 ticks.reverse();
17130 }
17131
17132 // At this point, we need to update our max and min given the tick values since we have expanded the
17133 // range of the scale
17134 me.max = helpers.max(ticks);
17135 me.min = helpers.min(ticks);
17136
17137 if (tickOpts.reverse) {
17138 ticks.reverse();
17139
17140 me.start = me.max;
17141 me.end = me.min;
17142 } else {
17143 me.start = me.min;
17144 me.end = me.max;
17145 }
17146 },
17147 convertTicksToLabels: function() {
17148 this.tickValues = this.ticks.slice();
17149
17150 Chart.Scale.prototype.convertTicksToLabels.call(this);
17151 },
17152 // Get the correct tooltip label
17153 getLabelForIndex: function(index, datasetIndex) {
17154 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
17155 },
17156 getPixelForTick: function(index) {
17157 return this.getPixelForValue(this.tickValues[index]);
17158 },
17159 getPixelForValue: function(value) {
17160 var me = this;
17161 var start = me.start;
17162 var newVal = +me.getRightValue(value);
17163 var opts = me.options;
17164 var tickOpts = opts.ticks;
17165 var innerDimension, pixel, range;
17166
17167 if (me.isHorizontal()) {
17168 range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
17169 if (newVal === 0) {
17170 pixel = me.left;
17171 } else {
17172 innerDimension = me.width;
17173 pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
17174 }
17175 } else {
17176 // Bottom - top since pixels increase downward on a screen
17177 innerDimension = me.height;
17178 if (start === 0 && !tickOpts.reverse) {
17179 range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
17180 if (newVal === start) {
17181 pixel = me.bottom;
17182 } else if (newVal === me.minNotZero) {
17183 pixel = me.bottom - innerDimension * 0.02;
17184 } else {
17185 pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
17186 }
17187 } else if (me.end === 0 && tickOpts.reverse) {
17188 range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
17189 if (newVal === me.end) {
17190 pixel = me.top;
17191 } else if (newVal === me.minNotZero) {
17192 pixel = me.top + innerDimension * 0.02;
17193 } else {
17194 pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
17195 }
17196 } else if (newVal === 0) {
17197 pixel = tickOpts.reverse ? me.top : me.bottom;
17198 } else {
17199 range = helpers.log10(me.end) - helpers.log10(start);
17200 innerDimension = me.height;
17201 pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
17202 }
17203 }
17204 return pixel;
17205 },
17206 getValueForPixel: function(pixel) {
17207 var me = this;
17208 var range = helpers.log10(me.end) - helpers.log10(me.start);
17209 var value, innerDimension;
17210
17211 if (me.isHorizontal()) {
17212 innerDimension = me.width;
17213 value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
17214 } else { // todo: if start === 0
17215 innerDimension = me.height;
17216 value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
17217 }
17218 return value;
17219 }
17220 });
17221 Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
17222
17223 };
17224
17225 },{"34":34,"45":45}],56:[function(require,module,exports){
17226 'use strict';
17227
17228 var defaults = require(25);
17229 var helpers = require(45);
17230 var Ticks = require(34);
17231
17232 module.exports = function(Chart) {
17233
17234 var globalDefaults = defaults.global;
17235
17236 var defaultConfig = {
17237 display: true,
17238
17239 // Boolean - Whether to animate scaling the chart from the centre
17240 animate: true,
17241 position: 'chartArea',
17242
17243 angleLines: {
17244 display: true,
17245 color: 'rgba(0, 0, 0, 0.1)',
17246 lineWidth: 1
17247 },
17248
17249 gridLines: {
17250 circular: false
17251 },
17252
17253 // label settings
17254 ticks: {
17255 // Boolean - Show a backdrop to the scale label
17256 showLabelBackdrop: true,
17257
17258 // String - The colour of the label backdrop
17259 backdropColor: 'rgba(255,255,255,0.75)',
17260
17261 // Number - The backdrop padding above & below the label in pixels
17262 backdropPaddingY: 2,
17263
17264 // Number - The backdrop padding to the side of the label in pixels
17265 backdropPaddingX: 2,
17266
17267 callback: Ticks.formatters.linear
17268 },
17269
17270 pointLabels: {
17271 // Boolean - if true, show point labels
17272 display: true,
17273
17274 // Number - Point label font size in pixels
17275 fontSize: 10,
17276
17277 // Function - Used to convert point labels
17278 callback: function(label) {
17279 return label;
17280 }
17281 }
17282 };
17283
17284 function getValueCount(scale) {
17285 var opts = scale.options;
17286 return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
17287 }
17288
17289 function getPointLabelFontOptions(scale) {
17290 var pointLabelOptions = scale.options.pointLabels;
17291 var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
17292 var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
17293 var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
17294 var font = helpers.fontString(fontSize, fontStyle, fontFamily);
17295
17296 return {
17297 size: fontSize,
17298 style: fontStyle,
17299 family: fontFamily,
17300 font: font
17301 };
17302 }
17303
17304 function measureLabelSize(ctx, fontSize, label) {
17305 if (helpers.isArray(label)) {
17306 return {
17307 w: helpers.longestText(ctx, ctx.font, label),
17308 h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
17309 };
17310 }
17311
17312 return {
17313 w: ctx.measureText(label).width,
17314 h: fontSize
17315 };
17316 }
17317
17318 function determineLimits(angle, pos, size, min, max) {
17319 if (angle === min || angle === max) {
17320 return {
17321 start: pos - (size / 2),
17322 end: pos + (size / 2)
17323 };
17324 } else if (angle < min || angle > max) {
17325 return {
17326 start: pos - size - 5,
17327 end: pos
17328 };
17329 }
17330
17331 return {
17332 start: pos,
17333 end: pos + size + 5
17334 };
17335 }
17336
17337 /**
17338 * Helper function to fit a radial linear scale with point labels
17339 */
17340 function fitWithPointLabels(scale) {
17341 /*
17342 * Right, this is really confusing and there is a lot of maths going on here
17343 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
17344 *
17345 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
17346 *
17347 * Solution:
17348 *
17349 * We assume the radius of the polygon is half the size of the canvas at first
17350 * at each index we check if the text overlaps.
17351 *
17352 * Where it does, we store that angle and that index.
17353 *
17354 * After finding the largest index and angle we calculate how much we need to remove
17355 * from the shape radius to move the point inwards by that x.
17356 *
17357 * We average the left and right distances to get the maximum shape radius that can fit in the box
17358 * along with labels.
17359 *
17360 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
17361 * on each side, removing that from the size, halving it and adding the left x protrusion width.
17362 *
17363 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
17364 * and position it in the most space efficient manner
17365 *
17366 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
17367 */
17368
17369 var plFont = getPointLabelFontOptions(scale);
17370
17371 // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
17372 // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
17373 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
17374 var furthestLimits = {
17375 r: scale.width,
17376 l: 0,
17377 t: scale.height,
17378 b: 0
17379 };
17380 var furthestAngles = {};
17381 var i, textSize, pointPosition;
17382
17383 scale.ctx.font = plFont.font;
17384 scale._pointLabelSizes = [];
17385
17386 var valueCount = getValueCount(scale);
17387 for (i = 0; i < valueCount; i++) {
17388 pointPosition = scale.getPointPosition(i, largestPossibleRadius);
17389 textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
17390 scale._pointLabelSizes[i] = textSize;
17391
17392 // Add quarter circle to make degree 0 mean top of circle
17393 var angleRadians = scale.getIndexAngle(i);
17394 var angle = helpers.toDegrees(angleRadians) % 360;
17395 var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
17396 var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
17397
17398 if (hLimits.start < furthestLimits.l) {
17399 furthestLimits.l = hLimits.start;
17400 furthestAngles.l = angleRadians;
17401 }
17402
17403 if (hLimits.end > furthestLimits.r) {
17404 furthestLimits.r = hLimits.end;
17405 furthestAngles.r = angleRadians;
17406 }
17407
17408 if (vLimits.start < furthestLimits.t) {
17409 furthestLimits.t = vLimits.start;
17410 furthestAngles.t = angleRadians;
17411 }
17412
17413 if (vLimits.end > furthestLimits.b) {
17414 furthestLimits.b = vLimits.end;
17415 furthestAngles.b = angleRadians;
17416 }
17417 }
17418
17419 scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
17420 }
17421
17422 /**
17423 * Helper function to fit a radial linear scale with no point labels
17424 */
17425 function fit(scale) {
17426 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
17427 scale.drawingArea = Math.round(largestPossibleRadius);
17428 scale.setCenterPoint(0, 0, 0, 0);
17429 }
17430
17431 function getTextAlignForAngle(angle) {
17432 if (angle === 0 || angle === 180) {
17433 return 'center';
17434 } else if (angle < 180) {
17435 return 'left';
17436 }
17437
17438 return 'right';
17439 }
17440
17441 function fillText(ctx, text, position, fontSize) {
17442 if (helpers.isArray(text)) {
17443 var y = position.y;
17444 var spacing = 1.5 * fontSize;
17445
17446 for (var i = 0; i < text.length; ++i) {
17447 ctx.fillText(text[i], position.x, y);
17448 y += spacing;
17449 }
17450 } else {
17451 ctx.fillText(text, position.x, position.y);
17452 }
17453 }
17454
17455 function adjustPointPositionForLabelHeight(angle, textSize, position) {
17456 if (angle === 90 || angle === 270) {
17457 position.y -= (textSize.h / 2);
17458 } else if (angle > 270 || angle < 90) {
17459 position.y -= textSize.h;
17460 }
17461 }
17462
17463 function drawPointLabels(scale) {
17464 var ctx = scale.ctx;
17465 var valueOrDefault = helpers.valueOrDefault;
17466 var opts = scale.options;
17467 var angleLineOpts = opts.angleLines;
17468 var pointLabelOpts = opts.pointLabels;
17469
17470 ctx.lineWidth = angleLineOpts.lineWidth;
17471 ctx.strokeStyle = angleLineOpts.color;
17472
17473 var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
17474
17475 // Point Label Font
17476 var plFont = getPointLabelFontOptions(scale);
17477
17478 ctx.textBaseline = 'top';
17479
17480 for (var i = getValueCount(scale) - 1; i >= 0; i--) {
17481 if (angleLineOpts.display) {
17482 var outerPosition = scale.getPointPosition(i, outerDistance);
17483 ctx.beginPath();
17484 ctx.moveTo(scale.xCenter, scale.yCenter);
17485 ctx.lineTo(outerPosition.x, outerPosition.y);
17486 ctx.stroke();
17487 ctx.closePath();
17488 }
17489
17490 if (pointLabelOpts.display) {
17491 // Extra 3px out for some label spacing
17492 var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
17493
17494 // Keep this in loop since we may support array properties here
17495 var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
17496 ctx.font = plFont.font;
17497 ctx.fillStyle = pointLabelFontColor;
17498
17499 var angleRadians = scale.getIndexAngle(i);
17500 var angle = helpers.toDegrees(angleRadians);
17501 ctx.textAlign = getTextAlignForAngle(angle);
17502 adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
17503 fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
17504 }
17505 }
17506 }
17507
17508 function drawRadiusLine(scale, gridLineOpts, radius, index) {
17509 var ctx = scale.ctx;
17510 ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
17511 ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
17512
17513 if (scale.options.gridLines.circular) {
17514 // Draw circular arcs between the points
17515 ctx.beginPath();
17516 ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
17517 ctx.closePath();
17518 ctx.stroke();
17519 } else {
17520 // Draw straight lines connecting each index
17521 var valueCount = getValueCount(scale);
17522
17523 if (valueCount === 0) {
17524 return;
17525 }
17526
17527 ctx.beginPath();
17528 var pointPosition = scale.getPointPosition(0, radius);
17529 ctx.moveTo(pointPosition.x, pointPosition.y);
17530
17531 for (var i = 1; i < valueCount; i++) {
17532 pointPosition = scale.getPointPosition(i, radius);
17533 ctx.lineTo(pointPosition.x, pointPosition.y);
17534 }
17535
17536 ctx.closePath();
17537 ctx.stroke();
17538 }
17539 }
17540
17541 function numberOrZero(param) {
17542 return helpers.isNumber(param) ? param : 0;
17543 }
17544
17545 var LinearRadialScale = Chart.LinearScaleBase.extend({
17546 setDimensions: function() {
17547 var me = this;
17548 var opts = me.options;
17549 var tickOpts = opts.ticks;
17550 // Set the unconstrained dimension before label rotation
17551 me.width = me.maxWidth;
17552 me.height = me.maxHeight;
17553 me.xCenter = Math.round(me.width / 2);
17554 me.yCenter = Math.round(me.height / 2);
17555
17556 var minSize = helpers.min([me.height, me.width]);
17557 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
17558 me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
17559 },
17560 determineDataLimits: function() {
17561 var me = this;
17562 var chart = me.chart;
17563 var min = Number.POSITIVE_INFINITY;
17564 var max = Number.NEGATIVE_INFINITY;
17565
17566 helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
17567 if (chart.isDatasetVisible(datasetIndex)) {
17568 var meta = chart.getDatasetMeta(datasetIndex);
17569
17570 helpers.each(dataset.data, function(rawValue, index) {
17571 var value = +me.getRightValue(rawValue);
17572 if (isNaN(value) || meta.data[index].hidden) {
17573 return;
17574 }
17575
17576 min = Math.min(value, min);
17577 max = Math.max(value, max);
17578 });
17579 }
17580 });
17581
17582 me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
17583 me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
17584
17585 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
17586 me.handleTickRangeOptions();
17587 },
17588 getTickLimit: function() {
17589 var tickOpts = this.options.ticks;
17590 var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
17591 return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
17592 },
17593 convertTicksToLabels: function() {
17594 var me = this;
17595
17596 Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
17597
17598 // Point labels
17599 me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
17600 },
17601 getLabelForIndex: function(index, datasetIndex) {
17602 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
17603 },
17604 fit: function() {
17605 if (this.options.pointLabels.display) {
17606 fitWithPointLabels(this);
17607 } else {
17608 fit(this);
17609 }
17610 },
17611 /**
17612 * Set radius reductions and determine new radius and center point
17613 * @private
17614 */
17615 setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
17616 var me = this;
17617 var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
17618 var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
17619 var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
17620 var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
17621
17622 radiusReductionLeft = numberOrZero(radiusReductionLeft);
17623 radiusReductionRight = numberOrZero(radiusReductionRight);
17624 radiusReductionTop = numberOrZero(radiusReductionTop);
17625 radiusReductionBottom = numberOrZero(radiusReductionBottom);
17626
17627 me.drawingArea = Math.min(
17628 Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
17629 Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
17630 me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
17631 },
17632 setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
17633 var me = this;
17634 var maxRight = me.width - rightMovement - me.drawingArea;
17635 var maxLeft = leftMovement + me.drawingArea;
17636 var maxTop = topMovement + me.drawingArea;
17637 var maxBottom = me.height - bottomMovement - me.drawingArea;
17638
17639 me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
17640 me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
17641 },
17642
17643 getIndexAngle: function(index) {
17644 var angleMultiplier = (Math.PI * 2) / getValueCount(this);
17645 var startAngle = this.chart.options && this.chart.options.startAngle ?
17646 this.chart.options.startAngle :
17647 0;
17648
17649 var startAngleRadians = startAngle * Math.PI * 2 / 360;
17650
17651 // Start from the top instead of right, so remove a quarter of the circle
17652 return index * angleMultiplier + startAngleRadians;
17653 },
17654 getDistanceFromCenterForValue: function(value) {
17655 var me = this;
17656
17657 if (value === null) {
17658 return 0; // null always in center
17659 }
17660
17661 // Take into account half font size + the yPadding of the top value
17662 var scalingFactor = me.drawingArea / (me.max - me.min);
17663 if (me.options.ticks.reverse) {
17664 return (me.max - value) * scalingFactor;
17665 }
17666 return (value - me.min) * scalingFactor;
17667 },
17668 getPointPosition: function(index, distanceFromCenter) {
17669 var me = this;
17670 var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
17671 return {
17672 x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
17673 y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
17674 };
17675 },
17676 getPointPositionForValue: function(index, value) {
17677 return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
17678 },
17679
17680 getBasePosition: function() {
17681 var me = this;
17682 var min = me.min;
17683 var max = me.max;
17684
17685 return me.getPointPositionForValue(0,
17686 me.beginAtZero ? 0 :
17687 min < 0 && max < 0 ? max :
17688 min > 0 && max > 0 ? min :
17689 0);
17690 },
17691
17692 draw: function() {
17693 var me = this;
17694 var opts = me.options;
17695 var gridLineOpts = opts.gridLines;
17696 var tickOpts = opts.ticks;
17697 var valueOrDefault = helpers.valueOrDefault;
17698
17699 if (opts.display) {
17700 var ctx = me.ctx;
17701 var startAngle = this.getIndexAngle(0);
17702
17703 // Tick Font
17704 var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
17705 var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
17706 var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
17707 var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
17708
17709 helpers.each(me.ticks, function(label, index) {
17710 // Don't draw a centre value (if it is minimum)
17711 if (index > 0 || tickOpts.reverse) {
17712 var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
17713
17714 // Draw circular lines around the scale
17715 if (gridLineOpts.display && index !== 0) {
17716 drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
17717 }
17718
17719 if (tickOpts.display) {
17720 var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
17721 ctx.font = tickLabelFont;
17722
17723 ctx.save();
17724 ctx.translate(me.xCenter, me.yCenter);
17725 ctx.rotate(startAngle);
17726
17727 if (tickOpts.showLabelBackdrop) {
17728 var labelWidth = ctx.measureText(label).width;
17729 ctx.fillStyle = tickOpts.backdropColor;
17730 ctx.fillRect(
17731 -labelWidth / 2 - tickOpts.backdropPaddingX,
17732 -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
17733 labelWidth + tickOpts.backdropPaddingX * 2,
17734 tickFontSize + tickOpts.backdropPaddingY * 2
17735 );
17736 }
17737
17738 ctx.textAlign = 'center';
17739 ctx.textBaseline = 'middle';
17740 ctx.fillStyle = tickFontColor;
17741 ctx.fillText(label, 0, -yCenterOffset);
17742 ctx.restore();
17743 }
17744 }
17745 });
17746
17747 if (opts.angleLines.display || opts.pointLabels.display) {
17748 drawPointLabels(me);
17749 }
17750 }
17751 }
17752 });
17753 Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
17754
17755 };
17756
17757 },{"25":25,"34":34,"45":45}],57:[function(require,module,exports){
17758 /* global window: false */
17759 'use strict';
17760
17761 var moment = require(6);
17762 moment = typeof moment === 'function' ? moment : window.moment;
17763
17764 var defaults = require(25);
17765 var helpers = require(45);
17766
17767 // Integer constants are from the ES6 spec.
17768 var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
17769 var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
17770
17771 var INTERVALS = {
17772 millisecond: {
17773 major: true,
17774 size: 1,
17775 steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
17776 },
17777 second: {
17778 major: true,
17779 size: 1000,
17780 steps: [1, 2, 5, 10, 30]
17781 },
17782 minute: {
17783 major: true,
17784 size: 60000,
17785 steps: [1, 2, 5, 10, 30]
17786 },
17787 hour: {
17788 major: true,
17789 size: 3600000,
17790 steps: [1, 2, 3, 6, 12]
17791 },
17792 day: {
17793 major: true,
17794 size: 86400000,
17795 steps: [1, 2, 5]
17796 },
17797 week: {
17798 major: false,
17799 size: 604800000,
17800 steps: [1, 2, 3, 4]
17801 },
17802 month: {
17803 major: true,
17804 size: 2.628e9,
17805 steps: [1, 2, 3]
17806 },
17807 quarter: {
17808 major: false,
17809 size: 7.884e9,
17810 steps: [1, 2, 3, 4]
17811 },
17812 year: {
17813 major: true,
17814 size: 3.154e10
17815 }
17816 };
17817
17818 var UNITS = Object.keys(INTERVALS);
17819
17820 function sorter(a, b) {
17821 return a - b;
17822 }
17823
17824 function arrayUnique(items) {
17825 var hash = {};
17826 var out = [];
17827 var i, ilen, item;
17828
17829 for (i = 0, ilen = items.length; i < ilen; ++i) {
17830 item = items[i];
17831 if (!hash[item]) {
17832 hash[item] = true;
17833 out.push(item);
17834 }
17835 }
17836
17837 return out;
17838 }
17839
17840 /**
17841 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
17842 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
17843 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
17844 * extremity (left + width or top + height). Note that it would be more optimized to directly
17845 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
17846 * to create the lookup table. The table ALWAYS contains at least two items: min and max.
17847 *
17848 * @param {Number[]} timestamps - timestamps sorted from lowest to highest.
17849 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
17850 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
17851 * If 'series', timestamps will be positioned at the same distance from each other. In this
17852 * case, only timestamps that break the time linearity are registered, meaning that in the
17853 * best case, all timestamps are linear, the table contains only min and max.
17854 */
17855 function buildLookupTable(timestamps, min, max, distribution) {
17856 if (distribution === 'linear' || !timestamps.length) {
17857 return [
17858 {time: min, pos: 0},
17859 {time: max, pos: 1}
17860 ];
17861 }
17862
17863 var table = [];
17864 var items = [min];
17865 var i, ilen, prev, curr, next;
17866
17867 for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
17868 curr = timestamps[i];
17869 if (curr > min && curr < max) {
17870 items.push(curr);
17871 }
17872 }
17873
17874 items.push(max);
17875
17876 for (i = 0, ilen = items.length; i < ilen; ++i) {
17877 next = items[i + 1];
17878 prev = items[i - 1];
17879 curr = items[i];
17880
17881 // only add points that breaks the scale linearity
17882 if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
17883 table.push({time: curr, pos: i / (ilen - 1)});
17884 }
17885 }
17886
17887 return table;
17888 }
17889
17890 // @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
17891 function lookup(table, key, value) {
17892 var lo = 0;
17893 var hi = table.length - 1;
17894 var mid, i0, i1;
17895
17896 while (lo >= 0 && lo <= hi) {
17897 mid = (lo + hi) >> 1;
17898 i0 = table[mid - 1] || null;
17899 i1 = table[mid];
17900
17901 if (!i0) {
17902 // given value is outside table (before first item)
17903 return {lo: null, hi: i1};
17904 } else if (i1[key] < value) {
17905 lo = mid + 1;
17906 } else if (i0[key] > value) {
17907 hi = mid - 1;
17908 } else {
17909 return {lo: i0, hi: i1};
17910 }
17911 }
17912
17913 // given value is outside table (after last item)
17914 return {lo: i1, hi: null};
17915 }
17916
17917 /**
17918 * Linearly interpolates the given source `value` using the table items `skey` values and
17919 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
17920 * returns the position for a timestamp equal to 42. If value is out of bounds, values at
17921 * index [0, 1] or [n - 1, n] are used for the interpolation.
17922 */
17923 function interpolate(table, skey, sval, tkey) {
17924 var range = lookup(table, skey, sval);
17925
17926 // Note: the lookup table ALWAYS contains at least 2 items (min and max)
17927 var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
17928 var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
17929
17930 var span = next[skey] - prev[skey];
17931 var ratio = span ? (sval - prev[skey]) / span : 0;
17932 var offset = (next[tkey] - prev[tkey]) * ratio;
17933
17934 return prev[tkey] + offset;
17935 }
17936
17937 /**
17938 * Convert the given value to a moment object using the given time options.
17939 * @see http://momentjs.com/docs/#/parsing/
17940 */
17941 function momentify(value, options) {
17942 var parser = options.parser;
17943 var format = options.parser || options.format;
17944
17945 if (typeof parser === 'function') {
17946 return parser(value);
17947 }
17948
17949 if (typeof value === 'string' && typeof format === 'string') {
17950 return moment(value, format);
17951 }
17952
17953 if (!(value instanceof moment)) {
17954 value = moment(value);
17955 }
17956
17957 if (value.isValid()) {
17958 return value;
17959 }
17960
17961 // Labels are in an incompatible moment format and no `parser` has been provided.
17962 // The user might still use the deprecated `format` option to convert his inputs.
17963 if (typeof format === 'function') {
17964 return format(value);
17965 }
17966
17967 return value;
17968 }
17969
17970 function parse(input, scale) {
17971 if (helpers.isNullOrUndef(input)) {
17972 return null;
17973 }
17974
17975 var options = scale.options.time;
17976 var value = momentify(scale.getRightValue(input), options);
17977 if (!value.isValid()) {
17978 return null;
17979 }
17980
17981 if (options.round) {
17982 value.startOf(options.round);
17983 }
17984
17985 return value.valueOf();
17986 }
17987
17988 /**
17989 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
17990 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
17991 */
17992 function determineStepSize(min, max, unit, capacity) {
17993 var range = max - min;
17994 var interval = INTERVALS[unit];
17995 var milliseconds = interval.size;
17996 var steps = interval.steps;
17997 var i, ilen, factor;
17998
17999 if (!steps) {
18000 return Math.ceil(range / ((capacity || 1) * milliseconds));
18001 }
18002
18003 for (i = 0, ilen = steps.length; i < ilen; ++i) {
18004 factor = steps[i];
18005 if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
18006 break;
18007 }
18008 }
18009
18010 return factor;
18011 }
18012
18013 function determineUnit(minUnit, min, max, capacity) {
18014 var ilen = UNITS.length;
18015 var i, interval, factor;
18016
18017 for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
18018 interval = INTERVALS[UNITS[i]];
18019 factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;
18020
18021 if (Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
18022 return UNITS[i];
18023 }
18024 }
18025
18026 return UNITS[ilen - 1];
18027 }
18028
18029 function determineMajorUnit(unit) {
18030 for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
18031 if (INTERVALS[UNITS[i]].major) {
18032 return UNITS[i];
18033 }
18034 }
18035 }
18036
18037 /**
18038 * Generates a maximum of `capacity` timestamps between min and max, rounded to the
18039 * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
18040 * Important: this method can return ticks outside the min and max range, it's the
18041 * responsibility of the calling code to clamp values if needed.
18042 */
18043 function generate(min, max, minor, major, capacity, options) {
18044 var timeOpts = options.time;
18045 var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
18046 var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
18047 var majorTicksEnabled = options.ticks.major.enabled;
18048 var interval = INTERVALS[minor];
18049 var first = moment(min);
18050 var last = moment(max);
18051 var ticks = [];
18052 var time;
18053
18054 if (!stepSize) {
18055 stepSize = determineStepSize(min, max, minor, capacity);
18056 }
18057
18058 // For 'week' unit, handle the first day of week option
18059 if (weekday) {
18060 first = first.isoWeekday(weekday);
18061 last = last.isoWeekday(weekday);
18062 }
18063
18064 // Align first/last ticks on unit
18065 first = first.startOf(weekday ? 'day' : minor);
18066 last = last.startOf(weekday ? 'day' : minor);
18067
18068 // Make sure that the last tick include max
18069 if (last < max) {
18070 last.add(1, minor);
18071 }
18072
18073 time = moment(first);
18074
18075 if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
18076 // Align the first tick on the previous `minor` unit aligned on the `major` unit:
18077 // we first aligned time on the previous `major` unit then add the number of full
18078 // stepSize there is between first and the previous major time.
18079 time.startOf(major);
18080 time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
18081 }
18082
18083 for (; time < last; time.add(stepSize, minor)) {
18084 ticks.push(+time);
18085 }
18086
18087 ticks.push(+time);
18088
18089 return ticks;
18090 }
18091
18092 /**
18093 * Returns the right and left offsets from edges in the form of {left, right}.
18094 * Offsets are added when the `offset` option is true.
18095 */
18096 function computeOffsets(table, ticks, min, max, options) {
18097 var left = 0;
18098 var right = 0;
18099 var upper, lower;
18100
18101 if (options.offset && ticks.length) {
18102 if (!options.time.min) {
18103 upper = ticks.length > 1 ? ticks[1] : max;
18104 lower = ticks[0];
18105 left = (
18106 interpolate(table, 'time', upper, 'pos') -
18107 interpolate(table, 'time', lower, 'pos')
18108 ) / 2;
18109 }
18110 if (!options.time.max) {
18111 upper = ticks[ticks.length - 1];
18112 lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
18113 right = (
18114 interpolate(table, 'time', upper, 'pos') -
18115 interpolate(table, 'time', lower, 'pos')
18116 ) / 2;
18117 }
18118 }
18119
18120 return {left: left, right: right};
18121 }
18122
18123 function ticksFromTimestamps(values, majorUnit) {
18124 var ticks = [];
18125 var i, ilen, value, major;
18126
18127 for (i = 0, ilen = values.length; i < ilen; ++i) {
18128 value = values[i];
18129 major = majorUnit ? value === +moment(value).startOf(majorUnit) : false;
18130
18131 ticks.push({
18132 value: value,
18133 major: major
18134 });
18135 }
18136
18137 return ticks;
18138 }
18139
18140 module.exports = function(Chart) {
18141
18142 var defaultConfig = {
18143 position: 'bottom',
18144
18145 /**
18146 * Data distribution along the scale:
18147 * - 'linear': data are spread according to their time (distances can vary),
18148 * - 'series': data are spread at the same distance from each other.
18149 * @see https://github.com/chartjs/Chart.js/pull/4507
18150 * @since 2.7.0
18151 */
18152 distribution: 'linear',
18153
18154 /**
18155 * Scale boundary strategy (bypassed by min/max time options)
18156 * - `data`: make sure data are fully visible, ticks outside are removed
18157 * - `ticks`: make sure ticks are fully visible, data outside are truncated
18158 * @see https://github.com/chartjs/Chart.js/pull/4556
18159 * @since 2.7.0
18160 */
18161 bounds: 'data',
18162
18163 time: {
18164 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
18165 format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
18166 unit: false, // false == automatic or override with week, month, year, etc.
18167 round: false, // none, or override with week, month, year, etc.
18168 displayFormat: false, // DEPRECATED
18169 isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
18170 minUnit: 'millisecond',
18171
18172 // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
18173 displayFormats: {
18174 millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
18175 second: 'h:mm:ss a', // 11:20:01 AM
18176 minute: 'h:mm a', // 11:20 AM
18177 hour: 'hA', // 5PM
18178 day: 'MMM D', // Sep 4
18179 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
18180 month: 'MMM YYYY', // Sept 2015
18181 quarter: '[Q]Q - YYYY', // Q3
18182 year: 'YYYY' // 2015
18183 },
18184 },
18185 ticks: {
18186 autoSkip: false,
18187
18188 /**
18189 * Ticks generation input values:
18190 * - 'auto': generates "optimal" ticks based on scale size and time options.
18191 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
18192 * - 'labels': generates ticks from user given `data.labels` values ONLY.
18193 * @see https://github.com/chartjs/Chart.js/pull/4507
18194 * @since 2.7.0
18195 */
18196 source: 'auto',
18197
18198 major: {
18199 enabled: false
18200 }
18201 }
18202 };
18203
18204 var TimeScale = Chart.Scale.extend({
18205 initialize: function() {
18206 if (!moment) {
18207 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');
18208 }
18209
18210 this.mergeTicksOptions();
18211
18212 Chart.Scale.prototype.initialize.call(this);
18213 },
18214
18215 update: function() {
18216 var me = this;
18217 var options = me.options;
18218
18219 // DEPRECATIONS: output a message only one time per update
18220 if (options.time && options.time.format) {
18221 console.warn('options.time.format is deprecated and replaced by options.time.parser.');
18222 }
18223
18224 return Chart.Scale.prototype.update.apply(me, arguments);
18225 },
18226
18227 /**
18228 * Allows data to be referenced via 't' attribute
18229 */
18230 getRightValue: function(rawValue) {
18231 if (rawValue && rawValue.t !== undefined) {
18232 rawValue = rawValue.t;
18233 }
18234 return Chart.Scale.prototype.getRightValue.call(this, rawValue);
18235 },
18236
18237 determineDataLimits: function() {
18238 var me = this;
18239 var chart = me.chart;
18240 var timeOpts = me.options.time;
18241 var min = parse(timeOpts.min, me) || MAX_INTEGER;
18242 var max = parse(timeOpts.max, me) || MIN_INTEGER;
18243 var timestamps = [];
18244 var datasets = [];
18245 var labels = [];
18246 var i, j, ilen, jlen, data, timestamp;
18247
18248 // Convert labels to timestamps
18249 for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
18250 labels.push(parse(chart.data.labels[i], me));
18251 }
18252
18253 // Convert data to timestamps
18254 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
18255 if (chart.isDatasetVisible(i)) {
18256 data = chart.data.datasets[i].data;
18257
18258 // Let's consider that all data have the same format.
18259 if (helpers.isObject(data[0])) {
18260 datasets[i] = [];
18261
18262 for (j = 0, jlen = data.length; j < jlen; ++j) {
18263 timestamp = parse(data[j], me);
18264 timestamps.push(timestamp);
18265 datasets[i][j] = timestamp;
18266 }
18267 } else {
18268 timestamps.push.apply(timestamps, labels);
18269 datasets[i] = labels.slice(0);
18270 }
18271 } else {
18272 datasets[i] = [];
18273 }
18274 }
18275
18276 if (labels.length) {
18277 // Sort labels **after** data have been converted
18278 labels = arrayUnique(labels).sort(sorter);
18279 min = Math.min(min, labels[0]);
18280 max = Math.max(max, labels[labels.length - 1]);
18281 }
18282
18283 if (timestamps.length) {
18284 timestamps = arrayUnique(timestamps).sort(sorter);
18285 min = Math.min(min, timestamps[0]);
18286 max = Math.max(max, timestamps[timestamps.length - 1]);
18287 }
18288
18289 // In case there is no valid min/max, let's use today limits
18290 min = min === MAX_INTEGER ? +moment().startOf('day') : min;
18291 max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max;
18292
18293 // Make sure that max is strictly higher than min (required by the lookup table)
18294 me.min = Math.min(min, max);
18295 me.max = Math.max(min + 1, max);
18296
18297 // PRIVATE
18298 me._horizontal = me.isHorizontal();
18299 me._table = [];
18300 me._timestamps = {
18301 data: timestamps,
18302 datasets: datasets,
18303 labels: labels
18304 };
18305 },
18306
18307 buildTicks: function() {
18308 var me = this;
18309 var min = me.min;
18310 var max = me.max;
18311 var options = me.options;
18312 var timeOpts = options.time;
18313 var formats = timeOpts.displayFormats;
18314 var capacity = me.getLabelCapacity(min);
18315 var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, min, max, capacity);
18316 var majorUnit = determineMajorUnit(unit);
18317 var timestamps = [];
18318 var ticks = [];
18319 var i, ilen, timestamp;
18320
18321 switch (options.ticks.source) {
18322 case 'data':
18323 timestamps = me._timestamps.data;
18324 break;
18325 case 'labels':
18326 timestamps = me._timestamps.labels;
18327 break;
18328 case 'auto':
18329 default:
18330 timestamps = generate(min, max, unit, majorUnit, capacity, options);
18331 }
18332
18333 if (options.bounds === 'ticks' && timestamps.length) {
18334 min = timestamps[0];
18335 max = timestamps[timestamps.length - 1];
18336 }
18337
18338 // Enforce limits with user min/max options
18339 min = parse(timeOpts.min, me) || min;
18340 max = parse(timeOpts.max, me) || max;
18341
18342 // Remove ticks outside the min/max range
18343 for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
18344 timestamp = timestamps[i];
18345 if (timestamp >= min && timestamp <= max) {
18346 ticks.push(timestamp);
18347 }
18348 }
18349
18350 me.min = min;
18351 me.max = max;
18352
18353 // PRIVATE
18354 me._unit = unit;
18355 me._majorUnit = majorUnit;
18356 me._minorFormat = formats[unit];
18357 me._majorFormat = formats[majorUnit];
18358 me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
18359 me._offsets = computeOffsets(me._table, ticks, min, max, options);
18360
18361 return ticksFromTimestamps(ticks, majorUnit);
18362 },
18363
18364 getLabelForIndex: function(index, datasetIndex) {
18365 var me = this;
18366 var data = me.chart.data;
18367 var timeOpts = me.options.time;
18368 var label = data.labels && index < data.labels.length ? data.labels[index] : '';
18369 var value = data.datasets[datasetIndex].data[index];
18370
18371 if (helpers.isObject(value)) {
18372 label = me.getRightValue(value);
18373 }
18374 if (timeOpts.tooltipFormat) {
18375 label = momentify(label, timeOpts).format(timeOpts.tooltipFormat);
18376 }
18377
18378 return label;
18379 },
18380
18381 /**
18382 * Function to format an individual tick mark
18383 * @private
18384 */
18385 tickFormatFunction: function(tick, index, ticks) {
18386 var me = this;
18387 var options = me.options;
18388 var time = tick.valueOf();
18389 var majorUnit = me._majorUnit;
18390 var majorFormat = me._majorFormat;
18391 var majorTime = tick.clone().startOf(me._majorUnit).valueOf();
18392 var majorTickOpts = options.ticks.major;
18393 var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
18394 var label = tick.format(major ? majorFormat : me._minorFormat);
18395 var tickOpts = major ? majorTickOpts : options.ticks.minor;
18396 var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
18397
18398 return formatter ? formatter(label, index, ticks) : label;
18399 },
18400
18401 convertTicksToLabels: function(ticks) {
18402 var labels = [];
18403 var i, ilen;
18404
18405 for (i = 0, ilen = ticks.length; i < ilen; ++i) {
18406 labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
18407 }
18408
18409 return labels;
18410 },
18411
18412 /**
18413 * @private
18414 */
18415 getPixelForOffset: function(time) {
18416 var me = this;
18417 var size = me._horizontal ? me.width : me.height;
18418 var start = me._horizontal ? me.left : me.top;
18419 var pos = interpolate(me._table, 'time', time, 'pos');
18420
18421 return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
18422 },
18423
18424 getPixelForValue: function(value, index, datasetIndex) {
18425 var me = this;
18426 var time = null;
18427
18428 if (index !== undefined && datasetIndex !== undefined) {
18429 time = me._timestamps.datasets[datasetIndex][index];
18430 }
18431
18432 if (time === null) {
18433 time = parse(value, me);
18434 }
18435
18436 if (time !== null) {
18437 return me.getPixelForOffset(time);
18438 }
18439 },
18440
18441 getPixelForTick: function(index) {
18442 var ticks = this.getTicks();
18443 return index >= 0 && index < ticks.length ?
18444 this.getPixelForOffset(ticks[index].value) :
18445 null;
18446 },
18447
18448 getValueForPixel: function(pixel) {
18449 var me = this;
18450 var size = me._horizontal ? me.width : me.height;
18451 var start = me._horizontal ? me.left : me.top;
18452 var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
18453 var time = interpolate(me._table, 'pos', pos, 'time');
18454
18455 return moment(time);
18456 },
18457
18458 /**
18459 * Crude approximation of what the label width might be
18460 * @private
18461 */
18462 getLabelWidth: function(label) {
18463 var me = this;
18464 var ticksOpts = me.options.ticks;
18465 var tickLabelWidth = me.ctx.measureText(label).width;
18466 var angle = helpers.toRadians(ticksOpts.maxRotation);
18467 var cosRotation = Math.cos(angle);
18468 var sinRotation = Math.sin(angle);
18469 var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
18470
18471 return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
18472 },
18473
18474 /**
18475 * @private
18476 */
18477 getLabelCapacity: function(exampleTime) {
18478 var me = this;
18479
18480 me._minorFormat = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation
18481
18482 var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []);
18483 var tickLabelWidth = me.getLabelWidth(exampleLabel);
18484 var innerWidth = me.isHorizontal() ? me.width : me.height;
18485
18486 return Math.floor(innerWidth / tickLabelWidth);
18487 }
18488 });
18489
18490 Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig);
18491 };
18492
18493 },{"25":25,"45":45,"6":6}]},{},[7])(7)
18494 });