PageSourceSearch

https://open.toscana.it/o/open-toscana-restyled-theme/js/vendors/chartsjs/Chart.js

js open.toscana.it collected 2026-10-03 17:38:02 UTC 416,958 bytes, 14,456 lines download raw bytes

1/*!
2 * Chart.js
3 * http://chartjs.org/
4 * Version: 2.7.3
5 *
6 * Copyright 2018 Chart.js Contributors
7 * Released under the MIT license
8 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
9 */
10(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
11
12},{}],2:[function(require,module,exports){
13/* MIT license */
14var colorNames = require(6);
15
16module.exports = {
17   getRgba: getRgba,
18   getHsla: getHsla,
19   getRgb: getRgb,
20   getHsl: getHsl,
21   getHwb: getHwb,
22   getAlpha: getAlpha,
23
24   hexString: hexString,
25   rgbString: rgbString,
26   rgbaString: rgbaString,
27   percentString: percentString,
28   percentaString: percentaString,
29   hslString: hslString,
30   hslaString: hslaString,
31   hwbString: hwbString,
32   keyword: keyword
33}
34
35function getRgba(string) {
36   if (!string) {
37      return;
38   }
39   var abbr =  /^#([a-fA-F0-9]{3})$/i,
40       hex =  /^#([a-fA-F0-9]{6})$/i,
41       rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
42       per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
43       keyword = /(\w+)/;
44
45   var rgb = [0, 0, 0],
46       a = 1,
47       match = string.match(abbr);
48   if (match) {
49      match = match[1];
50      for (var i = 0; i < rgb.length; i++) {
51         rgb[i] = parseInt(match[i] + match[i], 16);
52      }
53   }
54   else if (match = string.match(hex)) {
55      match = match[1];
56      for (var i = 0; i < rgb.length; i++) {
57         rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
58      }
59   }
60   else if (match = string.match(rgba)) {
61      for (var i = 0; i < rgb.length; i++) {
62         rgb[i] = parseInt(match[i + 1]);
63      }
64      a = parseFloat(match[4]);
65   }
66   else if (match = string.match(per)) {
67      for (var i = 0; i < rgb.length; i++) {
68         rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
69      }
70      a = parseFloat(match[4]);
71   }
72   else if (match = string.match(keyword)) {
73      if (match[1] == "transparent") {
74         return [0, 0, 0, 0];
75      }
76      rgb = colorNames[match[1]];
77      if (!rgb) {
78         return;
79      }
80   }
81
82   for (var i = 0; i < rgb.length; i++) {
83      rgb[i] = scale(rgb[i], 0, 255);
84   }
85   if (!a && a != 0) {
86      a = 1;
87   }
88   else {
89      a = scale(a, 0, 1);
90   }
91   rgb[3] = a;
92   return rgb;
93}
94
95function getHsla(string) {
96   if (!string) {
97      return;
98   }
99   var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
100   var match = string.match(hsl);
101   if (match) {
102      var alpha = parseFloat(match[4]);
103      var h = scale(parseInt(match[1]), 0, 360),
104          s = scale(parseFloat(match[2]), 0, 100),
105          l = scale(parseFloat(match[3]), 0, 100),
106          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
107      return [h, s, l, a];
108   }
109}
110
111function getHwb(string) {
112   if (!string) {
113      return;
114   }
115   var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
116   var match = string.match(hwb);
117   if (match) {
118    var alpha = parseFloat(match[4]);
119      var h = scale(parseInt(match[1]), 0, 360),
120          w = scale(parseFloat(match[2]), 0, 100),
121          b = scale(parseFloat(match[3]), 0, 100),
122          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
123      return [h, w, b, a];
124   }
125}
126
127function getRgb(string) {
128   var rgba = getRgba(string);
129   return rgba && rgba.slice(0, 3);
130}
131
132function getHsl(string) {
133  var hsla = getHsla(string);
134  return hsla && hsla.slice(0, 3);
135}
136
137function getAlpha(string) {
138   var vals = getRgba(string);
139   if (vals) {
140      return vals[3];
141   }
142   else if (vals = getHsla(string)) {
143      return vals[3];
144   }
145   else if (vals = getHwb(string)) {
146      return vals[3];
147   }
148}
149
150// generators
151function hexString(rgb) {
152   return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
153              + hexDouble(rgb[2]);
154}
155
156function rgbString(rgba, alpha) {
vendor: 12,396 bytes, lines 157-643
157   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
158      return rgbaString(rgba, alpha);
159   }
160   return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
161}
162
163function rgbaString(rgba, alpha) {
164   if (alpha === undefined) {
165      alpha = (rgba[3] !== undefined ? rgba[3] : 1);
166   }
167   return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
168           + ", " + alpha + ")";
169}
170
171function percentString(rgba, alpha) {
172   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
173      return percentaString(rgba, alpha);
174   }
175   var r = Math.round(rgba[0]/255 * 100),
176       g = Math.round(rgba[1]/255 * 100),
177       b = Math.round(rgba[2]/255 * 100);
178
179   return "rgb(" + r + "%, " + g + "%, " + b + "%)";
180}
181
182function percentaString(rgba, alpha) {
183   var r = Math.round(rgba[0]/255 * 100),
184       g = Math.round(rgba[1]/255 * 100),
185       b = Math.round(rgba[2]/255 * 100);
186   return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
187}
188
189function hslString(hsla, alpha) {
190   if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
191      return hslaString(hsla, alpha);
192   }
193   return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
194}
195
196function hslaString(hsla, alpha) {
197   if (alpha === undefined) {
198      alpha = (hsla[3] !== undefined ? hsla[3] : 1);
199   }
200   return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
201           + alpha + ")";
202}
203
204// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
205// (hwb have alpha optional & 1 is default value)
206function hwbString(hwb, alpha) {
207   if (alpha === undefined) {
208      alpha = (hwb[3] !== undefined ? hwb[3] : 1);
209   }
210   return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
211           + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
212}
213
214function keyword(rgb) {
215  return reverseNames[rgb.slice(0, 3)];
216}
217
218// helpers
219function scale(num, min, max) {
220   return Math.min(Math.max(min, num), max);
221}
222
223function hexDouble(num) {
224  var str = num.toString(16).toUpperCase();
225  return (str.length < 2) ? "0" + str : str;
226}
227
228
229//create a list of reverse color names
230var reverseNames = {};
231for (var name in colorNames) {
232   reverseNames[colorNames[name]] = name;
233}
234
235},{"6":6}],3:[function(require,module,exports){
236/* MIT license */
237var convert = require(5);
238var string = require(2);
239
240var Color = function (obj) {
241	if (obj instanceof Color) {
242		return obj;
243	}
244	if (!(this instanceof Color)) {
245		return new Color(obj);
246	}
247
248	this.valid = false;
249	this.values = {
250		rgb: [0, 0, 0],
251		hsl: [0, 0, 0],
252		hsv: [0, 0, 0],
253		hwb: [0, 0, 0],
254		cmyk: [0, 0, 0, 0],
255		alpha: 1
256	};
257
258	// parse Color() argument
259	var vals;
260	if (typeof obj === 'string') {
261		vals = string.getRgba(obj);
262		if (vals) {
263			this.setValues('rgb', vals);
264		} else if (vals = string.getHsla(obj)) {
265			this.setValues('hsl', vals);
266		} else if (vals = string.getHwb(obj)) {
267			this.setValues('hwb', vals);
268		}
269	} else if (typeof obj === 'object') {
270		vals = obj;
271		if (vals.r !== undefined || vals.red !== undefined) {
272			this.setValues('rgb', vals);
273		} else if (vals.l !== undefined || vals.lightness !== undefined) {
274			this.setValues('hsl', vals);
275		} else if (vals.v !== undefined || vals.value !== undefined) {
276			this.setValues('hsv', vals);
277		} else if (vals.w !== undefined || vals.whiteness !== undefined) {
278			this.setValues('hwb', vals);
279		} else if (vals.c !== undefined || vals.cyan !== undefined) {
280			this.setValues('cmyk', vals);
281		}
282	}
283};
284
285Color.prototype = {
286	isValid: function () {
287		return this.valid;
288	},
289	rgb: function () {
290		return this.setSpace('rgb', arguments);
291	},
292	hsl: function () {
293		return this.setSpace('hsl', arguments);
294	},
295	hsv: function () {
296		return this.setSpace('hsv', arguments);
297	},
298	hwb: function () {
299		return this.setSpace('hwb', arguments);
300	},
301	cmyk: function () {
302		return this.setSpace('cmyk', arguments);
303	},
304
305	rgbArray: function () {
306		return this.values.rgb;
307	},
308	hslArray: function () {
309		return this.values.hsl;
310	},
311	hsvArray: function () {
312		return this.values.hsv;
313	},
314	hwbArray: function () {
315		var values = this.values;
316		if (values.alpha !== 1) {
317			return values.hwb.concat([values.alpha]);
318		}
319		return values.hwb;
320	},
321	cmykArray: function () {
322		return this.values.cmyk;
323	},
324	rgbaArray: function () {
325		var values = this.values;
326		return values.rgb.concat([values.alpha]);
327	},
328	hslaArray: function () {
329		var values = this.values;
330		return values.hsl.concat([values.alpha]);
331	},
332	alpha: function (val) {
333		if (val === undefined) {
334			return this.values.alpha;
335		}
336		this.setValues('alpha', val);
337		return this;
338	},
339
340	red: function (val) {
341		return this.setChannel('rgb', 0, val);
342	},
343	green: function (val) {
344		return this.setChannel('rgb', 1, val);
345	},
346	blue: function (val) {
347		return this.setChannel('rgb', 2, val);
348	},
349	hue: function (val) {
350		if (val) {
351			val %= 360;
352			val = val < 0 ? 360 + val : val;
353		}
354		return this.setChannel('hsl', 0, val);
355	},
356	saturation: function (val) {
357		return this.setChannel('hsl', 1, val);
358	},
359	lightness: function (val) {
360		return this.setChannel('hsl', 2, val);
361	},
362	saturationv: function (val) {
363		return this.setChannel('hsv', 1, val);
364	},
365	whiteness: function (val) {
366		return this.setChannel('hwb', 1, val);
367	},
368	blackness: function (val) {
369		return this.setChannel('hwb', 2, val);
370	},
371	value: function (val) {
372		return this.setChannel('hsv', 2, val);
373	},
374	cyan: function (val) {
375		return this.setChannel('cmyk', 0, val);
376	},
377	magenta: function (val) {
378		return this.setChannel('cmyk', 1, val);
379	},
380	yellow: function (val) {
381		return this.setChannel('cmyk', 2, val);
382	},
383	black: function (val) {
384		return this.setChannel('cmyk', 3, val);
385	},
386
387	hexString: function () {
388		return string.hexString(this.values.rgb);
389	},
390	rgbString: function () {
391		return string.rgbString(this.values.rgb, this.values.alpha);
392	},
393	rgbaString: function () {
394		return string.rgbaString(this.values.rgb, this.values.alpha);
395	},
396	percentString: function () {
397		return string.percentString(this.values.rgb, this.values.alpha);
398	},
399	hslString: function () {
400		return string.hslString(this.values.hsl, this.values.alpha);
401	},
402	hslaString: function () {
403		return string.hslaString(this.values.hsl, this.values.alpha);
404	},
405	hwbString: function () {
406		return string.hwbString(this.values.hwb, this.values.alpha);
407	},
408	keyword: function () {
409		return string.keyword(this.values.rgb, this.values.alpha);
410	},
411
412	rgbNumber: function () {
413		var rgb = this.values.rgb;
414		return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
415	},
416
417	luminosity: function () {
418		// http://www.w3.org/TR/WCAG20/#relativeluminancedef
419		var rgb = this.values.rgb;
420		var lum = [];
421		for (var i = 0; i < rgb.length; i++) {
422			var chan = rgb[i] / 255;
423			lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
424		}
425		return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
426	},
427
428	contrast: function (color2) {
429		// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
430		var lum1 = this.luminosity();
431		var lum2 = color2.luminosity();
432		if (lum1 > lum2) {
433			return (lum1 + 0.05) / (lum2 + 0.05);
434		}
435		return (lum2 + 0.05) / (lum1 + 0.05);
436	},
437
438	level: function (color2) {
439		var contrastRatio = this.contrast(color2);
440		if (contrastRatio >= 7.1) {
441			return 'AAA';
442		}
443
444		return (contrastRatio >= 4.5) ? 'AA' : '';
445	},
446
447	dark: function () {
448		// YIQ equation from http://24ways.org/2010/calculating-color-contrast
449		var rgb = this.values.rgb;
450		var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
451		return yiq < 128;
452	},
453
454	light: function () {
455		return !this.dark();
456	},
457
458	negate: function () {
459		var rgb = [];
460		for (var i = 0; i < 3; i++) {
461			rgb[i] = 255 - this.values.rgb[i];
462		}
463		this.setValues('rgb', rgb);
464		return this;
465	},
466
467	lighten: function (ratio) {
468		var hsl = this.values.hsl;
469		hsl[2] += hsl[2] * ratio;
470		this.setValues('hsl', hsl);
471		return this;
472	},
473
474	darken: function (ratio) {
475		var hsl = this.values.hsl;
476		hsl[2] -= hsl[2] * ratio;
477		this.setValues('hsl', hsl);
478		return this;
479	},
480
481	saturate: function (ratio) {
482		var hsl = this.values.hsl;
483		hsl[1] += hsl[1] * ratio;
484		this.setValues('hsl', hsl);
485		return this;
486	},
487
488	desaturate: function (ratio) {
489		var hsl = this.values.hsl;
490		hsl[1] -= hsl[1] * ratio;
491		this.setValues('hsl', hsl);
492		return this;
493	},
494
495	whiten: function (ratio) {
496		var hwb = this.values.hwb;
497		hwb[1] += hwb[1] * ratio;
498		this.setValues('hwb', hwb);
499		return this;
500	},
501
502	blacken: function (ratio) {
503		var hwb = this.values.hwb;
504		hwb[2] += hwb[2] * ratio;
505		this.setValues('hwb', hwb);
506		return this;
507	},
508
509	greyscale: function () {
510		var rgb = this.values.rgb;
511		// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
512		var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
513		this.setValues('rgb', [val, val, val]);
514		return this;
515	},
516
517	clearer: function (ratio) {
518		var alpha = this.values.alpha;
519		this.setValues('alpha', alpha - (alpha * ratio));
520		return this;
521	},
522
523	opaquer: function (ratio) {
524		var alpha = this.values.alpha;
525		this.setValues('alpha', alpha + (alpha * ratio));
526		return this;
527	},
528
529	rotate: function (degrees) {
530		var hsl = this.values.hsl;
531		var hue = (hsl[0] + degrees) % 360;
532		hsl[0] = hue < 0 ? 360 + hue : hue;
533		this.setValues('hsl', hsl);
534		return this;
535	},
536
537	/**
538	 * Ported from sass implementation in C
539	 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
540	 */
541	mix: function (mixinColor, weight) {
542		var color1 = this;
543		var color2 = mixinColor;
544		var p = weight === undefined ? 0.5 : weight;
545
546		var w = 2 * p - 1;
547		var a = color1.alpha() - color2.alpha();
548
549		var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
550		var w2 = 1 - w1;
551
552		return this
553			.rgb(
554				w1 * color1.red() + w2 * color2.red(),
555				w1 * color1.green() + w2 * color2.green(),
556				w1 * color1.blue() + w2 * color2.blue()
557			)
558			.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
559	},
560
561	toJSON: function () {
562		return this.rgb();
563	},
564
565	clone: function () {
566		// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
567		// making the final build way to big to embed in Chart.js. So let's do it manually,
568		// assuming that values to clone are 1 dimension arrays containing only numbers,
569		// except 'alpha' which is a number.
570		var result = new Color();
571		var source = this.values;
572		var target = result.values;
573		var value, type;
574
575		for (var prop in source) {
576			if (source.hasOwnProperty(prop)) {
577				value = source[prop];
578				type = ({}).toString.call(value);
579				if (type === '[object Array]') {
580					target[prop] = value.slice(0);
581				} else if (type === '[object Number]') {
582					target[prop] = value;
583				} else {
584					console.error('unexpected color value:', value);
585				}
586			}
587		}
588
589		return result;
590	}
591};
592
593Color.prototype.spaces = {
594	rgb: ['red', 'green', 'blue'],
595	hsl: ['hue', 'saturation', 'lightness'],
596	hsv: ['hue', 'saturation', 'value'],
597	hwb: ['hue', 'whiteness', 'blackness'],
598	cmyk: ['cyan', 'magenta', 'yellow', 'black']
599};
600
601Color.prototype.maxes = {
602	rgb: [255, 255, 255],
603	hsl: [360, 100, 100],
604	hsv: [360, 100, 100],
605	hwb: [360, 100, 100],
606	cmyk: [100, 100, 100, 100]
607};
608
609Color.prototype.getValues = function (space) {
610	var values = this.values;
611	var vals = {};
612
613	for (var i = 0; i < space.length; i++) {
614		vals[space.charAt(i)] = values[space][i];
615	}
616
617	if (values.alpha !== 1) {
618		vals.a = values.alpha;
619	}
620
621	// {r: 255, g: 255, b: 255, a: 0.4}
622	return vals;
623};
624
625Color.prototype.setValues = function (space, vals) {
626	var values = this.values;
627	var spaces = this.spaces;
628	var maxes = this.maxes;
629	var alpha = 1;
630	var i;
631
632	this.valid = true;
633
634	if (space === 'alpha') {
635		alpha = vals;
636	} else if (vals.length) {
637		// [10, 10, 10]
638		values[space] = vals.slice(0, space.length);
639		alpha = vals[space.length];
640	} else if (vals[space.charAt(0)] !== undefined) {
641		// {r: 10, g: 10, b: 10}
642		for (i = 0; i < space.length; i++) {
643			values[space][i] = vals[space.charAt(i)];
644		}
645
646		alpha = vals.a;
647	} else if (vals[spaces[space][0]] !== undefined) {
648		// {red: 10, green: 10, blue: 10}
649		var chans = spaces[space];
650
651		for (i = 0; i < space.length; i++) {
652			values[space][i] = vals[chans[i]];
653		}
654
655		alpha = vals.alpha;
656	}
657
658	values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
659
660	if (space === 'alpha') {
661		return false;
662	}
663
664	var capped;
665
666	// cap values of the space prior converting all values
667	for (i = 0; i < space.length; i++) {
668		capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
669		values[space][i] = Math.round(capped);
670	}
671
672	// convert to all the other color spaces
673	for (var sname in spaces) {
674		if (sname !== space) {
675			values[sname] = convert[space][sname](values[space]);
676		}
677	}
678
679	return true;
680};
681
682Color.prototype.setSpace = function (space, args) {
683	var vals = args[0];
684
685	if (vals === undefined) {
686		// color.rgb()
687		return this.getValues(space);
688	}
689
690	// color.rgb(10, 10, 10)
691	if (typeof vals === 'number') {
692		vals = Array.prototype.slice.call(args);
693	}
694
695	this.setValues(space, vals);
696	return this;
697};
698
699Color.prototype.setChannel = function (space, index, val) {
700	var svalues = this.values[space];
701	if (val === undefined) {
702		// color.red()
703		return svalues[index];
704	} else if (val === svalues[index]) {
705		// color.red(color.red())
706		return this;
707	}
708
709	// color.red(100)
710	svalues[index] = val;
711	this.setValues(space, svalues);
712
713	return this;
714};
715
716if (typeof window !== 'undefined') {
717	window.Color = Color;
718}
719
720module.exports = Color;
721
722},{"2":2,"5":5}],4:[function(require,module,exports){
723/* MIT license */
724
725module.exports = {
726  rgb2hsl: rgb2hsl,
727  rgb2hsv: rgb2hsv,
728  rgb2hwb: rgb2hwb,
729  rgb2cmyk: rgb2cmyk,
730  rgb2keyword: rgb2keyword,
731  rgb2xyz: rgb2xyz,
732  rgb2lab: rgb2lab,
733  rgb2lch: rgb2lch,
734
735  hsl2rgb: hsl2rgb,
736  hsl2hsv: hsl2hsv,
737  hsl2hwb: hsl2hwb,
738  hsl2cmyk: hsl2cmyk,
739  hsl2keyword: hsl2keyword,
740
741  hsv2rgb: hsv2rgb,
742  hsv2hsl: hsv2hsl,
743  hsv2hwb: hsv2hwb,
744  hsv2cmyk: hsv2cmyk,
745  hsv2keyword: hsv2keyword,
746
747  hwb2rgb: hwb2rgb,
748  hwb2hsl: hwb2hsl,
749  hwb2hsv: hwb2hsv,
750  hwb2cmyk: hwb2cmyk,
751  hwb2keyword: hwb2keyword,
752
753  cmyk2rgb: cmyk2rgb,
754  cmyk2hsl: cmyk2hsl,
755  cmyk2hsv: cmyk2hsv,
756  cmyk2hwb: cmyk2hwb,
757  cmyk2keyword: cmyk2keyword,
758
759  keyword2rgb: keyword2rgb,
760  keyword2hsl: keyword2hsl,
761  keyword2hsv: keyword2hsv,
762  keyword2hwb: keyword2hwb,
763  keyword2cmyk: keyword2cmyk,
764  keyword2lab: keyword2lab,
765  keyword2xyz: keyword2xyz,
766
767  xyz2rgb: xyz2rgb,
768  xyz2lab: xyz2lab,
769  xyz2lch: xyz2lch,
770
771  lab2xyz: lab2xyz,
772  lab2rgb: lab2rgb,
773  lab2lch: lab2lch,
774
775  lch2lab: lch2lab,
776  lch2xyz: lch2xyz,
777  lch2rgb: lch2rgb
778}
779
780
781function rgb2hsl(rgb) {
782  var r = rgb[0]/255,
783      g = rgb[1]/255,
784      b = rgb[2]/255,
785      min = Math.min(r, g, b),
786      max = Math.max(r, g, b),
787      delta = max - min,
788      h, s, l;
789
790  if (max == min)
791    h = 0;
792  else if (r == max)
793    h = (g - b) / delta;
794  else if (g == max)
795    h = 2 + (b - r) / delta;
796  else if (b == max)
797    h = 4 + (r - g)/ delta;
798
799  h = Math.min(h * 60, 360);
800
801  if (h < 0)
802    h += 360;
803
804  l = (min + max) / 2;
805
806  if (max == min)
807    s = 0;
808  else if (l <= 0.5)
809    s = delta / (max + min);
810  else
811    s = delta / (2 - max - min);
812
813  return [h, s * 100, l * 100];
814}
815
816function rgb2hsv(rgb) {
817  var r = rgb[0],
818      g = rgb[1],
819      b = rgb[2],
820      min = Math.min(r, g, b),
821      max = Math.max(r, g, b),
822      delta = max - min,
823      h, s, v;
824
825  if (max == 0)
826    s = 0;
827  else
828    s = (delta/max * 1000)/10;
829
830  if (max == min)
831    h = 0;
832  else if (r == max)
833    h = (g - b) / delta;
834  else if (g == max)
835    h = 2 + (b - r) / delta;
836  else if (b == max)
837    h = 4 + (r - g) / delta;
838
839  h = Math.min(h * 60, 360);
840
841  if (h < 0)
842    h += 360;
843
844  v = ((max / 255) * 1000) / 10;
845
846  return [h, s, v];
847}
848
849function rgb2hwb(rgb) {
850  var r = rgb[0],
851      g = rgb[1],
852      b = rgb[2],
853      h = rgb2hsl(rgb)[0],
854      w = 1/255 * Math.min(r, Math.min(g, b)),
855      b = 1 - 1/255 * Math.max(r, Math.max(g, b));
856
857  return [h, w * 100, b * 100];
858}
859
860function rgb2cmyk(rgb) {
861  var r = rgb[0] / 255,
862      g = rgb[1] / 255,
863      b = rgb[2] / 255,
864      c, m, y, k;
865
866  k = Math.min(1 - r, 1 - g, 1 - b);
867  c = (1 - r - k) / (1 - k) || 0;
868  m = (1 - g - k) / (1 - k) || 0;
869  y = (1 - b - k) / (1 - k) || 0;
870  return [c * 100, m * 100, y * 100, k * 100];
871}
872
873function rgb2keyword(rgb) {
874  return reverseKeywords[JSON.stringify(rgb)];
875}
876
877function rgb2xyz(rgb) {
878  var r = rgb[0] / 255,
879      g = rgb[1] / 255,
880      b = rgb[2] / 255;
881
882  // assume sRGB
883  r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
884  g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
885  b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
886
887  var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
888  var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
889  var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
890
891  return [x * 100, y *100, z * 100];
892}
893
894function rgb2lab(rgb) {
895  var xyz = rgb2xyz(rgb),
896        x = xyz[0],
897        y = xyz[1],
898        z = xyz[2],
899        l, a, b;
900
901  x /= 95.047;
902  y /= 100;
903  z /= 108.883;
904
905  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
906  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
907  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
908
909  l = (116 * y) - 16;
910  a = 500 * (x - y);
911  b = 200 * (y - z);
912
913  return [l, a, b];
914}
915
916function rgb2lch(args) {
917  return lab2lch(rgb2lab(args));
918}
919
920function hsl2rgb(hsl) {
921  var h = hsl[0] / 360,
922      s = hsl[1] / 100,
923      l = hsl[2] / 100,
924      t1, t2, t3, rgb, val;
925
926  if (s == 0) {
927    val = l * 255;
928    return [val, val, val];
929  }
930
931  if (l < 0.5)
932    t2 = l * (1 + s);
933  else
934    t2 = l + s - l * s;
935  t1 = 2 * l - t2;
936
937  rgb = [0, 0, 0];
938  for (var i = 0; i < 3; i++) {
939    t3 = h + 1 / 3 * - (i - 1);
940    t3 < 0 && t3++;
941    t3 > 1 && t3--;
942
943    if (6 * t3 < 1)
944      val = t1 + (t2 - t1) * 6 * t3;
945    else if (2 * t3 < 1)
946      val = t2;
947    else if (3 * t3 < 2)
948      val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
949    else
950      val = t1;
951
952    rgb[i] = val * 255;
953  }
954
955  return rgb;
956}
957
958function hsl2hsv(hsl) {
959  var h = hsl[0],
960      s = hsl[1] / 100,
961      l = hsl[2] / 100,
962      sv, v;
963
964  if(l === 0) {
965      // no need to do calc on black
966      // also avoids divide by 0 error
967      return [0, 0, 0];
968  }
969
970  l *= 2;
971  s *= (l <= 1) ? l : 2 - l;
972  v = (l + s) / 2;
973  sv = (2 * s) / (l + s);
974  return [h, sv * 100, v * 100];
975}
976
977function hsl2hwb(args) {
978  return rgb2hwb(hsl2rgb(args));
979}
980
981function hsl2cmyk(args) {
982  return rgb2cmyk(hsl2rgb(args));
983}
984
985function hsl2keyword(args) {
986  return rgb2keyword(hsl2rgb(args));
987}
988
989
990function hsv2rgb(hsv) {
991  var h = hsv[0] / 60,
992      s = hsv[1] / 100,
993      v = hsv[2] / 100,
994      hi = Math.floor(h) % 6;
995
996  var f = h - Math.floor(h),
997      p = 255 * v * (1 - s),
998      q = 255 * v * (1 - (s * f)),
999      t = 255 * v * (1 - (s * (1 - f))),
1000      v = 255 * v;
1001
1002  switch(hi) {
1003    case 0:
1004      return [v, t, p];
1005    case 1:
1006      return [q, v, p];
1007    case 2:
1008      return [p, v, t];
1009    case 3:
1010      return [p, q, v];
1011    case 4:
1012      return [t, p, v];
1013    case 5:
1014      return [v, p, q];
1015  }
1016}
1017
1018function hsv2hsl(hsv) {
1019  var h = hsv[0],
1020      s = hsv[1] / 100,
1021      v = hsv[2] / 100,
1022      sl, l;
1023
1024  l = (2 - s) * v;
1025  sl = s * v;
1026  sl /= (l <= 1) ? l : 2 - l;
1027  sl = sl || 0;
1028  l /= 2;
1029  return [h, sl * 100, l * 100];
1030}
1031
1032function hsv2hwb(args) {
1033  return rgb2hwb(hsv2rgb(args))
1034}
1035
1036function hsv2cmyk(args) {
1037  return rgb2cmyk(hsv2rgb(args));
1038}
1039
1040function hsv2keyword(args) {
1041  return rgb2keyword(hsv2rgb(args));
1042}
1043
1044// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
1045function hwb2rgb(hwb) {
1046  var h = hwb[0] / 360,
1047      wh = hwb[1] / 100,
1048      bl = hwb[2] / 100,
1049      ratio = wh + bl,
1050      i, v, f, n;
1051
1052  // wh + bl cant be > 1
1053  if (ratio > 1) {
1054    wh /= ratio;
1055    bl /= ratio;
1056  }
1057
1058  i = Math.floor(6 * h);
1059  v = 1 - bl;
1060  f = 6 * h - i;
1061  if ((i & 0x01) != 0) {
1062    f = 1 - f;
1063  }
1064  n = wh + f * (v - wh);  // linear interpolation
1065
1066  switch (i) {
1067    default:
1068    case 6:
1069    case 0: r = v; g = n; b = wh; break;
1070    case 1: r = n; g = v; b = wh; break;
1071    case 2: r = wh; g = v; b = n; break;
1072    case 3: r = wh; g = n; b = v; break;
1073    case 4: r = n; g = wh; b = v; break;
1074    case 5: r = v; g = wh; b = n; break;
1075  }
1076
1077  return [r * 255, g * 255, b * 255];
1078}
1079
1080function hwb2hsl(args) {
1081  return rgb2hsl(hwb2rgb(args));
1082}
1083
1084function hwb2hsv(args) {
1085  return rgb2hsv(hwb2rgb(args));
1086}
1087
1088function hwb2cmyk(args) {
1089  return rgb2cmyk(hwb2rgb(args));
1090}
1091
1092function hwb2keyword(args) {
1093  return rgb2keyword(hwb2rgb(args));
1094}
1095
1096function cmyk2rgb(cmyk) {
1097  var c = cmyk[0] / 100,
1098      m = cmyk[1] / 100,
1099      y = cmyk[2] / 100,
1100      k = cmyk[3] / 100,
1101      r, g, b;
1102
1103  r = 1 - Math.min(1, c * (1 - k) + k);
1104  g = 1 - Math.min(1, m * (1 - k) + k);
1105  b = 1 - Math.min(1, y * (1 - k) + k);
1106  return [r * 255, g * 255, b * 255];
1107}
1108
1109function cmyk2hsl(args) {
1110  return rgb2hsl(cmyk2rgb(args));
1111}
1112
1113function cmyk2hsv(args) {
1114  return rgb2hsv(cmyk2rgb(args));
1115}
1116
1117function cmyk2hwb(args) {
1118  return rgb2hwb(cmyk2rgb(args));
1119}
1120
1121function cmyk2keyword(args) {
1122  return rgb2keyword(cmyk2rgb(args));
1123}
1124
1125
1126function xyz2rgb(xyz) {
1127  var x = xyz[0] / 100,
1128      y = xyz[1] / 100,
1129      z = xyz[2] / 100,
1130      r, g, b;
1131
1132  r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
1133  g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
1134  b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
1135
1136  // assume sRGB
1137  r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
1138    : r = (r * 12.92);
1139
1140  g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
1141    : g = (g * 12.92);
1142
1143  b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
1144    : b = (b * 12.92);
1145
1146  r = Math.min(Math.max(0, r), 1);
1147  g = Math.min(Math.max(0, g), 1);
1148  b = Math.min(Math.max(0, b), 1);
1149
1150  return [r * 255, g * 255, b * 255];
1151}
1152
1153function xyz2lab(xyz) {
1154  var x = xyz[0],
1155      y = xyz[1],
1156      z = xyz[2],
1157      l, a, b;
1158
1159  x /= 95.047;
1160  y /= 100;
1161  z /= 108.883;
1162
1163  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
1164  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
1165  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
1166
1167  l = (116 * y) - 16;
1168  a = 500 * (x - y);
1169  b = 200 * (y - z);
1170
1171  return [l, a, b];
1172}
1173
1174function xyz2lch(args) {
1175  return lab2lch(xyz2lab(args));
1176}
1177
1178function lab2xyz(lab) {
1179  var l = lab[0],
1180      a = lab[1],
1181      b = lab[2],
1182      x, y, z, y2;
1183
1184  if (l <= 8) {
1185    y = (l * 100) / 903.3;
1186    y2 = (7.787 * (y / 100)) + (16 / 116);
1187  } else {
1188    y = 100 * Math.pow((l + 16) / 116, 3);
1189    y2 = Math.pow(y / 100, 1/3);
1190  }
1191
1192  x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
1193
1194  z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
1195
1196  return [x, y, z];
1197}
1198
1199function lab2lch(lab) {
1200  var l = lab[0],
1201      a = lab[1],
1202      b = lab[2],
1203      hr, h, c;
1204
1205  hr = Math.atan2(b, a);
1206  h = hr * 360 / 2 / Math.PI;
1207  if (h < 0) {
1208    h += 360;
1209  }
1210  c = Math.sqrt(a * a + b * b);
1211  return [l, c, h];
1212}
1213
1214function lab2rgb(args) {
1215  return xyz2rgb(lab2xyz(args));
1216}
1217
1218function lch2lab(lch) {
1219  var l = lch[0],
1220      c = lch[1],
1221      h = lch[2],
1222      a, b, hr;
1223
1224  hr = h / 360 * 2 * Math.PI;
1225  a = c * Math.cos(hr);
1226  b = c * Math.sin(hr);
1227  return [l, a, b];
1228}
1229
1230function lch2xyz(args) {
1231  return lab2xyz(lch2lab(args));
1232}
1233
1234function lch2rgb(args) {
1235  return lab2rgb(lch2lab(args));
1236}
1237
1238function keyword2rgb(keyword) {
1239  return cssKeywords[keyword];
1240}
1241
1242function keyword2hsl(args) {
1243  return rgb2hsl(keyword2rgb(args));
1244}
1245
1246function keyword2hsv(args) {
1247  return rgb2hsv(keyword2rgb(args));
1248}
1249
1250function keyword2hwb(args) {
1251  return rgb2hwb(keyword2rgb(args));
1252}
1253
1254function keyword2cmyk(args) {
1255  return rgb2cmyk(keyword2rgb(args));
1256}
1257
1258function keyword2lab(args) {
1259  return rgb2lab(keyword2rgb(args));
1260}
1261
1262function keyword2xyz(args) {
1263  return rgb2xyz(keyword2rgb(args));
1264}
1265
1266var cssKeywords = {
1267  aliceblue:  [240,248,255],
1268  antiquewhite: [250,235,215],
1269  aqua: [0,255,255],
1270  aquamarine: [127,255,212],
1271  azure:  [240,255,255],
1272  beige:  [245,245,220],
1273  bisque: [255,228,196],
1274  black:  [0,0,0],
1275  blanchedalmond: [255,235,205],
1276  blue: [0,0,255],
1277  blueviolet: [138,43,226],
1278  brown:  [165,42,42],
1279  burlywood:  [222,184,135],
1280  cadetblue:  [95,158,160],
1281  chartreuse: [127,255,0],
1282  chocolate:  [210,105,30],
1283  coral:  [255,127,80],
1284  cornflowerblue: [100,149,237],
1285  cornsilk: [255,248,220],
1286  crimson:  [220,20,60],
1287  cyan: [0,255,255],
1288  darkblue: [0,0,139],
1289  darkcyan: [0,139,139],
1290  darkgoldenrod:  [184,134,11],
1291  darkgray: [169,169,169],
1292  darkgreen:  [0,100,0],
1293  darkgrey: [169,169,169],
1294  darkkhaki:  [189,183,107],
1295  darkmagenta:  [139,0,139],
1296  darkolivegreen: [85,107,47],
1297  darkorange: [255,140,0],
1298  darkorchid: [153,50,204],
1299  darkred:  [139,0,0],
1300  darksalmon: [233,150,122],
1301  darkseagreen: [143,188,143],
1302  darkslateblue:  [72,61,139],
1303  darkslategray:  [47,79,79],
1304  darkslategrey:  [47,79,79],
1305  darkturquoise:  [0,206,209],
1306  darkviolet: [148,0,211],
1307  deeppink: [255,20,147],
1308  deepskyblue:  [0,191,255],
1309  dimgray:  [105,105,105],
1310  dimgrey:  [105,105,105],
1311  dodgerblue: [30,144,255],
1312  firebrick:  [178,34,34],
1313  floralwhite:  [255,250,240],
1314  forestgreen:  [34,139,34],
1315  fuchsia:  [255,0,255],
1316  gainsboro:  [220,220,220],
1317  ghostwhite: [248,248,255],
1318  gold: [255,215,0],
1319  goldenrod:  [218,165,32],
1320  gray: [128,128,128],
1321  green:  [0,128,0],
1322  greenyellow:  [173,255,47],
1323  grey: [128,128,128],
1324  honeydew: [240,255,240],
1325  hotpink:  [255,105,180],
1326  indianred:  [205,92,92],
1327  indigo: [75,0,130],
1328  ivory:  [255,255,240],
1329  khaki:  [240,230,140],
1330  lavender: [230,230,250],
1331  lavenderblush:  [255,240,245],
1332  lawngreen:  [124,252,0],
1333  lemonchiffon: [255,250,205],
1334  lightblue:  [173,216,230],
1335  lightcoral: [240,128,128],
1336  lightcyan:  [224,255,255],
1337  lightgoldenrodyellow: [250,250,210],
1338  lightgray:  [211,211,211],
1339  lightgreen: [144,238,144],
1340  lightgrey:  [211,211,211],
1341  lightpink:  [255,182,193],
1342  lightsalmon:  [255,160,122],
1343  lightseagreen:  [32,178,170],
1344  lightskyblue: [135,206,250],
1345  lightslategray: [119,136,153],
1346  lightslategrey: [119,136,153],
1347  lightsteelblue: [176,196,222],
1348  lightyellow:  [255,255,224],
1349  lime: [0,255,0],
1350  limegreen:  [50,205,50],
1351  linen:  [250,240,230],
1352  magenta:  [255,0,255],
1353  maroon: [128,0,0],
1354  mediumaquamarine: [102,205,170],
1355  mediumblue: [0,0,205],
1356  mediumorchid: [186,85,211],
1357  mediumpurple: [147,112,219],
1358  mediumseagreen: [60,179,113],
1359  mediumslateblue:  [123,104,238],
1360  mediumspringgreen:  [0,250,154],
1361  mediumturquoise:  [72,209,204],
1362  mediumvioletred:  [199,21,133],
1363  midnightblue: [25,25,112],
1364  mintcream:  [245,255,250],
1365  mistyrose:  [255,228,225],
1366  moccasin: [255,228,181],
1367  navajowhite:  [255,222,173],
1368  navy: [0,0,128],
1369  oldlace:  [253,245,230],
1370  olive:  [128,128,0],
1371  olivedrab:  [107,142,35],
1372  orange: [255,165,0],
1373  orangered:  [255,69,0],
1374  orchid: [218,112,214],
1375  palegoldenrod:  [238,232,170],
1376  palegreen:  [152,251,152],
1377  paleturquoise:  [175,238,238],
1378  palevioletred:  [219,112,147],
1379  papayawhip: [255,239,213],
1380  peachpuff:  [255,218,185],
1381  peru: [205,133,63],
1382  pink: [255,192,203],
1383  plum: [221,160,221],
1384  powderblue: [176,224,230],
1385  purple: [128,0,128],
1386  rebeccapurple: [102, 51, 153],
1387  red:  [255,0,0],
1388  rosybrown:  [188,143,143],
1389  royalblue:  [65,105,225],
1390  saddlebrown:  [139,69,19],
1391  salmon: [250,128,114],
1392  sandybrown: [244,164,96],
1393  seagreen: [46,139,87],
1394  seashell: [255,245,238],
1395  sienna: [160,82,45],
1396  silver: [192,192,192],
1397  skyblue:  [135,206,235],
1398  slateblue:  [106,90,205],
1399  slategray:  [112,128,144],
1400  slategrey:  [112,128,144],
1401  snow: [255,250,250],
1402  springgreen:  [0,255,127],
1403  steelblue:  [70,130,180],
1404  tan:  [210,180,140],
1405  teal: [0,128,128],
1406  thistle:  [216,191,216],
1407  tomato: [255,99,71],
1408  turquoise:  [64,224,208],
1409  violet: [238,130,238],
1410  wheat:  [245,222,179],
1411  white:  [255,255,255],
1412  whitesmoke: [245,245,245],
1413  yellow: [255,255,0],
1414  yellowgreen:  [154,205,50]
1415};
1416
1417var reverseKeywords = {};
1418for (var key in cssKeywords) {
1419  reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
1420}
1421
1422},{}],5:[function(require,module,exports){
1423var conversions = require(4);
1424
1425var convert = function() {
1426   return new Converter();
1427}
1428
1429for (var func in conversions) {
1430  // export Raw versions
1431  convert[func + "Raw"] =  (function(func) {
1432    // accept array or plain args
1433    return function(arg) {
1434      if (typeof arg == "number")
1435        arg = Array.prototype.slice.call(arguments);
1436      return conversions[func](arg);
1437    }
1438  })(func);
1439
1440  var pair = /(\w+)2(\w+)/.exec(func),
1441      from = pair[1],
1442      to = pair[2];
1443
1444  // export rgb2hsl and ["rgb"]["hsl"]
1445  convert[from] = convert[from] || {};
1446
1447  convert[from][to] = convert[func] = (function(func) { 
1448    return function(arg) {
1449      if (typeof arg == "number")
1450        arg = Array.prototype.slice.call(arguments);
1451      
1452      var val = conversions[func](arg);
1453      if (typeof val == "string" || val === undefined)
1454        return val; // keyword
1455
1456      for (var i = 0; i < val.length; i++)
1457        val[i] = Math.round(val[i]);
1458      return val;
1459    }
1460  })(func);
1461}
1462
1463
1464/* Converter does lazy conversion and caching */
1465var Converter = function() {
1466   this.convs = {};
1467};
1468
1469/* Either get the values for a space or
1470  set the values for a space, depending on args */
1471Converter.prototype.routeSpace = function(space, args) {
1472   var values = args[0];
1473   if (values === undefined) {
1474      // color.rgb()
1475      return this.getValues(space);
1476   }
1477   // color.rgb(10, 10, 10)
1478   if (typeof values == "number") {
1479      values = Array.prototype.slice.call(args);        
1480   }
1481
1482   return this.setValues(space, values);
1483};
1484  
1485/* Set the values for a space, invalidating cache */
1486Converter.prototype.setValues = function(space, values) {
1487   this.space = space;
1488   this.convs = {};
1489   this.convs[space] = values;
1490   return this;
1491};
1492
1493/* Get the values for a space. If there's already
1494  a conversion for the space, fetch it, otherwise
1495  compute it */
1496Converter.prototype.getValues = function(space) {
1497   var vals = this.convs[space];
1498   if (!vals) {
1499      var fspace = this.space,
1500          from = this.convs[fspace];
1501      vals = convert[fspace][space](from);
1502
1503      this.convs[space] = vals;
1504   }
1505  return vals;
1506};
1507
1508["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
1509   Converter.prototype[space] = function(vals) {
1510      return this.routeSpace(space, arguments);
1511   }
1512});
1513
1514module.exports = convert;
1515},{"4":4}],6:[function(require,module,exports){
1516'use strict'
1517
1518module.exports = {
1519	"aliceblue": [240, 248, 255],
1520	"antiquewhite": [250, 235, 215],
1521	"aqua": [0, 255, 255],
1522	"aquamarine": [127, 255, 212],
1523	"azure": [240, 255, 255],
1524	"beige": [245, 245, 220],
1525	"bisque": [255, 228, 196],
1526	"black": [0, 0, 0],
1527	"blanchedalmond": [255, 235, 205],
1528	"blue": [0, 0, 255],
1529	"blueviolet": [138, 43, 226],
1530	"brown": [165, 42, 42],
1531	"burlywood": [222, 184, 135],
1532	"cadetblue": [95, 158, 160],
1533	"chartreuse": [127, 255, 0],
1534	"chocolate": [210, 105, 30],
1535	"coral": [255, 127, 80],
1536	"cornflowerblue": [100, 149, 237],
1537	"cornsilk": [255, 248, 220],
1538	"crimson": [220, 20, 60],
1539	"cyan": [0, 255, 255],
1540	"darkblue": [0, 0, 139],
1541	"darkcyan": [0, 139, 139],
1542	"darkgoldenrod": [184, 134, 11],
1543	"darkgray": [169, 169, 169],
1544	"darkgreen": [0, 100, 0],
1545	"darkgrey": [169, 169, 169],
1546	"darkkhaki": [189, 183, 107],
1547	"darkmagenta": [139, 0, 139],
1548	"darkolivegreen": [85, 107, 47],
1549	"darkorange": [255, 140, 0],
1550	"darkorchid": [153, 50, 204],
1551	"darkred": [139, 0, 0],
1552	"darksalmon": [233, 150, 122],
1553	"darkseagreen": [143, 188, 143],
1554	"darkslateblue": [72, 61, 139],
1555	"darkslategray": [47, 79, 79],
1556	"darkslategrey": [47, 79, 79],
1557	"darkturquoise": [0, 206, 209],
1558	"darkviolet": [148, 0, 211],
1559	"deeppink": [255, 20, 147],
1560	"deepskyblue": [0, 191, 255],
1561	"dimgray": [105, 105, 105],
1562	"dimgrey": [105, 105, 105],
1563	"dodgerblue": [30, 144, 255],
1564	"firebrick": [178, 34, 34],
1565	"floralwhite": [255, 250, 240],
1566	"forestgreen": [34, 139, 34],
1567	"fuchsia": [255, 0, 255],
1568	"gainsboro": [220, 220, 220],
1569	"ghostwhite": [248, 248, 255],
1570	"gold": [255, 215, 0],
1571	"goldenrod": [218, 165, 32],
1572	"gray": [128, 128, 128],
1573	"green": [0, 128, 0],
1574	"greenyellow": [173, 255, 47],
1575	"grey": [128, 128, 128],
1576	"honeydew": [240, 255, 240],
1577	"hotpink": [255, 105, 180],
1578	"indianred": [205, 92, 92],
1579	"indigo": [75, 0, 130],
1580	"ivory": [255, 255, 240],
1581	"khaki": [240, 230, 140],
1582	"lavender": [230, 230, 250],
1583	"lavenderblush": [255, 240, 245],
1584	"lawngreen": [124, 252, 0],
1585	"lemonchiffon": [255, 250, 205],
1586	"lightblue": [173, 216, 230],
1587	"lightcoral": [240, 128, 128],
1588	"lightcyan": [224, 255, 255],
1589	"lightgoldenrodyellow": [250, 250, 210],
1590	"lightgray": [211, 211, 211],
1591	"lightgreen": [144, 238, 144],
1592	"lightgrey": [211, 211, 211],
1593	"lightpink": [255, 182, 193],
1594	"lightsalmon": [255, 160, 122],
1595	"lightseagreen": [32, 178, 170],
1596	"lightskyblue": [135, 206, 250],
1597	"lightslategray": [119, 136, 153],
1598	"lightslategrey": [119, 136, 153],
1599	"lightsteelblue": [176, 196, 222],
1600	"lightyellow": [255, 255, 224],
1601	"lime": [0, 255, 0],
1602	"limegreen": [50, 205, 50],
1603	"linen": [250, 240, 230],
1604	"magenta": [255, 0, 255],
1605	"maroon": [128, 0, 0],
1606	"mediumaquamarine": [102, 205, 170],
1607	"mediumblue": [0, 0, 205],
1608	"mediumorchid": [186, 85, 211],
1609	"mediumpurple": [147, 112, 219],
1610	"mediumseagreen": [60, 179, 113],
1611	"mediumslateblue": [123, 104, 238],
1612	"mediumspringgreen": [0, 250, 154],
1613	"mediumturquoise": [72, 209, 204],
1614	"mediumvioletred": [199, 21, 133],
1615	"midnightblue": [25, 25, 112],
1616	"mintcream": [245, 255, 250],
1617	"mistyrose": [255, 228, 225],
1618	"moccasin": [255, 228, 181],
1619	"navajowhite": [255, 222, 173],
1620	"navy": [0, 0, 128],
1621	"oldlace": [253, 245, 230],
1622	"olive": [128, 128, 0],
1623	"olivedrab": [107, 142, 35],
1624	"orange": [255, 165, 0],
1625	"orangered": [255, 69, 0],
1626	"orchid": [218, 112, 214],
1627	"palegoldenrod": [238, 232, 170],
1628	"palegreen": [152, 251, 152],
1629	"paleturquoise": [175, 238, 238],
1630	"palevioletred": [219, 112, 147],
1631	"papayawhip": [255, 239, 213],
1632	"peachpuff": [255, 218, 185],
1633	"peru": [205, 133, 63],
1634	"pink": [255, 192, 203],
1635	"plum": [221, 160, 221],
1636	"powderblue": [176, 224, 230],
1637	"purple": [128, 0, 128],
1638	"rebeccapurple": [102, 51, 153],
1639	"red": [255, 0, 0],
1640	"rosybrown": [188, 143, 143],
1641	"royalblue": [65, 105, 225],
1642	"saddlebrown": [139, 69, 19],
1643	"salmon": [250, 128, 114],
1644	"sandybrown": [244, 164, 96],
1645	"seagreen": [46, 139, 87],
1646	"seashell": [255, 245, 238],
1647	"sienna": [160, 82, 45],
1648	"silver": [192, 192, 192],
1649	"skyblue": [135, 206, 235],
1650	"slateblue": [106, 90, 205],
1651	"slategray": [112, 128, 144],
1652	"slategrey": [112, 128, 144],
1653	"snow": [255, 250, 250],
1654	"springgreen": [0, 255, 127],
1655	"steelblue": [70, 130, 180],
1656	"tan": [210, 180, 140],
1657	"teal": [0, 128, 128],
1658	"thistle": [216, 191, 216],
1659	"tomato": [255, 99, 71],
1660	"turquoise": [64, 224, 208],
1661	"violet": [238, 130, 238],
1662	"wheat": [245, 222, 179],
1663	"white": [255, 255, 255],
1664	"whitesmoke": [245, 245, 245],
1665	"yellow": [255, 255, 0],
1666	"yellowgreen": [154, 205, 50]
1667};
1668
1669},{}],7:[function(require,module,exports){
1670/**
1671 * @namespace Chart
1672 */
1673var Chart = require(30)();
1674
1675Chart.helpers = require(46);
1676
1677// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
1678require(28)(Chart);
1679
1680Chart.Animation = require(22);
1681Chart.animationService = require(23);
1682Chart.defaults = require(26);
1683Chart.Element = require(27);
1684Chart.elements = require(41);
1685Chart.Interaction = require(29);
1686Chart.layouts = require(31);
1687Chart.platform = require(49);
1688Chart.plugins = require(32);
1689Chart.Scale = require(33);
1690Chart.scaleService = require(34);
1691Chart.Ticks = require(35);
1692Chart.Tooltip = require(36);
1693
1694require(24)(Chart);
1695require(25)(Chart);
1696
1697require(56)(Chart);
1698require(54)(Chart);
1699require(55)(Chart);
1700require(57)(Chart);
1701require(58)(Chart);
1702require(59)(Chart);
1703
1704// Controllers must be loaded after elements
1705// See Chart.core.datasetController.dataElementType
1706require(15)(Chart);
1707require(16)(Chart);
1708require(17)(Chart);
1709require(18)(Chart);
1710require(19)(Chart);
1711require(20)(Chart);
1712require(21)(Chart);
1713
1714require(8)(Chart);
1715require(9)(Chart);
1716require(10)(Chart);
1717require(11)(Chart);
1718require(12)(Chart);
1719require(13)(Chart);
1720require(14)(Chart);
1721
1722// Loading built-in plugins
1723var plugins = require(50);
1724for (var k in plugins) {
1725	if (plugins.hasOwnProperty(k)) {
1726		Chart.plugins.register(plugins[k]);
1727	}
1728}
1729
1730Chart.platform.initialize();
1731
1732module.exports = Chart;
1733if (typeof window !== 'undefined') {
1734	window.Chart = Chart;
1735}
1736
1737// DEPRECATIONS
1738
1739/**
1740 * Provided for backward compatibility, not available anymore
1741 * @namespace Chart.Legend
1742 * @deprecated since version 2.1.5
1743 * @todo remove at version 3
1744 * @private
1745 */
1746Chart.Legend = plugins.legend._element;
1747
1748/**
1749 * Provided for backward compatibility, not available anymore
1750 * @namespace Chart.Title
1751 * @deprecated since version 2.1.5
1752 * @todo remove at version 3
1753 * @private
1754 */
1755Chart.Title = plugins.title._element;
1756
1757/**
1758 * Provided for backward compatibility, use Chart.plugins instead
1759 * @namespace Chart.pluginService
1760 * @deprecated since version 2.1.5
1761 * @todo remove at version 3
1762 * @private
1763 */
1764Chart.pluginService = Chart.plugins;
1765
1766/**
1767 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
1768 * effect, instead simply create/register plugins via plain JavaScript objects.
1769 * @interface Chart.PluginBase
1770 * @deprecated since version 2.5.0
1771 * @todo remove at version 3
1772 * @private
1773 */
1774Chart.PluginBase = Chart.Element.extend({});
1775
1776/**
1777 * Provided for backward compatibility, use Chart.helpers.canvas instead.
1778 * @namespace Chart.canvasHelpers
1779 * @deprecated since version 2.6.0
1780 * @todo remove at version 3
1781 * @private
1782 */
1783Chart.canvasHelpers = Chart.helpers.canvas;
1784
1785/**
1786 * Provided for backward compatibility, use Chart.layouts instead.
1787 * @namespace Chart.layoutService
1788 * @deprecated since version 2.8.0
1789 * @todo remove at version 3
1790 * @private
1791 */
1792Chart.layoutService = Chart.layouts;
1793
1794},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"41":41,"46":46,"49":49,"50":50,"54":54,"55":55,"56":56,"57":57,"58":58,"59":59,"8":8,"9":9}],8:[function(require,module,exports){
1795'use strict';
1796
1797module.exports = function(Chart) {
1798
1799	Chart.Bar = function(context, config) {
1800		config.type = 'bar';
1801
1802		return new Chart(context, config);
1803	};
1804
1805};
1806
1807},{}],9:[function(require,module,exports){
1808'use strict';
1809
1810module.exports = function(Chart) {
1811
1812	Chart.Bubble = function(context, config) {
1813		config.type = 'bubble';
1814		return new Chart(context, config);
1815	};
1816
1817};
1818
1819},{}],10:[function(require,module,exports){
1820'use strict';
1821
1822module.exports = function(Chart) {
1823
1824	Chart.Doughnut = function(context, config) {
1825		config.type = 'doughnut';
1826
1827		return new Chart(context, config);
1828	};
1829
1830};
1831
1832},{}],11:[function(require,module,exports){
1833'use strict';
1834
1835module.exports = function(Chart) {
1836
1837	Chart.Line = function(context, config) {
1838		config.type = 'line';
1839
1840		return new Chart(context, config);
1841	};
1842
1843};
1844
1845},{}],12:[function(require,module,exports){
1846'use strict';
1847
1848module.exports = function(Chart) {
1849
1850	Chart.PolarArea = function(context, config) {
1851		config.type = 'polarArea';
1852
1853		return new Chart(context, config);
1854	};
1855
1856};
1857
1858},{}],13:[function(require,module,exports){
1859'use strict';
1860
1861module.exports = function(Chart) {
1862
1863	Chart.Radar = function(context, config) {
1864		config.type = 'radar';
1865
1866		return new Chart(context, config);
1867	};
1868
1869};
1870
1871},{}],14:[function(require,module,exports){
1872'use strict';
1873
1874module.exports = function(Chart) {
1875	Chart.Scatter = function(context, config) {
1876		config.type = 'scatter';
1877		return new Chart(context, config);
1878	};
1879};
1880
1881},{}],15:[function(require,module,exports){
1882'use strict';
1883
1884var defaults = require(26);
1885var elements = require(41);
1886var helpers = require(46);
1887
1888defaults._set('bar', {
1889	hover: {
1890		mode: 'label'
1891	},
1892
1893	scales: {
1894		xAxes: [{
1895			type: 'category',
1896
1897			// Specific to Bar Controller
1898			categoryPercentage: 0.8,
1899			barPercentage: 0.9,
1900
1901			// offset settings
1902			offset: true,
1903
1904			// grid line settings
1905			gridLines: {
1906				offsetGridLines: true
1907			}
1908		}],
1909
1910		yAxes: [{
1911			type: 'linear'
1912		}]
1913	}
1914});
1915
1916defaults._set('horizontalBar', {
1917	hover: {
1918		mode: 'index',
1919		axis: 'y'
1920	},
1921
1922	scales: {
1923		xAxes: [{
1924			type: 'linear',
1925			position: 'bottom'
1926		}],
1927
1928		yAxes: [{
1929			position: 'left',
1930			type: 'category',
1931
1932			// Specific to Horizontal Bar Controller
1933			categoryPercentage: 0.8,
1934			barPercentage: 0.9,
1935
1936			// offset settings
1937			offset: true,
1938
1939			// grid line settings
1940			gridLines: {
1941				offsetGridLines: true
1942			}
1943		}]
1944	},
1945
1946	elements: {
1947		rectangle: {
1948			borderSkipped: 'left'
1949		}
1950	},
1951
1952	tooltips: {
1953		callbacks: {
1954			title: function(item, data) {
1955				// Pick first xLabel for now
1956				var title = '';
1957
1958				if (item.length > 0) {
1959					if (item[0].yLabel) {
1960						title = item[0].yLabel;
1961					} else if (data.labels.length > 0 && item[0].index < data.labels.length) {
1962						title = data.labels[item[0].index];
1963					}
1964				}
1965
1966				return title;
1967			},
1968
1969			label: function(item, data) {
1970				var datasetLabel = data.datasets[item.datasetIndex].label || '';
1971				return datasetLabel + ': ' + item.xLabel;
1972			}
1973		},
1974		mode: 'index',
1975		axis: 'y'
1976	}
1977});
1978
1979/**
1980 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
1981 * @private
1982 */
1983function computeMinSampleSize(scale, pixels) {
1984	var min = scale.isHorizontal() ? scale.width : scale.height;
1985	var ticks = scale.getTicks();
1986	var prev, curr, i, ilen;
1987
1988	for (i = 1, ilen = pixels.length; i < ilen; ++i) {
1989		min = Math.min(min, pixels[i] - pixels[i - 1]);
1990	}
1991
1992	for (i = 0, ilen = ticks.length; i < ilen; ++i) {
1993		curr = scale.getPixelForTick(i);
1994		min = i > 0 ? Math.min(min, curr - prev) : min;
1995		prev = curr;
1996	}
1997
1998	return min;
1999}
2000
2001/**
2002 * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
2003 * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
2004 * mode currently always generates bars equally sized (until we introduce scriptable options?).
2005 * @private
2006 */
2007function computeFitCategoryTraits(index, ruler, options) {
2008	var thickness = options.barThickness;
2009	var count = ruler.stackCount;
2010	var curr = ruler.pixels[index];
2011	var size, ratio;
2012
2013	if (helpers.isNullOrUndef(thickness)) {
2014		size = ruler.min * options.categoryPercentage;
2015		ratio = options.barPercentage;
2016	} else {
2017		// When bar thickness is enforced, category and bar percentages are ignored.
2018		// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
2019		// and deprecate barPercentage since this value is ignored when thickness is absolute.
2020		size = thickness * count;
2021		ratio = 1;
2022	}
2023
2024	return {
2025		chunk: size / count,
2026		ratio: ratio,
2027		start: curr - (size / 2)
2028	};
2029}
2030
2031/**
2032 * Computes an "optimal" category that globally arranges bars side by side (no gap when
2033 * percentage options are 1), based on the previous and following categories. This mode
2034 * generates bars with different widths when data are not evenly spaced.
2035 * @private
2036 */
2037function computeFlexCategoryTraits(index, ruler, options) {
2038	var pixels = ruler.pixels;
2039	var curr = pixels[index];
2040	var prev = index > 0 ? pixels[index - 1] : null;
2041	var next = index < pixels.length - 1 ? pixels[index + 1] : null;
2042	var percent = options.categoryPercentage;
2043	var start, size;
2044
2045	if (prev === null) {
2046		// first data: its size is double based on the next point or,
2047		// if it's also the last data, we use the scale end extremity.
2048		prev = curr - (next === null ? ruler.end - curr : next - curr);
2049	}
2050
2051	if (next === null) {
2052		// last data: its size is also double based on the previous point.
2053		next = curr + curr - prev;
2054	}
2055
2056	start = curr - ((curr - prev) / 2) * percent;
2057	size = ((next - prev) / 2) * percent;
2058
2059	return {
2060		chunk: size / ruler.stackCount,
2061		ratio: options.barPercentage,
2062		start: start
2063	};
2064}
2065
2066module.exports = function(Chart) {
2067
2068	Chart.controllers.bar = Chart.DatasetController.extend({
2069
2070		dataElementType: elements.Rectangle,
2071
2072		initialize: function() {
2073			var me = this;
2074			var meta;
2075
2076			Chart.DatasetController.prototype.initialize.apply(me, arguments);
2077
2078			meta = me.getMeta();
2079			meta.stack = me.getDataset().stack;
2080			meta.bar = true;
2081		},
2082
2083		update: function(reset) {
2084			var me = this;
2085			var rects = me.getMeta().data;
2086			var i, ilen;
2087
2088			me._ruler = me.getRuler();
2089
2090			for (i = 0, ilen = rects.length; i < ilen; ++i) {
2091				me.updateElement(rects[i], i, reset);
2092			}
2093		},
2094
2095		updateElement: function(rectangle, index, reset) {
2096			var me = this;
2097			var chart = me.chart;
2098			var meta = me.getMeta();
2099			var dataset = me.getDataset();
2100			var custom = rectangle.custom || {};
2101			var rectangleOptions = chart.options.elements.rectangle;
2102
2103			rectangle._xScale = me.getScaleForId(meta.xAxisID);
2104			rectangle._yScale = me.getScaleForId(meta.yAxisID);
2105			rectangle._datasetIndex = me.index;
2106			rectangle._index = index;
2107
2108			rectangle._model = {
2109				datasetLabel: dataset.label,
2110				label: chart.data.labels[index],
2111				borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
2112				backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
2113				borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
2114				borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
2115			};
2116
2117			me.updateElementGeometry(rectangle, index, reset);
2118
2119			rectangle.pivot();
2120		},
2121
2122		/**
2123		 * @private
2124		 */
2125		updateElementGeometry: function(rectangle, index, reset) {
2126			var me = this;
2127			var model = rectangle._model;
2128			var vscale = me.getValueScale();
2129			var base = vscale.getBasePixel();
2130			var horizontal = vscale.isHorizontal();
2131			var ruler = me._ruler || me.getRuler();
2132			var vpixels = me.calculateBarValuePixels(me.index, index);
2133			var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
2134
2135			model.horizontal = horizontal;
2136			model.base = reset ? base : vpixels.base;
2137			model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
2138			model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
2139			model.height = horizontal ? ipixels.size : undefined;
2140			model.width = horizontal ? undefined : ipixels.size;
2141		},
2142
2143		/**
2144		 * @private
2145		 */
2146		getValueScaleId: function() {
2147			return this.getMeta().yAxisID;
2148		},
2149
2150		/**
2151		 * @private
2152		 */
2153		getIndexScaleId: function() {
2154			return this.getMeta().xAxisID;
2155		},
2156
2157		/**
2158		 * @private
2159		 */
2160		getValueScale: function() {
2161			return this.getScaleForId(this.getValueScaleId());
2162		},
2163
2164		/**
2165		 * @private
2166		 */
2167		getIndexScale: function() {
2168			return this.getScaleForId(this.getIndexScaleId());
2169		},
2170
2171		/**
2172		 * Returns the stacks based on groups and bar visibility.
2173		 * @param {Number} [last] - The dataset index
2174		 * @returns {Array} The stack list
2175		 * @private
2176		 */
2177		_getStacks: function(last) {
2178			var me = this;
2179			var chart = me.chart;
2180			var scale = me.getIndexScale();
2181			var stacked = scale.options.stacked;
2182			var ilen = last === undefined ? chart.data.datasets.length : last + 1;
2183			var stacks = [];
2184			var i, meta;
2185
2186			for (i = 0; i < ilen; ++i) {
2187				meta = chart.getDatasetMeta(i);
2188				if (meta.bar && chart.isDatasetVisible(i) &&
2189					(stacked === false ||
2190					(stacked === true && stacks.indexOf(meta.stack) === -1) ||
2191					(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
2192					stacks.push(meta.stack);
2193				}
2194			}
2195
2196			return stacks;
2197		},
2198
2199		/**
2200		 * Returns the effective number of stacks based on groups and bar visibility.
2201		 * @private
2202		 */
2203		getStackCount: function() {
2204			return this._getStacks().length;
2205		},
2206
2207		/**
2208		 * Returns the stack index for the given dataset based on groups and bar visibility.
2209		 * @param {Number} [datasetIndex] - The dataset index
2210		 * @param {String} [name] - The stack name to find
2211		 * @returns {Number} The stack index
2212		 * @private
2213		 */
2214		getStackIndex: function(datasetIndex, name) {
2215			var stacks = this._getStacks(datasetIndex);
2216			var index = (name !== undefined)
2217				? stacks.indexOf(name)
2218				: -1; // indexOf returns -1 if element is not present
2219
2220			return (index === -1)
2221				? stacks.length - 1
2222				: index;
2223		},
2224
2225		/**
2226		 * @private
2227		 */
2228		getRuler: function() {
2229			var me = this;
2230			var scale = me.getIndexScale();
2231			var stackCount = me.getStackCount();
2232			var datasetIndex = me.index;
2233			var isHorizontal = scale.isHorizontal();
2234			var start = isHorizontal ? scale.left : scale.top;
2235			var end = start + (isHorizontal ? scale.width : scale.height);
2236			var pixels = [];
2237			var i, ilen, min;
2238
2239			for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
2240				pixels.push(scale.getPixelForValue(null, i, datasetIndex));
2241			}
2242
2243			min = helpers.isNullOrUndef(scale.options.barThickness)
2244				? computeMinSampleSize(scale, pixels)
2245				: -1;
2246
2247			return {
2248				min: min,
2249				pixels: pixels,
2250				start: start,
2251				end: end,
2252				stackCount: stackCount,
2253				scale: scale
2254			};
2255		},
2256
2257		/**
2258		 * Note: pixel values are not clamped to the scale area.
2259		 * @private
2260		 */
2261		calculateBarValuePixels: function(datasetIndex, index) {
2262			var me = this;
2263			var chart = me.chart;
2264			var meta = me.getMeta();
2265			var scale = me.getValueScale();
2266			var datasets = chart.data.datasets;
2267			var value = scale.getRightValue(datasets[datasetIndex].data[index]);
2268			var stacked = scale.options.stacked;
2269			var stack = meta.stack;
2270			var start = 0;
2271			var i, imeta, ivalue, base, head, size;
2272
2273			if (stacked || (stacked === undefined && stack !== undefined)) {
2274				for (i = 0; i < datasetIndex; ++i) {
2275					imeta = chart.getDatasetMeta(i);
2276
2277					if (imeta.bar &&
2278						imeta.stack === stack &&
2279						imeta.controller.getValueScaleId() === scale.id &&
2280						chart.isDatasetVisible(i)) {
2281
2282						ivalue = scale.getRightValue(datasets[i].data[index]);
2283						if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
2284							start += ivalue;
2285						}
2286					}
2287				}
2288			}
2289
2290			base = scale.getPixelForValue(start);
2291			head = scale.getPixelForValue(start + value);
2292			size = (head - base) / 2;
2293
2294			return {
2295				size: size,
2296				base: base,
2297				head: head,
2298				center: head + size / 2
2299			};
2300		},
2301
2302		/**
2303		 * @private
2304		 */
2305		calculateBarIndexPixels: function(datasetIndex, index, ruler) {
2306			var me = this;
2307			var options = ruler.scale.options;
2308			var range = options.barThickness === 'flex'
2309				? computeFlexCategoryTraits(index, ruler, options)
2310				: computeFitCategoryTraits(index, ruler, options);
2311
2312			var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
2313			var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
2314			var size = Math.min(
2315				helpers.valueOrDefault(options.maxBarThickness, Infinity),
2316				range.chunk * range.ratio);
2317
2318			return {
2319				base: center - size / 2,
2320				head: center + size / 2,
2321				center: center,
2322				size: size
2323			};
2324		},
2325
2326		draw: function() {
2327			var me = this;
2328			var chart = me.chart;
2329			var scale = me.getValueScale();
2330			var rects = me.getMeta().data;
2331			var dataset = me.getDataset();
2332			var ilen = rects.length;
2333			var i = 0;
2334
2335			helpers.canvas.clipArea(chart.ctx, chart.chartArea);
2336
2337			for (; i < ilen; ++i) {
2338				if (!isNaN(scale.getRightValue(dataset.data[i]))) {
2339					rects[i].draw();
2340				}
2341			}
2342
2343			helpers.canvas.unclipArea(chart.ctx);
2344		},
2345	});
2346
2347	Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
2348		/**
2349		 * @private
2350		 */
2351		getValueScaleId: function() {
2352			return this.getMeta().xAxisID;
2353		},
2354
2355		/**
2356		 * @private
2357		 */
2358		getIndexScaleId: function() {
2359			return this.getMeta().yAxisID;
2360		}
2361	});
2362};
2363
2364},{"26":26,"41":41,"46":46}],16:[function(require,module,exports){
2365'use strict';
2366
2367var defaults = require(26);
2368var elements = require(41);
2369var helpers = require(46);
2370
2371defaults._set('bubble', {
2372	hover: {
2373		mode: 'single'
2374	},
2375
2376	scales: {
2377		xAxes: [{
2378			type: 'linear', // bubble should probably use a linear scale by default
2379			position: 'bottom',
2380			id: 'x-axis-0' // need an ID so datasets can reference the scale
2381		}],
2382		yAxes: [{
2383			type: 'linear',
2384			position: 'left',
2385			id: 'y-axis-0'
2386		}]
2387	},
2388
2389	tooltips: {
2390		callbacks: {
2391			title: function() {
2392				// Title doesn't make sense for scatter since we format the data as a point
2393				return '';
2394			},
2395			label: function(item, data) {
2396				var datasetLabel = data.datasets[item.datasetIndex].label || '';
2397				var dataPoint = data.datasets[item.datasetIndex].data[item.index];
2398				return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
2399			}
2400		}
2401	}
2402});
2403
2404
2405module.exports = function(Chart) {
2406
2407	Chart.controllers.bubble = Chart.DatasetController.extend({
2408		/**
2409		 * @protected
2410		 */
2411		dataElementType: elements.Point,
2412
2413		/**
2414		 * @protected
2415		 */
2416		update: function(reset) {
2417			var me = this;
2418			var meta = me.getMeta();
2419			var points = meta.data;
2420
2421			// Update Points
2422			helpers.each(points, function(point, index) {
2423				me.updateElement(point, index, reset);
2424			});
2425		},
2426
2427		/**
2428		 * @protected
2429		 */
2430		updateElement: function(point, index, reset) {
2431			var me = this;
2432			var meta = me.getMeta();
2433			var custom = point.custom || {};
2434			var xScale = me.getScaleForId(meta.xAxisID);
2435			var yScale = me.getScaleForId(meta.yAxisID);
2436			var options = me._resolveElementOptions(point, index);
2437			var data = me.getDataset().data[index];
2438			var dsIndex = me.index;
2439
2440			var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
2441			var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
2442
2443			point._xScale = xScale;
2444			point._yScale = yScale;
2445			point._options = options;
2446			point._datasetIndex = dsIndex;
2447			point._index = index;
2448			point._model = {
2449				backgroundColor: options.backgroundColor,
2450				borderColor: options.borderColor,
2451				borderWidth: options.borderWidth,
2452				hitRadius: options.hitRadius,
2453				pointStyle: options.pointStyle,
2454				rotation: options.rotation,
2455				radius: reset ? 0 : options.radius,
2456				skip: custom.skip || isNaN(x) || isNaN(y),
2457				x: x,
2458				y: y,
2459			};
2460
2461			point.pivot();
2462		},
2463
2464		/**
2465		 * @protected
2466		 */
2467		setHoverStyle: function(point) {
2468			var model = point._model;
2469			var options = point._options;
2470			point.$previousStyle = {
2471				backgroundColor: model.backgroundColor,
2472				borderColor: model.borderColor,
2473				borderWidth: model.borderWidth,
2474				radius: model.radius
2475			};
2476			model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
2477			model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
2478			model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
2479			model.radius = options.radius + options.hoverRadius;
2480		},
2481
2482		/**
2483		 * @private
2484		 */
2485		_resolveElementOptions: function(point, index) {
2486			var me = this;
2487			var chart = me.chart;
2488			var datasets = chart.data.datasets;
2489			var dataset = datasets[me.index];
2490			var custom = point.custom || {};
2491			var options = chart.options.elements.point;
2492			var resolve = helpers.options.resolve;
2493			var data = dataset.data[index];
2494			var values = {};
2495			var i, ilen, key;
2496
2497			// Scriptable options
2498			var context = {
2499				chart: chart,
2500				dataIndex: index,
2501				dataset: dataset,
2502				datasetIndex: me.index
2503			};
2504
2505			var keys = [
2506				'backgroundColor',
2507				'borderColor',
2508				'borderWidth',
2509				'hoverBackgroundColor',
2510				'hoverBorderColor',
2511				'hoverBorderWidth',
2512				'hoverRadius',
2513				'hitRadius',
2514				'pointStyle',
2515				'rotation'
2516			];
2517
2518			for (i = 0, ilen = keys.length; i < ilen; ++i) {
2519				key = keys[i];
2520				values[key] = resolve([
2521					custom[key],
2522					dataset[key],
2523					options[key]
2524				], context, index);
2525			}
2526
2527			// Custom radius resolution
2528			values.radius = resolve([
2529				custom.radius,
2530				data ? data.r : undefined,
2531				dataset.radius,
2532				options.radius
2533			], context, index);
2534			return values;
2535		}
2536	});
2537};
2538
2539},{"26":26,"41":41,"46":46}],17:[function(require,module,exports){
2540'use strict';
2541
2542var defaults = require(26);
2543var elements = require(41);
2544var helpers = require(46);
2545
2546defaults._set('doughnut', {
2547	animation: {
2548		// Boolean - Whether we animate the rotation of the Doughnut
2549		animateRotate: true,
2550		// Boolean - Whether we animate scaling the Doughnut from the centre
2551		animateScale: false
2552	},
2553	hover: {
2554		mode: 'single'
2555	},
2556	legendCallback: function(chart) {
2557		var text = [];
2558		text.push('<ul class="' + chart.id + '-legend">');
2559
2560		var data = chart.data;
2561		var datasets = data.datasets;
2562		var labels = data.labels;
2563
2564		if (datasets.length) {
2565			for (var i = 0; i < datasets[0].data.length; ++i) {
2566				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
2567				if (labels[i]) {
2568					text.push(labels[i]);
2569				}
2570				text.push('</li>');
2571			}
2572		}
2573
2574		text.push('</ul>');
2575		return text.join('');
2576	},
2577	legend: {
2578		labels: {
2579			generateLabels: function(chart) {
2580				var data = chart.data;
2581				if (data.labels.length && data.datasets.length) {
2582					return data.labels.map(function(label, i) {
2583						var meta = chart.getDatasetMeta(0);
2584						var ds = data.datasets[0];
2585						var arc = meta.data[i];
2586						var custom = arc && arc.custom || {};
2587						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2588						var arcOpts = chart.options.elements.arc;
2589						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
2590						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
2591						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
2592
2593						return {
2594							text: label,
2595							fillStyle: fill,
2596							strokeStyle: stroke,
2597							lineWidth: bw,
2598							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
2599
2600							// Extra data used for toggling the correct item
2601							index: i
2602						};
2603					});
2604				}
2605				return [];
2606			}
2607		},
2608
2609		onClick: function(e, legendItem) {
2610			var index = legendItem.index;
2611			var chart = this.chart;
2612			var i, ilen, meta;
2613
2614			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
2615				meta = chart.getDatasetMeta(i);
2616				// toggle visibility of index if exists
2617				if (meta.data[index]) {
2618					meta.data[index].hidden = !meta.data[index].hidden;
2619				}
2620			}
2621
2622			chart.update();
2623		}
2624	},
2625
2626	// The percentage of the chart that we cut out of the middle.
2627	cutoutPercentage: 50,
2628
2629	// The rotation of the chart, where the first data arc begins.
2630	rotation: Math.PI * -0.5,
2631
2632	// The total circumference of the chart.
2633	circumference: Math.PI * 2.0,
2634
2635	// Need to override these to give a nice default
2636	tooltips: {
2637		callbacks: {
2638			title: function() {
2639				return '';
2640			},
2641			label: function(tooltipItem, data) {
2642				var dataLabel = data.labels[tooltipItem.index];
2643				var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
2644
2645				if (helpers.isArray(dataLabel)) {
2646					// show value on first line of multiline label
2647					// need to clone because we are changing the value
2648					dataLabel = dataLabel.slice();
2649					dataLabel[0] += value;
2650				} else {
2651					dataLabel += value;
2652				}
2653
2654				return dataLabel;
2655			}
2656		}
2657	}
2658});
2659
2660defaults._set('pie', helpers.clone(defaults.doughnut));
2661defaults._set('pie', {
2662	cutoutPercentage: 0
2663});
2664
2665module.exports = function(Chart) {
2666
2667	Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
2668
2669		dataElementType: elements.Arc,
2670
2671		linkScales: helpers.noop,
2672
2673		// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
2674		getRingIndex: function(datasetIndex) {
2675			var ringIndex = 0;
2676
2677			for (var j = 0; j < datasetIndex; ++j) {
2678				if (this.chart.isDatasetVisible(j)) {
2679					++ringIndex;
2680				}
2681			}
2682
2683			return ringIndex;
2684		},
2685
2686		update: function(reset) {
2687			var me = this;
2688			var chart = me.chart;
2689			var chartArea = chart.chartArea;
2690			var opts = chart.options;
2691			var arcOpts = opts.elements.arc;
2692			var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
2693			var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
2694			var minSize = Math.min(availableWidth, availableHeight);
2695			var offset = {x: 0, y: 0};
2696			var meta = me.getMeta();
2697			var cutoutPercentage = opts.cutoutPercentage;
2698			var circumference = opts.circumference;
2699
2700			// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
2701			if (circumference < Math.PI * 2.0) {
2702				var startAngle = opts.rotation % (Math.PI * 2.0);
2703				startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
2704				var endAngle = startAngle + circumference;
2705				var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
2706				var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
2707				var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
2708				var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
2709				var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
2710				var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
2711				var cutout = cutoutPercentage / 100.0;
2712				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))};
2713				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))};
2714				var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
2715				minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
2716				offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
2717			}
2718
2719			chart.borderWidth = me.getMaxBorderWidth(meta.data);
2720			chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
2721			chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
2722			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
2723			chart.offsetX = offset.x * chart.outerRadius;
2724			chart.offsetY = offset.y * chart.outerRadius;
2725
2726			meta.total = me.calculateTotal();
2727
2728			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
2729			me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
2730
2731			helpers.each(meta.data, function(arc, index) {
2732				me.updateElement(arc, index, reset);
2733			});
2734		},
2735
2736		updateElement: function(arc, index, reset) {
2737			var me = this;
2738			var chart = me.chart;
2739			var chartArea = chart.chartArea;
2740			var opts = chart.options;
2741			var animationOpts = opts.animation;
2742			var centerX = (chartArea.left + chartArea.right) / 2;
2743			var centerY = (chartArea.top + chartArea.bottom) / 2;
2744			var startAngle = opts.rotation; // non reset case handled later
2745			var endAngle = opts.rotation; // non reset case handled later
2746			var dataset = me.getDataset();
2747			var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
2748			var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
2749			var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
2750			var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2751
2752			helpers.extend(arc, {
2753				// Utility
2754				_datasetIndex: me.index,
2755				_index: index,
2756
2757				// Desired view properties
2758				_model: {
2759					x: centerX + chart.offsetX,
2760					y: centerY + chart.offsetY,
2761					startAngle: startAngle,
2762					endAngle: endAngle,
2763					circumference: circumference,
2764					outerRadius: outerRadius,
2765					innerRadius: innerRadius,
2766					label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
2767				}
2768			});
2769
2770			var model = arc._model;
2771
2772			// Resets the visual styles
2773			var custom = arc.custom || {};
2774			var valueOrDefault = helpers.valueAtIndexOrDefault;
2775			var elementOpts = this.chart.options.elements.arc;
2776			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
2777			model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
2778			model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
2779
2780			// Set correct angles if not resetting
2781			if (!reset || !animationOpts.animateRotate) {
2782				if (index === 0) {
2783					model.startAngle = opts.rotation;
2784				} else {
2785					model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
2786				}
2787
2788				model.endAngle = model.startAngle + model.circumference;
2789			}
2790
2791			arc.pivot();
2792		},
2793
2794		calculateTotal: function() {
2795			var dataset = this.getDataset();
2796			var meta = this.getMeta();
2797			var total = 0;
2798			var value;
2799
2800			helpers.each(meta.data, function(element, index) {
2801				value = dataset.data[index];
2802				if (!isNaN(value) && !element.hidden) {
2803					total += Math.abs(value);
2804				}
2805			});
2806
2807			/* if (total === 0) {
2808				total = NaN;
2809			}*/
2810
2811			return total;
2812		},
2813
2814		calculateCircumference: function(value) {
2815			var total = this.getMeta().total;
2816			if (total > 0 && !isNaN(value)) {
2817				return (Math.PI * 2.0) * (Math.abs(value) / total);
2818			}
2819			return 0;
2820		},
2821
2822		// gets the max border or hover width to properly scale pie charts
2823		getMaxBorderWidth: function(arcs) {
2824			var max = 0;
2825			var index = this.index;
2826			var length = arcs.length;
2827			var borderWidth;
2828			var hoverWidth;
2829
2830			for (var i = 0; i < length; i++) {
2831				borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
2832				hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
2833
2834				max = borderWidth > max ? borderWidth : max;
2835				max = hoverWidth > max ? hoverWidth : max;
2836			}
2837			return max;
2838		}
2839	});
2840};
2841
2842},{"26":26,"41":41,"46":46}],18:[function(require,module,exports){
2843'use strict';
2844
2845var defaults = require(26);
2846var elements = require(41);
2847var helpers = require(46);
2848
2849defaults._set('line', {
2850	showLines: true,
2851	spanGaps: false,
2852
2853	hover: {
2854		mode: 'label'
2855	},
2856
2857	scales: {
2858		xAxes: [{
2859			type: 'category',
2860			id: 'x-axis-0'
2861		}],
2862		yAxes: [{
2863			type: 'linear',
2864			id: 'y-axis-0'
2865		}]
2866	}
2867});
2868
2869module.exports = function(Chart) {
2870
2871	function lineEnabled(dataset, options) {
2872		return helpers.valueOrDefault(dataset.showLine, options.showLines);
2873	}
2874
2875	Chart.controllers.line = Chart.DatasetController.extend({
2876
2877		datasetElementType: elements.Line,
2878
2879		dataElementType: elements.Point,
2880
2881		update: function(reset) {
2882			var me = this;
2883			var meta = me.getMeta();
2884			var line = meta.dataset;
2885			var points = meta.data || [];
2886			var options = me.chart.options;
2887			var lineElementOptions = options.elements.line;
2888			var scale = me.getScaleForId(meta.yAxisID);
2889			var i, ilen, custom;
2890			var dataset = me.getDataset();
2891			var showLine = lineEnabled(dataset, options);
2892
2893			// Update Line
2894			if (showLine) {
2895				custom = line.custom || {};
2896
2897				// Compatibility: If the properties are defined with only the old name, use those values
2898				if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
2899					dataset.lineTension = dataset.tension;
2900				}
2901
2902				// Utility
2903				line._scale = scale;
2904				line._datasetIndex = me.index;
2905				// Data
2906				line._children = points;
2907				// Model
2908				line._model = {
2909					// Appearance
2910					// The default behavior of lines is to break at null values, according
2911					// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
2912					// This option gives lines the ability to span gaps
2913					spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
2914					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
2915					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
2916					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
2917					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
2918					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
2919					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
2920					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
2921					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
2922					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
2923					steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
2924					cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
2925				};
2926
2927				line.pivot();
2928			}
2929
2930			// Update Points
2931			for (i = 0, ilen = points.length; i < ilen; ++i) {
2932				me.updateElement(points[i], i, reset);
2933			}
2934
2935			if (showLine && line._model.tension !== 0) {
2936				me.updateBezierControlPoints();
2937			}
2938
2939			// Now pivot the point for animation
2940			for (i = 0, ilen = points.length; i < ilen; ++i) {
2941				points[i].pivot();
2942			}
2943		},
2944
2945		getPointBackgroundColor: function(point, index) {
2946			var backgroundColor = this.chart.options.elements.point.backgroundColor;
2947			var dataset = this.getDataset();
2948			var custom = point.custom || {};
2949
2950			if (custom.backgroundColor) {
2951				backgroundColor = custom.backgroundColor;
2952			}
2952 else if (dataset.pointBackgroundColor) {
2953				backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
2954			} else if (dataset.backgroundColor) {
2955				backgroundColor = dataset.backgroundColor;
2956			}
2957
2958			return backgroundColor;
2959		},
2960
2961		getPointBorderColor: function(point, index) {
2962			var borderColor = this.chart.options.elements.point.borderColor;
2963			var dataset = this.getDataset();
2964			var custom = point.custom || {};
2965
2966			if (custom.borderColor) {
2967				borderColor = custom.borderColor;
2968			} else if (dataset.pointBorderColor) {
2969				borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
2970			} else if (dataset.borderColor) {
2971				borderColor = dataset.borderColor;
2972			}
2973
2974			return borderColor;
2975		},
2976
2977		getPointBorderWidth: function(point, index) {
2978			var borderWidth = this.chart.options.elements.point.borderWidth;
2979			var dataset = this.getDataset();
2980			var custom = point.custom || {};
2981
2982			if (!isNaN(custom.borderWidth)) {
2983				borderWidth = custom.borderWidth;
2984			} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
2985				borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
2986			} else if (!isNaN(dataset.borderWidth)) {
2987				borderWidth = dataset.borderWidth;
2988			}
2989
2990			return borderWidth;
2991		},
2992
2993		getPointRotation: function(point, index) {
2994			var pointRotation = this.chart.options.elements.point.rotation;
2995			var dataset = this.getDataset();
2996			var custom = point.custom || {};
2997
2998			if (!isNaN(custom.rotation)) {
2999				pointRotation = custom.rotation;
3000			} else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) {
3001				pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation);
3002			}
3003			return pointRotation;
3004		},
3005
3006		updateElement: function(point, index, reset) {
3007			var me = this;
3008			var meta = me.getMeta();
3009			var custom = point.custom || {};
3010			var dataset = me.getDataset();
3011			var datasetIndex = me.index;
3012			var value = dataset.data[index];
3013			var yScale = me.getScaleForId(meta.yAxisID);
3014			var xScale = me.getScaleForId(meta.xAxisID);
3015			var pointOptions = me.chart.options.elements.point;
3016			var x, y;
3017
3018			// Compatibility: If the properties are defined with only the old name, use those values
3019			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3020				dataset.pointRadius = dataset.radius;
3021			}
3022			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3023				dataset.pointHitRadius = dataset.hitRadius;
3024			}
3025
3026			x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
3027			y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
3028
3029			// Utility
3030			point._xScale = xScale;
3031			point._yScale = yScale;
3032			point._datasetIndex = datasetIndex;
3033			point._index = index;
3034
3035			// Desired view properties
3036			point._model = {
3037				x: x,
3038				y: y,
3039				skip: custom.skip || isNaN(x) || isNaN(y),
3040				// Appearance
3041				radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
3042				pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
3043				rotation: me.getPointRotation(point, index),
3044				backgroundColor: me.getPointBackgroundColor(point, index),
3045				borderColor: me.getPointBorderColor(point, index),
3046				borderWidth: me.getPointBorderWidth(point, index),
3047				tension: meta.dataset._model ? meta.dataset._model.tension : 0,
3048				steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
3049				// Tooltip
3050				hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
3051			};
3052		},
3053
3054		calculatePointY: function(value, index, datasetIndex) {
3055			var me = this;
3056			var chart = me.chart;
3057			var meta = me.getMeta();
3058			var yScale = me.getScaleForId(meta.yAxisID);
3059			var sumPos = 0;
3060			var sumNeg = 0;
3061			var i, ds, dsMeta;
3062
3063			if (yScale.options.stacked) {
3064				for (i = 0; i < datasetIndex; i++) {
3065					ds = chart.data.datasets[i];
3066					dsMeta = chart.getDatasetMeta(i);
3067					if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
3068						var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
3069						if (stackedRightValue < 0) {
3070							sumNeg += stackedRightValue || 0;
3071						} else {
3072							sumPos += stackedRightValue || 0;
3073						}
3074					}
3075				}
3076
3077				var rightValue = Number(yScale.getRightValue(value));
3078				if (rightValue < 0) {
3079					return yScale.getPixelForValue(sumNeg + rightValue);
3080				}
3081				return yScale.getPixelForValue(sumPos + rightValue);
3082			}
3083
3084			return yScale.getPixelForValue(value);
3085		},
3086
3087		updateBezierControlPoints: function() {
3088			var me = this;
3089			var meta = me.getMeta();
3090			var area = me.chart.chartArea;
3091			var points = (meta.data || []);
3092			var i, ilen, point, model, controlPoints;
3093
3094			// Only consider points that are drawn in case the spanGaps option is used
3095			if (meta.dataset._model.spanGaps) {
3096				points = points.filter(function(pt) {
3097					return !pt._model.skip;
3098				});
3099			}
3100
3101			function capControlPoint(pt, min, max) {
3102				return Math.max(Math.min(pt, max), min);
3103			}
3104
3105			if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
3106				helpers.splineCurveMonotone(points);
3107			} else {
3108				for (i = 0, ilen = points.length; i < ilen; ++i) {
3109					point = points[i];
3110					model = point._model;
3111					controlPoints = helpers.splineCurve(
3112						helpers.previousItem(points, i)._model,
3113						model,
3114						helpers.nextItem(points, i)._model,
3115						meta.dataset._model.tension
3116					);
3117					model.controlPointPreviousX = controlPoints.previous.x;
3118					model.controlPointPreviousY = controlPoints.previous.y;
3119					model.controlPointNextX = controlPoints.next.x;
3120					model.controlPointNextY = controlPoints.next.y;
3121				}
3122			}
3123
3124			if (me.chart.options.elements.line.capBezierPoints) {
3125				for (i = 0, ilen = points.length; i < ilen; ++i) {
3126					model = points[i]._model;
3127					model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
3128					model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
3129					model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
3130					model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
3131				}
3132			}
3133		},
3134
3135		draw: function() {
3136			var me = this;
3137			var chart = me.chart;
3138			var meta = me.getMeta();
3139			var points = meta.data || [];
3140			var area = chart.chartArea;
3141			var ilen = points.length;
3142			var halfBorderWidth;
3143			var i = 0;
3144
3145			if (lineEnabled(me.getDataset(), chart.options)) {
3146				halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;
3147
3148				helpers.canvas.clipArea(chart.ctx, {
3149					left: area.left,
3150					right: area.right,
3151					top: area.top - halfBorderWidth,
3152					bottom: area.bottom + halfBorderWidth
3153				});
3154
3155				meta.dataset.draw();
3156
3157				helpers.canvas.unclipArea(chart.ctx);
3158			}
3159
3160			// Draw the points
3161			for (; i < ilen; ++i) {
3162				points[i].draw(area);
3163			}
3164		},
3165
3166		setHoverStyle: function(element) {
3167			// Point
3168			var dataset = this.chart.data.datasets[element._datasetIndex];
3169			var index = element._index;
3170			var custom = element.custom || {};
3171			var model = element._model;
3172
3173			element.$previousStyle = {
3174				backgroundColor: model.backgroundColor,
3175				borderColor: model.borderColor,
3176				borderWidth: model.borderWidth,
3177				radius: model.radius
3178			};
3179
3180			model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3181			model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3182			model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3183			model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3184		},
3185	});
3186};
3187
3188},{"26":26,"41":41,"46":46}],19:[function(require,module,exports){
3189'use strict';
3190
3191var defaults = require(26);
3192var elements = require(41);
3193var helpers = require(46);
3194
3195defaults._set('polarArea', {
3196	scale: {
3197		type: 'radialLinear',
3198		angleLines: {
3199			display: false
3200		},
3201		gridLines: {
3202			circular: true
3203		},
3204		pointLabels: {
3205			display: false
3206		},
3207		ticks: {
3208			beginAtZero: true
3209		}
3210	},
3211
3212	// Boolean - Whether to animate the rotation of the chart
3213	animation: {
3214		animateRotate: true,
3215		animateScale: true
3216	},
3217
3218	startAngle: -0.5 * Math.PI,
3219	legendCallback: function(chart) {
3220		var text = [];
3221		text.push('<ul class="' + chart.id + '-legend">');
3222
3223		var data = chart.data;
3224		var datasets = data.datasets;
3225		var labels = data.labels;
3226
3227		if (datasets.length) {
3228			for (var i = 0; i < datasets[0].data.length; ++i) {
3229				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
3230				if (labels[i]) {
3231					text.push(labels[i]);
3232				}
3233				text.push('</li>');
3234			}
3235		}
3236
3237		text.push('</ul>');
3238		return text.join('');
3239	},
3240	legend: {
3241		labels: {
3242			generateLabels: function(chart) {
3243				var data = chart.data;
3244				if (data.labels.length && data.datasets.length) {
3245					return data.labels.map(function(label, i) {
3246						var meta = chart.getDatasetMeta(0);
3247						var ds = data.datasets[0];
3248						var arc = meta.data[i];
3249						var custom = arc.custom || {};
3250						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
3251						var arcOpts = chart.options.elements.arc;
3252						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
3253						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
3254						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
3255
3256						return {
3257							text: label,
3258							fillStyle: fill,
3259							strokeStyle: stroke,
3260							lineWidth: bw,
3261							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
3262
3263							// Extra data used for toggling the correct item
3264							index: i
3265						};
3266					});
3267				}
3268				return [];
3269			}
3270		},
3271
3272		onClick: function(e, legendItem) {
3273			var index = legendItem.index;
3274			var chart = this.chart;
3275			var i, ilen, meta;
3276
3277			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
3278				meta = chart.getDatasetMeta(i);
3279				meta.data[index].hidden = !meta.data[index].hidden;
3280			}
3281
3282			chart.update();
3283		}
3284	},
3285
3286	// Need to override these to give a nice default
3287	tooltips: {
3288		callbacks: {
3289			title: function() {
3290				return '';
3291			},
3292			label: function(item, data) {
3293				return data.labels[item.index] + ': ' + item.yLabel;
3294			}
3295		}
3296	}
3297});
3298
3299module.exports = function(Chart) {
3300
3301	Chart.controllers.polarArea = Chart.DatasetController.extend({
3302
3303		dataElementType: elements.Arc,
3304
3305		linkScales: helpers.noop,
3306
3307		update: function(reset) {
3308			var me = this;
3309			var dataset = me.getDataset();
3310			var meta = me.getMeta();
3311			var start = me.chart.options.startAngle || 0;
3312			var starts = me._starts = [];
3313			var angles = me._angles = [];
3314			var i, ilen, angle;
3315
3316			me._updateRadius();
3317
3318			meta.count = me.countVisibleElements();
3319
3320			for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
3321				starts[i] = start;
3322				angle = me._computeAngle(i);
3323				angles[i] = angle;
3324				start += angle;
3325			}
3326
3327			helpers.each(meta.data, function(arc, index) {
3328				me.updateElement(arc, index, reset);
3329			});
3330		},
3331
3332		/**
3333		 * @private
3334		 */
3335		_updateRadius: function() {
3336			var me = this;
3337			var chart = me.chart;
3338			var chartArea = chart.chartArea;
3339			var opts = chart.options;
3340			var arcOpts = opts.elements.arc;
3341			var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
3342
3343			chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
3344			chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
3345			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
3346
3347			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
3348			me.innerRadius = me.outerRadius - chart.radiusLength;
3349		},
3350
3351		updateElement: function(arc, index, reset) {
3352			var me = this;
3353			var chart = me.chart;
3354			var dataset = me.getDataset();
3355			var opts = chart.options;
3356			var animationOpts = opts.animation;
3357			var scale = chart.scale;
3358			var labels = chart.data.labels;
3359
3360			var centerX = scale.xCenter;
3361			var centerY = scale.yCenter;
3362
3363			// var negHalfPI = -0.5 * Math.PI;
3364			var datasetStartAngle = opts.startAngle;
3365			var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3366			var startAngle = me._starts[index];
3367			var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
3368
3369			var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3370
3371			helpers.extend(arc, {
3372				// Utility
3373				_datasetIndex: me.index,
3374				_index: index,
3375				_scale: scale,
3376
3377				// Desired view properties
3378				_model: {
3379					x: centerX,
3380					y: centerY,
3381					innerRadius: 0,
3382					outerRadius: reset ? resetRadius : distance,
3383					startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
3384					endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
3385					label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
3386				}
3387			});
3388
3389			// Apply border and fill style
3390			var elementOpts = this.chart.options.elements.arc;
3391			var custom = arc.custom || {};
3392			var valueOrDefault = helpers.valueAtIndexOrDefault;
3393			var model = arc._model;
3394
3395			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
3396			model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
3397			model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
3398
3399			arc.pivot();
3400		},
3401
3402		countVisibleElements: function() {
3403			var dataset = this.getDataset();
3404			var meta = this.getMeta();
3405			var count = 0;
3406
3407			helpers.each(meta.data, function(element, index) {
3408				if (!isNaN(dataset.data[index]) && !element.hidden) {
3409					count++;
3410				}
3411			});
3412
3413			return count;
3414		},
3415
3416		/**
3417		 * @private
3418		 */
3419		_computeAngle: function(index) {
3420			var me = this;
3421			var count = this.getMeta().count;
3422			var dataset = me.getDataset();
3423			var meta = me.getMeta();
3424
3425			if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
3426				return 0;
3427			}
3428
3429			// Scriptable options
3430			var context = {
3431				chart: me.chart,
3432				dataIndex: index,
3433				dataset: dataset,
3434				datasetIndex: me.index
3435			};
3436
3437			return helpers.options.resolve([
3438				me.chart.options.elements.arc.angle,
3439				(2 * Math.PI) / count
3440			], context, index);
3441		}
3442	});
3443};
3444
3445},{"26":26,"41":41,"46":46}],20:[function(require,module,exports){
3446'use strict';
3447
3448var defaults = require(26);
3449var elements = require(41);
3450var helpers = require(46);
3451
3452defaults._set('radar', {
3453	scale: {
3454		type: 'radialLinear'
3455	},
3456	elements: {
3457		line: {
3458			tension: 0 // no bezier in radar
3459		}
3460	}
3461});
3462
3463module.exports = function(Chart) {
3464
3465	Chart.controllers.radar = Chart.DatasetController.extend({
3466
3467		datasetElementType: elements.Line,
3468
3469		dataElementType: elements.Point,
3470
3471		linkScales: helpers.noop,
3472
3473		update: function(reset) {
3474			var me = this;
3475			var meta = me.getMeta();
3476			var line = meta.dataset;
3477			var points = meta.data;
3478			var custom = line.custom || {};
3479			var dataset = me.getDataset();
3480			var lineElementOptions = me.chart.options.elements.line;
3481			var scale = me.chart.scale;
3482
3483			// Compatibility: If the properties are defined with only the old name, use those values
3484			if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
3485				dataset.lineTension = dataset.tension;
3486			}
3487
3488			helpers.extend(meta.dataset, {
3489				// Utility
3490				_datasetIndex: me.index,
3491				_scale: scale,
3492				// Data
3493				_children: points,
3494				_loop: true,
3495				// Model
3496				_model: {
3497					// Appearance
3498					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
3499					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
3500					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
3501					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
3502					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
3503					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
3504					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
3505					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
3506					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
3507				}
3508			});
3509
3510			meta.dataset.pivot();
3511
3512			// Update Points
3513			helpers.each(points, function(point, index) {
3514				me.updateElement(point, index, reset);
3515			}, me);
3516
3517			// Update bezier control points
3518			me.updateBezierControlPoints();
3519		},
3520		updateElement: function(point, index, reset) {
3521			var me = this;
3522			var custom = point.custom || {};
3523			var dataset = me.getDataset();
3524			var scale = me.chart.scale;
3525			var pointElementOptions = me.chart.options.elements.point;
3526			var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
3527
3528			// Compatibility: If the properties are defined with only the old name, use those values
3529			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3530				dataset.pointRadius = dataset.radius;
3531			}
3532			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3533				dataset.pointHitRadius = dataset.hitRadius;
3534			}
3535
3536			helpers.extend(point, {
3537				// Utility
3538				_datasetIndex: me.index,
3539				_index: index,
3540				_scale: scale,
3541
3542				// Desired view properties
3543				_model: {
3544					x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
3545					y: reset ? scale.yCenter : pointPosition.y,
3546
3547					// Appearance
3548					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
3549					radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
3550					backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
3550dColor, index, pointElementOptions.backgroundColor),
3551					borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
3552					borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
3553					pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
3554					rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation),
3555
3556					// Tooltip
3557					hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
3558				}
3559			});
3560
3561			point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
3562		},
3563		updateBezierControlPoints: function() {
3564			var chartArea = this.chart.chartArea;
3565			var meta = this.getMeta();
3566
3567			helpers.each(meta.data, function(point, index) {
3568				var model = point._model;
3569				var controlPoints = helpers.splineCurve(
3570					helpers.previousItem(meta.data, index, true)._model,
3571					model,
3572					helpers.nextItem(meta.data, index, true)._model,
3573					model.tension
3574				);
3575
3576				// Prevent the bezier going outside of the bounds of the graph
3577				model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
3578				model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
3579
3580				model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
3581				model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
3582
3583				// Now pivot the point for animation
3584				point.pivot();
3585			});
3586		},
3587
3588		setHoverStyle: function(point) {
3589			// Point
3590			var dataset = this.chart.data.datasets[point._datasetIndex];
3591			var custom = point.custom || {};
3592			var index = point._index;
3593			var model = point._model;
3594
3595			point.$previousStyle = {
3596				backgroundColor: model.backgroundColor,
3597				borderColor: model.borderColor,
3598				borderWidth: model.borderWidth,
3599				radius: model.radius
3600			};
3601
3602			model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3603			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3604			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3605			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3606		},
3607	});
3608};
3609
3610},{"26":26,"41":41,"46":46}],21:[function(require,module,exports){
3611'use strict';
3612
3613var defaults = require(26);
3614
3615defaults._set('scatter', {
3616	hover: {
3617		mode: 'single'
3618	},
3619
3620	scales: {
3621		xAxes: [{
3622			id: 'x-axis-1',    // need an ID so datasets can reference the scale
3623			type: 'linear',    // scatter should not use a category axis
3624			position: 'bottom'
3625		}],
3626		yAxes: [{
3627			id: 'y-axis-1',
3628			type: 'linear',
3629			position: 'left'
3630		}]
3631	},
3632
3633	showLines: false,
3634
3635	tooltips: {
3636		callbacks: {
3637			title: function() {
3638				return '';     // doesn't make sense for scatter since data are formatted as a point
3639			},
3640			label: function(item) {
3641				return '(' + item.xLabel + ', ' + item.yLabel + ')';
3642			}
3643		}
3644	}
3645});
3646
3647module.exports = function(Chart) {
3648
3649	// Scatter charts use line controllers
3650	Chart.controllers.scatter = Chart.controllers.line;
3651
3652};
3653
3654},{"26":26}],22:[function(require,module,exports){
3655'use strict';
3656
3657var Element = require(27);
3658
3659var exports = module.exports = Element.extend({
3660	chart: null, // the animation associated chart instance
3661	currentStep: 0, // the current animation step
3662	numSteps: 60, // default number of steps
3663	easing: '', // the easing to use for this animation
3664	render: null, // render function used by the animation service
3665
3666	onAnimationProgress: null, // user specified callback to fire on each step of the animation
3667	onAnimationComplete: null, // user specified callback to fire when the animation finishes
3668});
3669
3670// DEPRECATIONS
3671
3672/**
3673 * Provided for backward compatibility, use Chart.Animation instead
3674 * @prop Chart.Animation#animationObject
3675 * @deprecated since version 2.6.0
3676 * @todo remove at version 3
3677 */
3678Object.defineProperty(exports.prototype, 'animationObject', {
3679	get: function() {
3680		return this;
3681	}
3682});
3683
3684/**
3685 * Provided for backward compatibility, use Chart.Animation#chart instead
3686 * @prop Chart.Animation#chartInstance
3687 * @deprecated since version 2.6.0
3688 * @todo remove at version 3
3689 */
3690Object.defineProperty(exports.prototype, 'chartInstance', {
3691	get: function() {
3692		return this.chart;
3693	},
3694	set: function(value) {
3695		this.chart = value;
3696	}
3697});
3698
3699},{"27":27}],23:[function(require,module,exports){
3700/* global window: false */
3701'use strict';
3702
3703var defaults = require(26);
3704var helpers = require(46);
3705
3706defaults._set('global', {
3707	animation: {
3708		duration: 1000,
3709		easing: 'easeOutQuart',
3710		onProgress: helpers.noop,
3711		onComplete: helpers.noop
3712	}
3713});
3714
3715module.exports = {
3716	frameDuration: 17,
3717	animations: [],
3718	dropFrames: 0,
3719	request: null,
3720
3721	/**
3722	 * @param {Chart} chart - The chart to animate.
3723	 * @param {Chart.Animation} animation - The animation that we will animate.
3724	 * @param {Number} duration - The animation duration in ms.
3725	 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
3726	 */
3727	addAnimation: function(chart, animation, duration, lazy) {
3728		var animations = this.animations;
3729		var i, ilen;
3730
3731		animation.chart = chart;
3732
3733		if (!lazy) {
3734			chart.animating = true;
3735		}
3736
3737		for (i = 0, ilen = animations.length; i < ilen; ++i) {
3738			if (animations[i].chart === chart) {
3739				animations[i] = animation;
3740				return;
3741			}
3742		}
3743
3744		animations.push(animation);
3745
3746		// If there are no animations queued, manually kickstart a digest, for lack of a better word
3747		if (animations.length === 1) {
3748			this.requestAnimationFrame();
3749		}
3750	},
3751
3752	cancelAnimation: function(chart) {
3753		var index = helpers.findIndex(this.animations, function(animation) {
3754			return animation.chart === chart;
3755		});
3756
3757		if (index !== -1) {
3758			this.animations.splice(index, 1);
3759			chart.animating = false;
3760		}
3761	},
3762
3763	requestAnimationFrame: function() {
3764		var me = this;
3765		if (me.request === null) {
3766			// Skip animation frame requests until the active one is executed.
3767			// This can happen when processing mouse events, e.g. 'mousemove'
3768			// and 'mouseout' events will trigger multiple renders.
3769			me.request = helpers.requestAnimFrame.call(window, function() {
3770				me.request = null;
3771				me.startDigest();
3772			});
3773		}
3774	},
3775
3776	/**
3777	 * @private
3778	 */
3779	startDigest: function() {
3780		var me = this;
3781		var startTime = Date.now();
3782		var framesToDrop = 0;
3783
3784		if (me.dropFrames > 1) {
3785			framesToDrop = Math.floor(me.dropFrames);
3786			me.dropFrames = me.dropFrames % 1;
3787		}
3788
3789		me.advance(1 + framesToDrop);
3790
3791		var endTime = Date.now();
3792
3793		me.dropFrames += (endTime - startTime) / me.frameDuration;
3794
3795		// Do we have more stuff to animate?
3796		if (me.animations.length > 0) {
3797			me.requestAnimationFrame();
3798		}
3799	},
3800
3801	/**
3802	 * @private
3803	 */
3804	advance: function(count) {
3805		var animations = this.animations;
3806		var animation, chart;
3807		var i = 0;
3808
3809		while (i < animations.length) {
3810			animation = animations[i];
3811			chart = animation.chart;
3812
3813			animation.currentStep = (animation.currentStep || 0) + count;
3814			animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
3815
3816			helpers.callback(animation.render, [chart, animation], chart);
3817			helpers.callback(animation.onAnimationProgress, [animation], chart);
3818
3819			if (animation.currentStep >= animation.numSteps) {
3820				helpers.callback(animation.onAnimationComplete, [animation], chart);
3821				chart.animating = false;
3822				animations.splice(i, 1);
3823			} else {
3824				++i;
3825			}
3826		}
3827	}
3828};
3829
3830},{"26":26,"46":46}],24:[function(require,module,exports){
3831'use strict';
3832
3833var Animation = require(22);
3834var animations = require(23);
3835var defaults = require(26);
3836var helpers = require(46);
3837var Interaction = require(29);
3838var layouts = require(31);
3839var platform = require(49);
3840var plugins = require(32);
3841var scaleService = require(34);
3842var Tooltip = require(36);
3843
3844module.exports = function(Chart) {
3845
3846	// Create a dictionary of chart types, to allow for extension of existing types
3847	Chart.types = {};
3848
3849	// Store a reference to each instance - allowing us to globally resize chart instances on window resize.
3850	// Destroy method on the chart will remove the instance of the chart from this reference.
3851	Chart.instances = {};
3852
3853	// Controllers available for dataset visualization eg. bar, line, slice, etc.
3854	Chart.controllers = {};
3855
3856	/**
3857	 * Initializes the given config with global and chart default values.
3858	 */
3859	function initConfig(config) {
3860		config = config || {};
3861
3862		// Do NOT use configMerge() for the data object because this method merges arrays
vendor: 12,884 bytes, lines 3863-4327
3863		// and so would change references to labels and datasets, preventing data updates.
3864		var data = config.data = config.data || {};
3865		data.datasets = data.datasets || [];
3866		data.labels = data.labels || [];
3867
3868		config.options = helpers.configMerge(
3869			defaults.global,
3870			defaults[config.type],
3871			config.options || {});
3872
3873		return config;
3874	}
3875
3876	/**
3877	 * Updates the config of the chart
3878	 * @param chart {Chart} chart to update the options for
3879	 */
3880	function updateConfig(chart) {
3881		var newOptions = chart.options;
3882
3883		helpers.each(chart.scales, function(scale) {
3884			layouts.removeBox(chart, scale);
3885		});
3886
3887		newOptions = helpers.configMerge(
3888			Chart.defaults.global,
3889			Chart.defaults[chart.config.type],
3890			newOptions);
3891
3892		chart.options = chart.config.options = newOptions;
3893		chart.ensureScalesHaveIDs();
3894		chart.buildOrUpdateScales();
3895		// Tooltip
3896		chart.tooltip._options = newOptions.tooltips;
3897		chart.tooltip.initialize();
3898	}
3899
3900	function positionIsHorizontal(position) {
3901		return position === 'top' || position === 'bottom';
3902	}
3903
3904	helpers.extend(Chart.prototype, /** @lends Chart */ {
3905		/**
3906		 * @private
3907		 */
3908		construct: function(item, config) {
3909			var me = this;
3910
3911			config = initConfig(config);
3912
3913			var context = platform.acquireContext(item, config);
3914			var canvas = context && context.canvas;
3915			var height = canvas && canvas.height;
3916			var width = canvas && canvas.width;
3917
3918			me.id = helpers.uid();
3919			me.ctx = context;
3920			me.canvas = canvas;
3921			me.config = config;
3922			me.width = width;
3923			me.height = height;
3924			me.aspectRatio = height ? width / height : null;
3925			me.options = config.options;
3926			me._bufferedRender = false;
3927
3928			/**
3929			 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
3930			 * the "instance" still need to be defined since it might be called from plugins.
3931			 * @prop Chart#chart
3932			 * @deprecated since version 2.6.0
3933			 * @todo remove at version 3
3934			 * @private
3935			 */
3936			me.chart = me;
3937			me.controller = me; // chart.chart.controller #inception
3938
3939			// Add the chart instance to the global namespace
3940			Chart.instances[me.id] = me;
3941
3942			// Define alias to the config data: `chart.data === chart.config.data`
3943			Object.defineProperty(me, 'data', {
3944				get: function() {
3945					return me.config.data;
3946				},
3947				set: function(value) {
3948					me.config.data = value;
3949				}
3950			});
3951
3952			if (!context || !canvas) {
3953				// The given item is not a compatible context2d element, let's return before finalizing
3954				// the chart initialization but after setting basic chart / controller properties that
3955				// can help to figure out that the chart is not valid (e.g chart.canvas !== null);
3956				// https://github.com/chartjs/Chart.js/issues/2807
3957				console.error("Failed to create chart: can't acquire context from the given item");
3958				return;
3959			}
3960
3961			me.initialize();
3962			me.update();
3963		},
3964
3965		/**
3966		 * @private
3967		 */
3968		initialize: function() {
3969			var me = this;
3970
3971			// Before init plugin notification
3972			plugins.notify(me, 'beforeInit');
3973
3974			helpers.retinaScale(me, me.options.devicePixelRatio);
3975
3976			me.bindEvents();
3977
3978			if (me.options.responsive) {
3979				// Initial resize before chart draws (must be silent to preserve initial animations).
3980				me.resize(true);
3981			}
3982
3983			// Make sure scales have IDs and are built before we build any controllers.
3984			me.ensureScalesHaveIDs();
3985			me.buildOrUpdateScales();
3986			me.initToolTip();
3987
3988			// After init plugin notification
3989			plugins.notify(me, 'afterInit');
3990
3991			return me;
3992		},
3993
3994		clear: function() {
3995			helpers.canvas.clear(this);
3996			return this;
3997		},
3998
3999		stop: function() {
4000			// Stops any current animation loop occurring
4001			animations.cancelAnimation(this);
4002			return this;
4003		},
4004
4005		resize: function(silent) {
4006			var me = this;
4007			var options = me.options;
4008			var canvas = me.canvas;
4009			var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
4010
4011			// the canvas render width and height will be casted to integers so make sure that
4012			// the canvas display style uses the same integer values to avoid blurring effect.
4013
4014			// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
4015			var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
4016			var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
4017
4018			if (me.width === newWidth && me.height === newHeight) {
4019				return;
4020			}
4021
4022			canvas.width = me.width = newWidth;
4023			canvas.height = me.height = newHeight;
4024			canvas.style.width = newWidth + 'px';
4025			canvas.style.height = newHeight + 'px';
4026
4027			helpers.retinaScale(me, options.devicePixelRatio);
4028
4029			if (!silent) {
4030				// Notify any plugins about the resize
4031				var newSize = {width: newWidth, height: newHeight};
4032				plugins.notify(me, 'resize', [newSize]);
4033
4034				// Notify of resize
4035				if (me.options.onResize) {
4036					me.options.onResize(me, newSize);
4037				}
4038
4039				me.stop();
4040				me.update({
4041					duration: me.options.responsiveAnimationDuration
4042				});
4043			}
4044		},
4045
4046		ensureScalesHaveIDs: function() {
4047			var options = this.options;
4048			var scalesOptions = options.scales || {};
4049			var scaleOptions = options.scale;
4050
4051			helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
4052				xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
4053			});
4054
4055			helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
4056				yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
4057			});
4058
4059			if (scaleOptions) {
4060				scaleOptions.id = scaleOptions.id || 'scale';
4061			}
4062		},
4063
4064		/**
4065		 * Builds a map of scale ID to scale object for future lookup.
4066		 */
4067		buildOrUpdateScales: function() {
4068			var me = this;
4069			var options = me.options;
4070			var scales = me.scales || {};
4071			var items = [];
4072			var updated = Object.keys(scales).reduce(function(obj, id) {
4073				obj[id] = false;
4074				return obj;
4075			}, {});
4076
4077			if (options.scales) {
4078				items = items.concat(
4079					(options.scales.xAxes || []).map(function(xAxisOptions) {
4080						return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
4081					}),
4082					(options.scales.yAxes || []).map(function(yAxisOptions) {
4083						return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
4084					})
4085				);
4086			}
4087
4088			if (options.scale) {
4089				items.push({
4090					options: options.scale,
4091					dtype: 'radialLinear',
4092					isDefault: true,
4093					dposition: 'chartArea'
4094				});
4095			}
4096
4097			helpers.each(items, function(item) {
4098				var scaleOptions = item.options;
4099				var id = scaleOptions.id;
4100				var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);
4101
4102				if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
4103					scaleOptions.position = item.dposition;
4104				}
4105
4106				updated[id] = true;
4107				var scale = null;
4108				if (id in scales && scales[id].type === scaleType) {
4109					scale = scales[id];
4110					scale.options = scaleOptions;
4111					scale.ctx = me.ctx;
4112					scale.chart = me;
4113				} else {
4114					var scaleClass = scaleService.getScaleConstructor(scaleType);
4115					if (!scaleClass) {
4116						return;
4117					}
4118					scale = new scaleClass({
4119						id: id,
4120						type: scaleType,
4121						options: scaleOptions,
4122						ctx: me.ctx,
4123						chart: me
4124					});
4125					scales[scale.id] = scale;
4126				}
4127
4128				scale.mergeTicksOptions();
4129
4130				// TODO(SB): I think we should be able to remove this custom case (options.scale)
4131				// and consider it as a regular scale part of the "scales"" map only! This would
4132				// make the logic easier and remove some useless? custom code.
4133				if (item.isDefault) {
4134					me.scale = scale;
4135				}
4136			});
4137			// clear up discarded scales
4138			helpers.each(updated, function(hasUpdated, id) {
4139				if (!hasUpdated) {
4140					delete scales[id];
4141				}
4142			});
4143
4144			me.scales = scales;
4145
4146			scaleService.addScalesToLayout(this);
4147		},
4148
4149		buildOrUpdateControllers: function() {
4150			var me = this;
4151			var types = [];
4152			var newControllers = [];
4153
4154			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4155				var meta = me.getDatasetMeta(datasetIndex);
4156				var type = dataset.type || me.config.type;
4157
4158				if (meta.type && meta.type !== type) {
4159					me.destroyDatasetMeta(datasetIndex);
4160					meta = me.getDatasetMeta(datasetIndex);
4161				}
4162				meta.type = type;
4163
4164				types.push(meta.type);
4165
4166				if (meta.controller) {
4167					meta.controller.updateIndex(datasetIndex);
4168					meta.controller.linkScales();
4169				} else {
4170					var ControllerClass = Chart.controllers[meta.type];
4171					if (ControllerClass === undefined) {
4172						throw new Error('"' + meta.type + '" is not a chart type.');
4173					}
4174
4175					meta.controller = new ControllerClass(me, datasetIndex);
4176					newControllers.push(meta.controller);
4177				}
4178			}, me);
4179
4180			return newControllers;
4181		},
4182
4183		/**
4184		 * Reset the elements of all datasets
4185		 * @private
4186		 */
4187		resetElements: function() {
4188			var me = this;
4189			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4190				me.getDatasetMeta(datasetIndex).controller.reset();
4191			}, me);
4192		},
4193
4194		/**
4195		* Resets the chart back to it's state before the initial animation
4196		*/
4197		reset: function() {
4198			this.resetElements();
4199			this.tooltip.initialize();
4200		},
4201
4202		update: function(config) {
4203			var me = this;
4204
4205			if (!config || typeof config !== 'object') {
4206				// backwards compatibility
4207				config = {
4208					duration: config,
4209					lazy: arguments[1]
4210				};
4211			}
4212
4213			updateConfig(me);
4214
4215			// plugins options references might have change, let's invalidate the cache
4216			// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
4217			plugins._invalidate(me);
4218
4219			if (plugins.notify(me, 'beforeUpdate') === false) {
4220				return;
4221			}
4222
4223			// In case the entire data object changed
4224			me.tooltip._data = me.data;
4225
4226			// Make sure dataset controllers are updated and new controllers are reset
4227			var newControllers = me.buildOrUpdateControllers();
4228
4229			// Make sure all dataset controllers have correct meta data counts
4230			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4231				me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
4232			}, me);
4233
4234			me.updateLayout();
4235
4236			// Can only reset the new controllers after the scales have been updated
4237			if (me.options.animation && me.options.animation.duration) {
4238				helpers.each(newControllers, function(controller) {
4239					controller.reset();
4240				});
4241			}
4242
4243			me.updateDatasets();
4244
4245			// Need to reset tooltip in case it is displayed with elements that are removed
4246			// after update.
4247			me.tooltip.initialize();
4248
4249			// Last active contains items that were previously in the tooltip.
4250			// When we reset the tooltip, we need to clear it
4251			me.lastActive = [];
4252
4253			// Do this before render so that any plugins that need final scale updates can use it
4254			plugins.notify(me, 'afterUpdate');
4255
4256			if (me._bufferedRender) {
4257				me._bufferedRequest = {
4258					duration: config.duration,
4259					easing: config.easing,
4260					lazy: config.lazy
4261				};
4262			} else {
4263				me.render(config);
4264			}
4265		},
4266
4267		/**
4268		 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
4269		 * hook, in which case, plugins will not be called on `afterLayout`.
4270		 * @private
4271		 */
4272		updateLayout: function() {
4273			var me = this;
4274
4275			if (plugins.notify(me, 'beforeLayout') === false) {
4276				return;
4277			}
4278
4279			layouts.update(this, this.width, this.height);
4280
4281			/**
4282			 * Provided for backward compatibility, use `afterLayout` instead.
4283			 * @method IPlugin#afterScaleUpdate
4284			 * @deprecated since version 2.5.0
4285			 * @todo remove at version 3
4286			 * @private
4287			 */
4288			plugins.notify(me, 'afterScaleUpdate');
4289			plugins.notify(me, 'afterLayout');
4290		},
4291
4292		/**
4293		 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
4294		 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
4295		 * @private
4296		 */
4297		updateDatasets: function() {
4298			var me = this;
4299
4300			if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
4301				return;
4302			}
4303
4304			for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4305				me.updateDataset(i);
4306			}
4307
4308			plugins.notify(me, 'afterDatasetsUpdate');
4309		},
4310
4311		/**
4312		 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
4313		 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
4314		 * @private
4315		 */
4316		updateDataset: function(index) {
4317			var me = this;
4318			var meta = me.getDatasetMeta(index);
4319			var args = {
4320				meta: meta,
4321				index: index
4322			};
4323
4324			if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
4325				return;
4326			}
4327
4328			meta.controller.update();
4329
4330			plugins.notify(me, 'afterDatasetUpdate', [args]);
4331		},
4332
4333		render: function(config) {
4334			var me = this;
4335
4336			if (!config || typeof config !== 'object') {
4337				// backwards compatibility
4338				config = {
4339					duration: config,
4340					lazy: arguments[1]
4341				};
4342			}
4343
4344			var duration = config.duration;
4345			var lazy = config.lazy;
4346
4347			if (plugins.notify(me, 'beforeRender') === false) {
4348				return;
4349			}
4350
4351			var animationOptions = me.options.animation;
4352			var onComplete = function(animation) {
4353				plugins.notify(me, 'afterRender');
4354				helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
4355			};
4356
4357			if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
4358				var animation = new Animation({
4359					numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
4360					easing: config.easing || animationOptions.easing,
4361
4362					render: function(chart, animationObject) {
4363						var easingFunction = helpers.easing.effects[animationObject.easing];
4364						var currentStep = animationObject.currentStep;
4365						var stepDecimal = currentStep / animationObject.numSteps;
4366
4367						chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
4368					},
4369
4370					onAnimationProgress: animationOptions.onProgress,
4371					onAnimationComplete: onComplete
4372				});
4373
4374				animations.addAnimation(me, animation, duration, lazy);
4375			} else {
4376				me.draw();
4377
4378				// See https://github.com/chartjs/Chart.js/issues/3781
4379				onComplete(new Animation({numSteps: 0, chart: me}));
4380			}
4381
4382			return me;
4383		},
4384
4385		draw: function(easingValue) {
4386			var me = this;
4387
4388			me.clear();
4389
4390			if (helpers.isNullOrUndef(easingValue)) {
4391				easingValue = 1;
4392			}
4393
4394			me.transition(easingValue);
4395
4396			if (me.width <= 0 || me.height <= 0) {
4397				return;
4398			}
4399
4400			if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
4401				return;
4402			}
4403
4404			// Draw all the scales
4405			helpers.each(me.boxes, function(box) {
4406				box.draw(me.chartArea);
4407			}, me);
4408
4409			if (me.scale) {
4410				me.scale.draw();
4411			}
4412
4413			me.drawDatasets(easingValue);
4414			me._drawTooltip(easingValue);
4415
4416			plugins.notify(me, 'afterDraw', [easingValue]);
4417		},
4418
4419		/**
4420		 * @private
4421		 */
4422		transition: function(easingValue) {
4423			var me = this;
4424
4425			for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
4426				if (me.isDatasetVisible(i)) {
4427					me.getDatasetMeta(i).controller.transition(easingValue);
4428				}
4429			}
4430
4431			me.tooltip.transition(easingValue);
4432		},
4433
4434		/**
4435		 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
4436		 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
4437		 * @private
4438		 */
4439		drawDatasets: function(easingValue) {
4440			var me = this;
4441
4442			if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
4443				return;
4444			}
4445
4446			// Draw datasets reversed to support proper line stacking
4447			for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
4448				if (me.isDatasetVisible(i)) {
4449					me.drawDataset(i, easingValue);
4450				}
4451			}
4452
4453			plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
4454		},
4455
4456		/**
4457		 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
4458		 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
4459		 * @private
4460		 */
4461		drawDataset: function(index, easingValue) {
4462			var me = this;
4463			var meta = me.getDatasetMeta(index);
4464			var args = {
4465				meta: meta,
4466				index: index,
4467				easingValue: easingValue
4468			};
4469
4470			if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
4471				return;
4472			}
4473
4474			meta.controller.draw(easingValue);
4475
4476			plugins.notify(me, 'afterDatasetDraw', [args]);
4477		},
4478
4479		/**
4480		 * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
4481		 * hook, in which case, plugins will not be called on `afterTooltipDraw`.
4482		 * @private
4483		 */
4484		_drawTooltip: function(easingValue) {
4485			var me = this;
4486			var tooltip = me.tooltip;
4487			var args = {
4488				tooltip: tooltip,
4489				easingValue: easingValue
4490			};
4491
4492			if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
4493				return;
4494			}
4495
4496			tooltip.draw();
4497
4498			plugins.notify(me, 'afterTooltipDraw', [args]);
4499		},
4500
4501		// Get the single element that was clicked on
vendor: 46,092 bytes, lines 4502-6087
4502		// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
4503		getElementAtEvent: function(e) {
4504			return Interaction.modes.single(this, e);
4505		},
4506
4507		getElementsAtEvent: function(e) {
4508			return Interaction.modes.label(this, e, {intersect: true});
4509		},
4510
4511		getElementsAtXAxis: function(e) {
4512			return Interaction.modes['x-axis'](this, e, {intersect: true});
4513		},
4514
4515		getElementsAtEventForMode: function(e, mode, options) {
4516			var method = Interaction.modes[mode];
4517			if (typeof method === 'function') {
4518				return method(this, e, options);
4519			}
4520
4521			return [];
4522		},
4523
4524		getDatasetAtEvent: function(e) {
4525			return Interaction.modes.dataset(this, e, {intersect: true});
4526		},
4527
4528		getDatasetMeta: function(datasetIndex) {
4529			var me = this;
4530			var dataset = me.data.datasets[datasetIndex];
4531			if (!dataset._meta) {
4532				dataset._meta = {};
4533			}
4534
4535			var meta = dataset._meta[me.id];
4536			if (!meta) {
4537				meta = dataset._meta[me.id] = {
4538					type: null,
4539					data: [],
4540					dataset: null,
4541					controller: null,
4542					hidden: null,			// See isDatasetVisible() comment
4543					xAxisID: null,
4544					yAxisID: null
4545				};
4546			}
4547
4548			return meta;
4549		},
4550
4551		getVisibleDatasetCount: function() {
4552			var count = 0;
4553			for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
4554				if (this.isDatasetVisible(i)) {
4555					count++;
4556				}
4557			}
4558			return count;
4559		},
4560
4561		isDatasetVisible: function(datasetIndex) {
4562			var meta = this.getDatasetMeta(datasetIndex);
4563
4564			// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
4565			// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
4566			return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
4567		},
4568
4569		generateLegend: function() {
4570			return this.options.legendCallback(this);
4571		},
4572
4573		/**
4574		 * @private
4575		 */
4576		destroyDatasetMeta: function(datasetIndex) {
4577			var id = this.id;
4578			var dataset = this.data.datasets[datasetIndex];
4579			var meta = dataset._meta && dataset._meta[id];
4580
4581			if (meta) {
4582				meta.controller.destroy();
4583				delete dataset._meta[id];
4584			}
4585		},
4586
4587		destroy: function() {
4588			var me = this;
4589			var canvas = me.canvas;
4590			var i, ilen;
4591
4592			me.stop();
4593
4594			// dataset controllers need to cleanup associated data
4595			for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4596				me.destroyDatasetMeta(i);
4597			}
4598
4599			if (canvas) {
4600				me.unbindEvents();
4601				helpers.canvas.clear(me);
4602				platform.releaseContext(me.ctx);
4603				me.canvas = null;
4604				me.ctx = null;
4605			}
4606
4607			plugins.notify(me, 'destroy');
4608
4609			delete Chart.instances[me.id];
4610		},
4611
4612		toBase64Image: function() {
4613			return this.canvas.toDataURL.apply(this.canvas, arguments);
4614		},
4615
4616		initToolTip: function() {
4617			var me = this;
4618			me.tooltip = new Tooltip({
4619				_chart: me,
4620				_chartInstance: me, // deprecated, backward compatibility
4621				_data: me.data,
4622				_options: me.options.tooltips
4623			}, me);
4624		},
4625
4626		/**
4627		 * @private
4628		 */
4629		bindEvents: function() {
4630			var me = this;
4631			var listeners = me._listeners = {};
4632			var listener = function() {
4633				me.eventHandler.apply(me, arguments);
4634			};
4635
4636			helpers.each(me.options.events, function(type) {
4637				platform.addEventListener(me, type, listener);
4638				listeners[type] = listener;
4639			});
4640
4641			// Elements used to detect size change should not be injected for non responsive charts.
4642			// See https://github.com/chartjs/Chart.js/issues/2210
4643			if (me.options.responsive) {
4644				listener = function() {
4645					me.resize();
4646				};
4647
4648				platform.addEventListener(me, 'resize', listener);
4649				listeners.resize = listener;
4650			}
4651		},
4652
4653		/**
4654		 * @private
4655		 */
4656		unbindEvents: function() {
4657			var me = this;
4658			var listeners = me._listeners;
4659			if (!listeners) {
4660				return;
4661			}
4662
4663			delete me._listeners;
4664			helpers.each(listeners, function(listener, type) {
4665				platform.removeEventListener(me, type, listener);
4666			});
4667		},
4668
4669		updateHoverStyle: function(elements, mode, enabled) {
4670			var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
4671			var element, i, ilen;
4672
4673			for (i = 0, ilen = elements.length; i < ilen; ++i) {
4674				element = elements[i];
4675				if (element) {
4676					this.getDatasetMeta(element._datasetIndex).controller[method](element);
4677				}
4678			}
4679		},
4680
4681		/**
4682		 * @private
4683		 */
4684		eventHandler: function(e) {
4685			var me = this;
4686			var tooltip = me.tooltip;
4687
4688			if (plugins.notify(me, 'beforeEvent', [e]) === false) {
4689				return;
4690			}
4691
4692			// Buffer any update calls so that renders do not occur
4693			me._bufferedRender = true;
4694			me._bufferedRequest = null;
4695
4696			var changed = me.handleEvent(e);
4697			// for smooth tooltip animations issue #4989
4698			// the tooltip should be the source of change
4699			// Animation check workaround:
4700			// tooltip._start will be null when tooltip isn't animating
4701			if (tooltip) {
4702				changed = tooltip._start
4703					? tooltip.handleEvent(e)
4704					: changed | tooltip.handleEvent(e);
4705			}
4706
4707			plugins.notify(me, 'afterEvent', [e]);
4708
4709			var bufferedRequest = me._bufferedRequest;
4710			if (bufferedRequest) {
4711				// If we have an update that was triggered, we need to do a normal render
4712				me.render(bufferedRequest);
4713			} else if (changed && !me.animating) {
4714				// If entering, leaving, or changing elements, animate the change via pivot
4715				me.stop();
4716
4717				// We only need to render at this point. Updating will cause scales to be
4718				// recomputed generating flicker & using more memory than necessary.
4719				me.render({
4720					duration: me.options.hover.animationDuration,
4721					lazy: true
4722				});
4723			}
4724
4725			me._bufferedRender = false;
4726			me._bufferedRequest = null;
4727
4728			return me;
4729		},
4730
4731		/**
4732		 * Handle an event
4733		 * @private
4734		 * @param {IEvent} event the event to handle
4735		 * @return {Boolean} true if the chart needs to re-render
4736		 */
4737		handleEvent: function(e) {
4738			var me = this;
4739			var options = me.options || {};
4740			var hoverOptions = options.hover;
4741			var changed = false;
4742
4743			me.lastActive = me.lastActive || [];
4744
4745			// Find Active Elements for hover and tooltips
4746			if (e.type === 'mouseout') {
4747				me.active = [];
4748			} else {
4749				me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
4750			}
4751
4752			// Invoke onHover hook
4753			// Need to call with native event here to not break backwards compatibility
4754			helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
4755
4756			if (e.type === 'mouseup' || e.type === 'click') {
4757				if (options.onClick) {
4758					// Use e.native here for backwards compatibility
4759					options.onClick.call(me, e.native, me.active);
4760				}
4761			}
4762
4763			// Remove styling for last active (even if it may still be active)
4764			if (me.lastActive.length) {
4765				me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
4766			}
4767
4768			// Built in hover styling
4769			if (me.active.length && hoverOptions.mode) {
4770				me.updateHoverStyle(me.active, hoverOptions.mode, true);
4771			}
4772
4773			changed = !helpers.arrayEquals(me.active, me.lastActive);
4774
4775			// Remember Last Actives
4776			me.lastActive = me.active;
4777
4778			return changed;
4779		}
4780	});
4781
4782	/**
4783	 * Provided for backward compatibility, use Chart instead.
4784	 * @class Chart.Controller
4785	 * @deprecated since version 2.6.0
4786	 * @todo remove at version 3
4787	 * @private
4788	 */
4789	Chart.Controller = Chart;
4790};
4791
4792},{"22":22,"23":23,"26":26,"29":29,"31":31,"32":32,"34":34,"36":36,"46":46,"49":49}],25:[function(require,module,exports){
4793'use strict';
4794
4795var helpers = require(46);
4796
4797module.exports = function(Chart) {
4798
4799	var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
4800
4801	/**
4802	 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
4803	 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
4804	 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
4805	 */
4806	function listenArrayEvents(array, listener) {
4807		if (array._chartjs) {
4808			array._chartjs.listeners.push(listener);
4809			return;
4810		}
4811
4812		Object.defineProperty(array, '_chartjs', {
4813			configurable: true,
4814			enumerable: false,
4815			value: {
4816				listeners: [listener]
4817			}
4818		});
4819
4820		arrayEvents.forEach(function(key) {
4821			var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
4822			var base = array[key];
4823
4824			Object.defineProperty(array, key, {
4825				configurable: true,
4826				enumerable: false,
4827				value: function() {
4828					var args = Array.prototype.slice.call(arguments);
4829					var res = base.apply(this, args);
4830
4831					helpers.each(array._chartjs.listeners, function(object) {
4832						if (typeof object[method] === 'function') {
4833							object[method].apply(object, args);
4834						}
4835					});
4836
4837					return res;
4838				}
4839			});
4840		});
4841	}
4842
4843	/**
4844	 * Removes the given array event listener and cleanup extra attached properties (such as
4845	 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
4846	 */
4847	function unlistenArrayEvents(array, listener) {
4848		var stub = array._chartjs;
4849		if (!stub) {
4850			return;
4851		}
4852
4853		var listeners = stub.listeners;
4854		var index = listeners.indexOf(listener);
4855		if (index !== -1) {
4856			listeners.splice(index, 1);
4857		}
4858
4859		if (listeners.length > 0) {
4860			return;
4861		}
4862
4863		arrayEvents.forEach(function(key) {
4864			delete array[key];
4865		});
4866
4867		delete array._chartjs;
4868	}
4869
4870	// Base class for all dataset controllers (line, bar, etc)
4871	Chart.DatasetController = function(chart, datasetIndex) {
4872		this.initialize(chart, datasetIndex);
4873	};
4874
4875	helpers.extend(Chart.DatasetController.prototype, {
4876
4877		/**
4878		 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
4879		 * @type {Chart.core.element}
4880		 */
4881		datasetElementType: null,
4882
4883		/**
4884		 * Element type used to generate a meta data (e.g. Chart.element.Point).
4885		 * @type {Chart.core.element}
4886		 */
4887		dataElementType: null,
4888
4889		initialize: function(chart, datasetIndex) {
4890			var me = this;
4891			me.chart = chart;
4892			me.index = datasetIndex;
4893			me.linkScales();
4894			me.addElements();
4895		},
4896
4897		updateIndex: function(datasetIndex) {
4898			this.index = datasetIndex;
4899		},
4900
4901		linkScales: function() {
4902			var me = this;
4903			var meta = me.getMeta();
4904			var dataset = me.getDataset();
4905
4906			if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {
4907				meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
4908			}
4909			if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {
4910				meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
4911			}
4912		},
4913
4914		getDataset: function() {
4915			return this.chart.data.datasets[this.index];
4916		},
4917
4918		getMeta: function() {
4919			return this.chart.getDatasetMeta(this.index);
4920		},
4921
4922		getScaleForId: function(scaleID) {
4923			return this.chart.scales[scaleID];
4924		},
4925
4926		reset: function() {
4927			this.update(true);
4928		},
4929
4930		/**
4931		 * @private
4932		 */
4933		destroy: function() {
4934			if (this._data) {
4935				unlistenArrayEvents(this._data, this);
4936			}
4937		},
4938
4939		createMetaDataset: function() {
4940			var me = this;
4941			var type = me.datasetElementType;
4942			return type && new type({
4943				_chart: me.chart,
4944				_datasetIndex: me.index
4945			});
4946		},
4947
4948		createMetaData: function(index) {
4949			var me = this;
4950			var type = me.dataElementType;
4951			return type && new type({
4952				_chart: me.chart,
4953				_datasetIndex: me.index,
4954				_index: index
4955			});
4956		},
4957
4958		addElements: function() {
4959			var me = this;
4960			var meta = me.getMeta();
4961			var data = me.getDataset().data || [];
4962			var metaData = meta.data;
4963			var i, ilen;
4964
4965			for (i = 0, ilen = data.length; i < ilen; ++i) {
4966				metaData[i] = metaData[i] || me.createMetaData(i);
4967			}
4968
4969			meta.dataset = meta.dataset || me.createMetaDataset();
4970		},
4971
4972		addElementAndReset: function(index) {
4973			var element = this.createMetaData(index);
4974			this.getMeta().data.splice(index, 0, element);
4975			this.updateElement(element, index, true);
4976		},
4977
4978		buildOrUpdateElements: function() {
4979			var me = this;
4980			var dataset = me.getDataset();
4981			var data = dataset.data || (dataset.data = []);
4982
4983			// In order to correctly handle data addition/deletion animation (an thus simulate
4984			// real-time charts), we need to monitor these data modifications and synchronize
4985			// the internal meta data accordingly.
4986			if (me._data !== data) {
4987				if (me._data) {
4988					// This case happens when the user replaced the data array instance.
4989					unlistenArrayEvents(me._data, me);
4990				}
4991
4992				listenArrayEvents(data, me);
4993				me._data = data;
4994			}
4995
4996			// Re-sync meta data in case the user replaced the data array or if we missed
4997			// any updates and so make sure that we handle number of datapoints changing.
4998			me.resyncElements();
4999		},
5000
5001		update: helpers.noop,
5002
5003		transition: function(easingValue) {
5004			var meta = this.getMeta();
5005			var elements = meta.data || [];
5006			var ilen = elements.length;
5007			var i = 0;
5008
5009			for (; i < ilen; ++i) {
5010				elements[i].transition(easingValue);
5011			}
5012
5013			if (meta.dataset) {
5014				meta.dataset.transition(easingValue);
5015			}
5016		},
5017
5018		draw: function() {
5019			var meta = this.getMeta();
5020			var elements = meta.data || [];
5021			var ilen = elements.length;
5022			var i = 0;
5023
5024			if (meta.dataset) {
5025				meta.dataset.draw();
5026			}
5027
5028			for (; i < ilen; ++i) {
5029				elements[i].draw();
5030			}
5031		},
5032
5033		removeHoverStyle: function(element) {
5034			helpers.merge(element._model, element.$previousStyle || {});
5035			delete element.$previousStyle;
5036		},
5037
5038		setHoverStyle: function(element) {
5039			var dataset = this.chart.data.datasets[element._datasetIndex];
5040			var index = element._index;
5041			var custom = element.custom || {};
5042			var valueOrDefault = helpers.valueAtIndexOrDefault;
5043			var getHoverColor = helpers.getHoverColor;
5044			var model = element._model;
5045
5046			element.$previousStyle = {
5047				backgroundColor: model.backgroundColor,
5048				borderColor: model.borderColor,
5049				borderWidth: model.borderWidth
5050			};
5051
5052			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
5053			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
5054			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
5055		},
5056
5057		/**
5058		 * @private
5059		 */
5060		resyncElements: function() {
5061			var me = this;
5062			var meta = me.getMeta();
5063			var data = me.getDataset().data;
5064			var numMeta = meta.data.length;
5065			var numData = data.length;
5066
5067			if (numData < numMeta) {
5068				meta.data.splice(numData, numMeta - numData);
5069			} else if (numData > numMeta) {
5070				me.insertElements(numMeta, numData - numMeta);
5071			}
5072		},
5073
5074		/**
5075		 * @private
5076		 */
5077		insertElements: function(start, count) {
5078			for (var i = 0; i < count; ++i) {
5079				this.addElementAndReset(start + i);
5080			}
5081		},
5082
5083		/**
5084		 * @private
5085		 */
5086		onDataPush: function() {
5087			this.insertElements(this.getDataset().data.length - 1, arguments.length);
5088		},
5089
5090		/**
5091		 * @private
5092		 */
5093		onDataPop: function() {
5094			this.getMeta().data.pop();
5095		},
5096
5097		/**
5098		 * @private
5099		 */
5100		onDataShift: function() {
5101			this.getMeta().data.shift();
5102		},
5103
5104		/**
5105		 * @private
5106		 */
5107		onDataSplice: function(start, count) {
5108			this.getMeta().data.splice(start, count);
5109			this.insertElements(start, arguments.length - 2);
5110		},
5111
5112		/**
5113		 * @private
5114		 */
5115		onDataUnshift: function() {
5116			this.insertElements(0, arguments.length);
5117		}
5118	});
5119
5120	Chart.DatasetController.extend = helpers.inherits;
5121};
5122
5123},{"46":46}],26:[function(require,module,exports){
5124'use strict';
5125
5126var helpers = require(46);
5127
5128module.exports = {
5129	/**
5130	 * @private
5131	 */
5132	_set: function(scope, values) {
5133		return helpers.merge(this[scope] || (this[scope] = {}), values);
5134	}
5135};
5136
5137},{"46":46}],27:[function(require,module,exports){
5138'use strict';
5139
5140var color = require(3);
5141var helpers = require(46);
5142
5143function interpolate(start, view, model, ease) {
5144	var keys = Object.keys(model);
5145	var i, ilen, key, actual, origin, target, type, c0, c1;
5146
5147	for (i = 0, ilen = keys.length; i < ilen; ++i) {
5148		key = keys[i];
5149
5150		target = model[key];
5151
5152		// if a value is added to the model after pivot() has been called, the view
5153		// doesn't contain it, so let's initialize the view to the target value.
5154		if (!view.hasOwnProperty(key)) {
5155			view[key] = target;
5156		}
5157
5158		actual = view[key];
5159
5160		if (actual === target || key[0] === '_') {
5161			continue;
5162		}
5163
5164		if (!start.hasOwnProperty(key)) {
5165			start[key] = actual;
5166		}
5167
5168		origin = start[key];
5169
5170		type = typeof target;
5171
5172		if (type === typeof origin) {
5173			if (type === 'string') {
5174				c0 = color(origin);
5175				if (c0.valid) {
5176					c1 = color(target);
5177					if (c1.valid) {
5178						view[key] = c1.mix(c0, ease).rgbString();
5179						continue;
5180					}
5181				}
5182			} else if (type === 'number' && isFinite(origin) && isFinite(target)) {
5183				view[key] = origin + (target - origin) * ease;
5184				continue;
5185			}
5186		}
5187
5188		view[key] = target;
5189	}
5190}
5191
5192var Element = function(configuration) {
5193	helpers.extend(this, configuration);
5194	this.initialize.apply(this, arguments);
5195};
5196
5197helpers.extend(Element.prototype, {
5198
5199	initialize: function() {
5200		this.hidden = false;
5201	},
5202
5203	pivot: function() {
5204		var me = this;
5205		if (!me._view) {
5206			me._view = helpers.clone(me._model);
5207		}
5208		me._start = {};
5209		return me;
5210	},
5211
5212	transition: function(ease) {
5213		var me = this;
5214		var model = me._model;
5215		var start = me._start;
5216		var view = me._view;
5217
5218		// No animation -> No Transition
5219		if (!model || ease === 1) {
5220			me._view = model;
5221			me._start = null;
5222			return me;
5223		}
5224
5225		if (!view) {
5226			view = me._view = {};
5227		}
5228
5229		if (!start) {
5230			start = me._start = {};
5231		}
5232
5233		interpolate(start, view, model, ease);
5234
5235		return me;
5236	},
5237
5238	tooltipPosition: function() {
5239		return {
5240			x: this._model.x,
5241			y: this._model.y
5242		};
5243	},
5244
5245	hasValue: function() {
5246		return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
5247	}
5248});
5249
5250Element.extend = helpers.inherits;
5251
5252module.exports = Element;
5253
5254},{"3":3,"46":46}],28:[function(require,module,exports){
5255/* global window: false */
5256/* global document: false */
5257'use strict';
5258
5259var color = require(3);
5260var defaults = require(26);
5261var helpers = require(46);
5262var scaleService = require(34);
5263
5264module.exports = function() {
5265
5266	// -- Basic js utility methods
5267
5268	helpers.configMerge = function(/* objects ... */) {
5269		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
5270			merger: function(key, target, source, options) {
5271				var tval = target[key] || {};
5272				var sval = source[key];
5273
5274				if (key === 'scales') {
5275					// scale config merging is complex. Add our own function here for that
5276					target[key] = helpers.scaleMerge(tval, sval);
5277				} else if (key === 'scale') {
5278					// used in polar area & radar charts since there is only one scale
5279					target[key] = helpers.merge(tval, [scaleService.getScaleDefaults(sval.type), sval]);
5280				} else {
5281					helpers._merger(key, target, source, options);
5282				}
5283			}
5284		});
5285	};
5286
5287	helpers.scaleMerge = function(/* objects ... */) {
5288		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
5289			merger: function(key, target, source, options) {
5290				if (key === 'xAxes' || key === 'yAxes') {
5291					var slen = source[key].length;
5292					var i, type, scale;
5293
5294					if (!target[key]) {
5295						target[key] = [];
5296					}
5297
5298					for (i = 0; i < slen; ++i) {
5299						scale = source[key][i];
5300						type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');
5301
5302						if (i >= target[key].length) {
5303							target[key].push({});
5304						}
5305
5306						if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
5307							// new/untyped scale or type changed: let's apply the new defaults
5308							// then merge source scale to correctly overwrite the defaults.
5309							helpers.merge(target[key][i], [scaleService.getScaleDefaults(type), scale]);
5310						} else {
5311							// scales type are the same
5312							helpers.merge(target[key][i], scale);
5313						}
5314					}
5315				} else {
5316					helpers._merger(key, target, source, options);
5317				}
5318			}
5319		});
5320	};
5321
5322	helpers.where = function(collection, filterCallback) {
5323		if (helpers.isArray(collection) && Array.prototype.filter) {
5324			return collection.filter(filterCallback);
5325		}
5326		var filtered = [];
5327
5328		helpers.each(collection, function(item) {
5329			if (filterCallback(item)) {
5330				filtered.push(item);
5331			}
5332		});
5333
5334		return filtered;
5335	};
5336	helpers.findIndex = Array.prototype.findIndex ?
5337		function(array, callback, scope) {
5338			return array.findIndex(callback, scope);
5339		} :
5340		function(array, callback, scope) {
5341			scope = scope === undefined ? array : scope;
5342			for (var i = 0, ilen = array.length; i < ilen; ++i) {
5343				if (callback.call(scope, array[i], i, array)) {
5344					return i;
5345				}
5346			}
5347			return -1;
5348		};
5349	helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
5350		// Default to start of the array
5351		if (helpers.isNullOrUndef(startIndex)) {
5352			startIndex = -1;
5353		}
5354		for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
5355			var currentItem = arrayToSearch[i];
5356			if (filterCallback(currentItem)) {
5357				return currentItem;
5358			}
5359		}
5360	};
5361	helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
5362		// Default to end of the array
5363		if (helpers.isNullOrUndef(startIndex)) {
5364			startIndex = arrayToSearch.length;
5365		}
5366		for (var i = startIndex - 1; i >= 0; i--) {
5367			var currentItem = arrayToSearch[i];
5368			if (filterCallback(currentItem)) {
5369				return currentItem;
5370			}
5371		}
5372	};
5373
5374	// -- Math methods
5375	helpers.isNumber = function(n) {
5376		return !isNaN(parseFloat(n)) && isFinite(n);
5377	};
5378	helpers.almostEquals = function(x, y, epsilon) {
5379		return Math.abs(x - y) < epsilon;
5380	};
5381	helpers.almostWhole = function(x, epsilon) {
5382		var rounded = Math.round(x);
5383		return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
5384	};
5385	helpers.max = function(array) {
5386		return array.reduce(function(max, value) {
5387			if (!isNaN(value)) {
5388				return Math.max(max, value);
5389			}
5390			return max;
5391		}, Number.NEGATIVE_INFINITY);
5392	};
5393	helpers.min = function(array) {
5394		return array.reduce(function(min, value) {
5395			if (!isNaN(value)) {
5396				return Math.min(min, value);
5397			}
5398			return min;
5399		}, Number.POSITIVE_INFINITY);
5400	};
5401	helpers.sign = Math.sign ?
5402		function(x) {
5403			return Math.sign(x);
5404		} :
5405		function(x) {
5406			x = +x; // convert to a number
5407			if (x === 0 || isNaN(x)) {
5408				return x;
5409			}
5410			return x > 0 ? 1 : -1;
5411		};
5412	helpers.log10 = Math.log10 ?
5413		function(x) {
5414			return Math.log10(x);
5415		} :
5416		function(x) {
5417			var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
5418			// Check for whole powers of 10,
5419			// which due to floating point rounding error should be corrected.
5420			var powerOf10 = Math.round(exponent);
5421			var isPowerOf10 = x === Math.pow(10, powerOf10);
5422
5423			return isPowerOf10 ? powerOf10 : exponent;
5424		};
5425	helpers.toRadians = function(degrees) {
5426		return degrees * (Math.PI / 180);
5427	};
5428	helpers.toDegrees = function(radians) {
5429		return radians * (180 / Math.PI);
5430	};
5431	// Gets the angle from vertical upright to the point about a centre.
5432	helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
5433		var distanceFromXCenter = anglePoint.x - centrePoint.x;
5434		var distanceFromYCenter = anglePoint.y - centrePoint.y;
5435		var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
5436
5437		var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
5438
5439		if (angle < (-0.5 * Math.PI)) {
5440			angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
5441		}
5442
5443		return {
5444			angle: angle,
5445			distance: radialDistanceFromCenter
5446		};
5447	};
5448	helpers.distanceBetweenPoints = function(pt1, pt2) {
5449		return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
5450	};
5451	helpers.aliasPixel = function(pixelWidth) {
5452		return (pixelWidth % 2 === 0) ? 0 : 0.5;
5453	};
5454	helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
5455		// Props to Rob Spencer at scaled innovation for his post on splining between points
5456		// http://scaledinnovation.com/analytics/splines/aboutSplines.html
5457
5458		// This function must also respect "skipped" points
5459
5460		var previous = firstPoint.skip ? middlePoint : firstPoint;
5461		var current = middlePoint;
5462		var next = afterPoint.skip ? middlePoint : afterPoint;
5463
5464		var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
5465		var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
5466
5467		var s01 = d01 / (d01 + d12);
5468		var s12 = d12 / (d01 + d12);
5469
5470		// If all points are the same, s01 & s02 will be inf
5471		s01 = isNaN(s01) ? 0 : s01;
5472		s12 = isNaN(s12) ? 0 : s12;
5473
5474		var fa = t * s01; // scaling factor for triangle Ta
5475		var fb = t * s12;
5476
5477		return {
5478			previous: {
5479				x: current.x - fa * (next.x - previous.x),
5480				y: current.y - fa * (next.y - previous.y)
5481			},
5482			next: {
5483				x: current.x + fb * (next.x - previous.x),
5484				y: current.y + fb * (next.y - previous.y)
5485			}
5486		};
5487	};
5488	helpers.EPSILON = Number.EPSILON || 1e-14;
5489	helpers.splineCurveMonotone = function(points) {
5490		// This function calculates Bézier control points in a similar way than |splineCurve|,
5491		// but preserves monotonicity of the provided data and ensures no local extremums are added
5492		// between the dataset discrete points due to the interpolation.
5493		// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
5494
5495		var pointsWithTangents = (points || []).map(function(point) {
5496			return {
5497				model: point._model,
5498				deltaK: 0,
5499				mK: 0
5500			};
5501		});
5502
5503		// Calculate slopes (deltaK) and initialize tangents (mK)
5504		var pointsLen = pointsWithTangents.length;
5505		var i, pointBefore, pointCurrent, pointAfter;
5506		for (i = 0; i < pointsLen; ++i) {
5507			pointCurrent = pointsWithTangents[i];
5508			if (pointCurrent.model.skip) {
5509				continue;
5510			}
5511
5512			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5513			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5514			if (pointAfter && !pointAfter.model.skip) {
5515				var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
5516
5517				// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
5518				pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
5519			}
5520
5521			if (!pointBefore || pointBefore.model.skip) {
5522				pointCurrent.mK = pointCurrent.deltaK;
5523			} else if (!pointAfter || pointAfter.model.skip) {
5524				pointCurrent.mK = pointBefore.deltaK;
5525			} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
5526				pointCurrent.mK = 0;
5527			} else {
5528				pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
5529			}
5530		}
5531
5532		// Adjust tangents to ensure monotonic properties
5533		var alphaK, betaK, tauK, squaredMagnitude;
5534		for (i = 0; i < pointsLen - 1; ++i) {
5535			pointCurrent = pointsWithTangents[i];
5536			pointAfter = pointsWithTangents[i + 1];
5537			if (pointCurrent.model.skip || pointAfter.model.skip) {
5538				continue;
5539			}
5540
5541			if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
5542				pointCurrent.mK = pointAfter.mK = 0;
5543				continue;
5544			}
5545
5546			alphaK = pointCurrent.mK / pointCurrent.deltaK;
5547			betaK = pointAfter.mK / pointCurrent.deltaK;
5548			squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
5549			if (squaredMagnitude <= 9) {
5550				continue;
5551			}
5552
5553			tauK = 3 / Math.sqrt(squaredMagnitude);
5554			pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
5555			pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
5556		}
5557
5558		// Compute control points
5559		var deltaX;
5560		for (i = 0; i < pointsLen; ++i) {
5561			pointCurrent = pointsWithTangents[i];
5562			if (pointCurrent.model.skip) {
5563				continue;
5564			}
5565
5566			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5567			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5568			if (pointBefore && !pointBefore.model.skip) {
5569				deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
5570				pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
5571				pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
5572			}
5573			if (pointAfter && !pointAfter.model.skip) {
5574				deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
5575				pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
5576				pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
5577			}
5578		}
5579	};
5580	helpers.nextItem = function(collection, index, loop) {
5581		if (loop) {
5582			return index >= collection.length - 1 ? collection[0] : collection[index + 1];
5583		}
5584		return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
5585	};
5586	helpers.previousItem = function(collection, index, loop) {
5587		if (loop) {
5588			return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
5589		}
5590		return index <= 0 ? collection[0] : collection[index - 1];
5591	};
5592	// Implementation of the nice number algorithm used in determining where axis labels will go
5593	helpers.niceNum = function(range, round) {
5594		var exponent = Math.floor(helpers.log10(range));
5595		var fraction = range / Math.pow(10, exponent);
5596		var niceFraction;
5597
5598		if (round) {
5599			if (fraction < 1.5) {
5600				niceFraction = 1;
5601			} else if (fraction < 3) {
5602				niceFraction = 2;
5603			} else if (fraction < 7) {
5604				niceFraction = 5;
5605			} else {
5606				niceFraction = 10;
5607			}
5608		} else if (fraction <= 1.0) {
5609			niceFraction = 1;
5610		} else if (fraction <= 2) {
5611			niceFraction = 2;
5612		} else if (fraction <= 5) {
5613			niceFraction = 5;
5614		} else {
5615			niceFraction = 10;
5616		}
5617
5618		return niceFraction * Math.pow(10, exponent);
5619	};
5620	// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
5621	helpers.requestAnimFrame = (function() {
5622		if (typeof window === 'undefined') {
5623			return function(callback) {
5624				callback();
5625			};
5626		}
5627		return window.requestAnimationFrame ||
5628			window.webkitRequestAnimationFrame ||
5629			window.mozRequestAnimationFrame ||
5630			window.oRequestAnimationFrame ||
5631			window.msRequestAnimationFrame ||
5632			function(callback) {
5633				return window.setTimeout(callback, 1000 / 60);
5634			};
5635	}());
5636	// -- DOM methods
5637	helpers.getRelativePosition = function(evt, chart) {
5638		var mouseX, mouseY;
5639		var e = evt.originalEvent || evt;
5640		var canvas = evt.target || evt.srcElement;
5641		var boundingRect = canvas.getBoundingClientRect();
5642
5643		var touches = e.touches;
5644		if (touches && touches.length > 0) {
5645			mouseX = touches[0].clientX;
5646			mouseY = touches[0].clientY;
5647
5648		} else {
5649			mouseX = e.clientX;
5650			mouseY = e.clientY;
5651		}
5652
5653		// Scale mouse coordinates into canvas coordinates
5654		// by following the pattern laid out by 'jerryj' in the comments of
5655		// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
5656		var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
5657		var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
5658		var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
5659		var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
5660		var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
5661		var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
5662
5663		// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
5664		// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
5665		mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
5666		mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
5667
5668		return {
5669			x: mouseX,
5670			y: mouseY
5671		};
5672
5673	};
5674
5675	// Private helper function to convert max-width/max-height values that may be percentages into a number
5676	function parseMaxStyle(styleValue, node, parentProperty) {
5677		var valueInPixels;
5678		if (typeof styleValue === 'string') {
5679			valueInPixels = parseInt(styleValue, 10);
5680
5681			if (styleValue.indexOf('%') !== -1) {
5682				// percentage * size in dimension
5683				valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
5684			}
5685		} else {
5686			valueInPixels = styleValue;
5687		}
5688
5689		return valueInPixels;
5690	}
5691
5692	/**
5693	 * Returns if the given value contains an effective constraint.
5694	 * @private
5695	 */
5696	function isConstrainedValue(value) {
5697		return value !== undefined && value !== null && value !== 'none';
5698	}
5699
5700	// Private helper to get a constraint dimension
5701	// @param domNode : the node to check the constraint on
5702	// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
5703	// @param percentageProperty : property of parent to use when calculating width as a percentage
5704	// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
5705	function getConstraintDimension(domNode, maxStyle, percentageProperty) {
5706		var view = document.defaultView;
5707		var parentNode = helpers._getParentNode(domNode);
5708		var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
5709		var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
5710		var hasCNode = isConstrainedValue(constrainedNode);
5711		var hasCContainer = isConstrainedValue(constrainedContainer);
5712		var infinity = Number.POSITIVE_INFINITY;
5713
5714		if (hasCNode || hasCContainer) {
5715			return Math.min(
5716				hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
5717				hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
5718		}
5719
5720		return 'none';
5721	}
5722	// returns Number or undefined if no constraint
5723	helpers.getConstraintWidth = function(domNode) {
5724		return getConstraintDimension(domNode, 'max-width', 'clientWidth');
5725	};
5726	// returns Number or undefined if no constraint
5727	helpers.getConstraintHeight = function(domNode) {
5728		return getConstraintDimension(domNode, 'max-height', 'clientHeight');
5729	};
5730	/**
5731	 * @private
5732 	 */
5733	helpers._calculatePadding = function(container, padding, parentDimension) {
5734		padding = helpers.getStyle(container, padding);
5735
5736		return padding.indexOf('%') > -1 ? parentDimension / parseInt(padding, 10) : parseInt(padding, 10);
5737	};
5738	/**
5739	 * @private
5740	 */
5741	helpers._getParentNode = function(domNode) {
5742		var parent = domNode.parentNode;
5743		if (parent && parent.host) {
5744			parent = parent.host;
5745		}
5746		return parent;
5747	};
5748	helpers.getMaximumWidth = function(domNode) {
5749		var container = helpers._getParentNode(domNode);
5750		if (!container) {
5751			return domNode.clientWidth;
5752		}
5753
5754		var clientWidth = container.clientWidth;
5755		var paddingLeft = helpers._calculatePadding(container, 'padding-left', clientWidth);
5756		var paddingRight = helpers._calculatePadding(container, 'padding-right', clientWidth);
5757
5758		var w = clientWidth - paddingLeft - paddingRight;
5759		var cw = helpers.getConstraintWidth(domNode);
5760		return isNaN(cw) ? w : Math.min(w, cw);
5761	};
5762	helpers.getMaximumHeight = function(domNode) {
5763		var container = helpers._getParentNode(domNode);
5764		if (!container) {
5765			return domNode.clientHeight;
5766		}
5767
5768		var clientHeight = container.clientHeight;
5769		var paddingTop = helpers._calculatePadding(container, 'padding-top', clientHeight);
5770		var paddingBottom = helpers._calculatePadding(container, 'padding-bottom', clientHeight);
5771
5772		var h = clientHeight - paddingTop - paddingBottom;
5773		var ch = helpers.getConstraintHeight(domNode);
5774		return isNaN(ch) ? h : Math.min(h, ch);
5775	};
5776	helpers.getStyle = function(el, property) {
5777		return el.currentStyle ?
5778			el.currentStyle[property] :
5779			document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
5780	};
5781	helpers.retinaScale = function(chart, forceRatio) {
5782		var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
5783		if (pixelRatio === 1) {
5784			return;
5785		}
5786
5787		var canvas = chart.canvas;
5788		var height = chart.height;
5789		var width = chart.width;
5790
5791		canvas.height = height * pixelRatio;
5792		canvas.width = width * pixelRatio;
5793		chart.ctx.scale(pixelRatio, pixelRatio);
5794
5795		// If no style has been set on the canvas, the render size is used as display size,
5796		// making the chart visually bigger, so let's enforce it to the "correct" values.
5797		// See https://github.com/chartjs/Chart.js/issues/3575
5798		if (!canvas.style.height && !canvas.style.width) {
5799			canvas.style.height = height + 'px';
5800			canvas.style.width = width + 'px';
5801		}
5802	};
5803	// -- Canvas methods
5804	helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
5805		return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
5806	};
5807	helpers.longestText = function(ctx, font, arrayOfThings, cache) {
5808		cache = cache || {};
5809		var data = cache.data = cache.data || {};
5810		var gc = cache.garbageCollect = cache.garbageCollect || [];
5811
5812		if (cache.font !== font) {
5813			data = cache.data = {};
5814			gc = cache.garbageCollect = [];
5815			cache.font = font;
5816		}
5817
5818		ctx.font = font;
5819		var longest = 0;
5820		helpers.each(arrayOfThings, function(thing) {
5821			// Undefined strings and arrays should not be measured
5822			if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
5823				longest = helpers.measureText(ctx, data, gc, longest, thing);
5824			} else if (helpers.isArray(thing)) {
5825				// if it is an array lets measure each element
5826				// to do maybe simplify this function a bit so we can do this more recursively?
5827				helpers.each(thing, function(nestedThing) {
5828					// Undefined strings and arrays should not be measured
5829					if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
5830						longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
5831					}
5832				});
5833			}
5834		});
5835
5836		var gcLen = gc.length / 2;
5837		if (gcLen > arrayOfThings.length) {
5838			for (var i = 0; i < gcLen; i++) {
5839				delete data[gc[i]];
5840			}
5841			gc.splice(0, gcLen);
5842		}
5843		return longest;
5844	};
5845	helpers.measureText = function(ctx, data, gc, longest, string) {
5846		var textWidth = data[string];
5847		if (!textWidth) {
5848			textWidth = data[string] = ctx.measureText(string).width;
5849			gc.push(string);
5850		}
5851		if (textWidth > longest) {
5852			longest = textWidth;
5853		}
5854		return longest;
5855	};
5856	helpers.numberOfLabelLines = function(arrayOfThings) {
5857		var numberOfLines = 1;
5858		helpers.each(arrayOfThings, function(thing) {
5859			if (helpers.isArray(thing)) {
5860				if (thing.length > numberOfLines) {
5861					numberOfLines = thing.length;
5862				}
5863			}
5864		});
5865		return numberOfLines;
5866	};
5867
5868	helpers.color = !color ?
5869		function(value) {
5870			console.error('Color.js not found!');
5871			return value;
5872		} :
5873		function(value) {
5874			/* global CanvasGradient */
5875			if (value instanceof CanvasGradient) {
5876				value = defaults.global.defaultColor;
5877			}
5878
5879			return color(value);
5880		};
5881
5882	helpers.getHoverColor = function(colorValue) {
5883		/* global CanvasPattern */
5884		return (colorValue instanceof CanvasPattern) ?
5885			colorValue :
5886			helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
5887	};
5888};
5889
5890},{"26":26,"3":3,"34":34,"46":46}],29:[function(require,module,exports){
5891'use strict';
5892
5893var helpers = require(46);
5894
5895/**
5896 * Helper function to get relative position for an event
5897 * @param {Event|IEvent} event - The event to get the position for
5898 * @param {Chart} chart - The chart
5899 * @returns {Point} the event position
5900 */
5901function getRelativePosition(e, chart) {
5902	if (e.native) {
5903		return {
5904			x: e.x,
5905			y: e.y
5906		};
5907	}
5908
5909	return helpers.getRelativePosition(e, chart);
5910}
5911
5912/**
5913 * Helper function to traverse all of the visible elements in the chart
5914 * @param chart {chart} the chart
5915 * @param handler {Function} the callback to execute for each visible item
5916 */
5917function parseVisibleItems(chart, handler) {
5918	var datasets = chart.data.datasets;
5919	var meta, i, j, ilen, jlen;
5920
5921	for (i = 0, ilen = datasets.length; i < ilen; ++i) {
5922		if (!chart.isDatasetVisible(i)) {
5923			continue;
5924		}
5925
5926		meta = chart.getDatasetMeta(i);
5927		for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
5928			var element = meta.data[j];
5929			if (!element._view.skip) {
5930				handler(element);
5931			}
5932		}
5933	}
5934}
5935
5936/**
5937 * Helper function to get the items that intersect the event position
5938 * @param items {ChartElement[]} elements to filter
5939 * @param position {Point} the point to be nearest to
5940 * @return {ChartElement[]} the nearest items
5941 */
5942function getIntersectItems(chart, position) {
5943	var elements = [];
5944
5945	parseVisibleItems(chart, function(element) {
5946		if (element.inRange(position.x, position.y)) {
5947			elements.push(element);
5948		}
5949	});
5950
5951	return elements;
5952}
5953
5954/**
5955 * Helper function to get the items nearest to the event position considering all visible items in teh chart
5956 * @param chart {Chart} the chart to look at elements from
5957 * @param position {Point} the point to be nearest to
5958 * @param intersect {Boolean} if true, only consider items that intersect the position
5959 * @param distanceMetric {Function} function to provide the distance between points
5960 * @return {ChartElement[]} the nearest items
5961 */
5962function getNearestItems(chart, position, intersect, distanceMetric) {
5963	var minDistance = Number.POSITIVE_INFINITY;
5964	var nearestItems = [];
5965
5966	parseVisibleItems(chart, function(element) {
5967		if (intersect && !element.inRange(position.x, position.y)) {
5968			return;
5969		}
5970
5971		var center = element.getCenterPoint();
5972		var distance = distanceMetric(position, center);
5973
5974		if (distance < minDistance) {
5975			nearestItems = [element];
5976			minDistance = distance;
5977		} else if (distance === minDistance) {
5978			// Can have multiple items at the same distance in which case we sort by size
5979			nearestItems.push(element);
5980		}
5981	});
5982
5983	return nearestItems;
5984}
5985
5986/**
5987 * Get a distance metric function for two points based on the
5988 * axis mode setting
5989 * @param {String} axis the axis mode. x|y|xy
5990 */
5991function getDistanceMetricForAxis(axis) {
5992	var useX = axis.indexOf('x') !== -1;
5993	var useY = axis.indexOf('y') !== -1;
5994
5995	return function(pt1, pt2) {
5996		var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
5997		var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
5998		return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
5999	};
6000}
6001
6002function indexMode(chart, e, options) {
6003	var position = getRelativePosition(e, chart);
6004	// Default axis for index mode is 'x' to match old behaviour
6005	options.axis = options.axis || 'x';
6006	var distanceMetric = getDistanceMetricForAxis(options.axis);
6007	var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6008	var elements = [];
6009
6010	if (!items.length) {
6011		return [];
6012	}
6013
6014	chart.data.datasets.forEach(function(dataset, datasetIndex) {
6015		if (chart.isDatasetVisible(datasetIndex)) {
6016			var meta = chart.getDatasetMeta(datasetIndex);
6017			var element = meta.data[items[0]._index];
6018
6019			// don't count items that are skipped (null data)
6020			if (element && !element._view.skip) {
6021				elements.push(element);
6022			}
6023		}
6024	});
6025
6026	return elements;
6027}
6028
6029/**
6030 * @interface IInteractionOptions
6031 */
6032/**
6033 * If true, only consider items that intersect the point
6034 * @name IInterfaceOptions#boolean
6035 * @type Boolean
6036 */
6037
6038/**
6039 * Contains interaction related functions
6040 * @namespace Chart.Interaction
6041 */
6042module.exports = {
6043	// Helper function for different modes
6044	modes: {
6045		single: function(chart, e) {
6046			var position = getRelativePosition(e, chart);
6047			var elements = [];
6048
6049			parseVisibleItems(chart, function(element) {
6050				if (element.inRange(position.x, position.y)) {
6051					elements.push(element);
6052					return elements;
6053				}
6054			});
6055
6056			return elements.slice(0, 1);
6057		},
6058
6059		/**
6060		 * @function Chart.Interaction.modes.label
6061		 * @deprecated since version 2.4.0
6062		 * @todo remove at version 3
6063		 * @private
6064		 */
6065		label: indexMode,
6066
6067		/**
6068		 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
6069		 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
6070		 * @function Chart.Interaction.modes.index
6071		 * @since v2.4.0
6072		 * @param chart {chart} the chart we are returning items from
6073		 * @param e {Event} the event we are find things at
6074		 * @param options {IInteractionOptions} options to use during interaction
6075		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6076		 */
6077		index: indexMode,
6078
6079		/**
6080		 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
6081		 * If the options.intersect is false, we find the nearest item and return the items in that dataset
6082		 * @function Chart.Interaction.modes.dataset
6083		 * @param chart {chart} the chart we are returning items from
6084		 * @param e {Event} the event we are find things at
6085		 * @param options {IInteractionOptions} options to use during interaction
6086		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6087		 */
6088		dataset: function(chart, e, options) {
6089			var position = getRelativePosition(e, chart);
6090			options.axis = options.axis || 'xy';
6091			var distanceMetric = getDistanceMetricForAxis(options.axis);
6092			var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6093
6094			if (items.length > 0) {
6095				items = chart.getDatasetMeta(items[0]._datasetIndex).data;
6096			}
6097
6098			return items;
6099		},
6100
6101		/**
6102		 * @function Chart.Interaction.modes.x-axis
6103		 * @deprecated since version 2.4.0. Use index mode and intersect == true
6104		 * @todo remove at version 3
6105		 * @private
6106		 */
6107		'x-axis': function(chart, e) {
6108			return indexMode(chart, e, {intersect: false});
6109		},
6110
6111		/**
6112		 * Point mode returns all elements that hit test based on the event position
6113		 * of the event
6114		 * @function Chart.Interaction.modes.intersect
6115		 * @param chart {chart} the chart we are returning items from
6116		 * @param e {Event} the event we are find things at
6117		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6118		 */
6119		point: function(chart, e) {
6120			var position = getRelativePosition(e, chart);
6121			return getIntersectItems(chart, position);
6122		},
6123
6124		/**
6125		 * nearest mode returns the element closest to the point
6126		 * @function Chart.Interaction.modes.intersect
6127		 * @param chart {chart} the chart we are returning items from
6128		 * @param e {Event} the event we are find things at
6129		 * @param options {IInteractionOptions} options to use
6130		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6131		 */
6132		nearest: function(chart, e, options) {
6133			var position = getRelativePosition(e, chart);
6134			options.axis = options.axis || 'xy';
6135			var distanceMetric = getDistanceMetricForAxis(options.axis);
6136			var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);
6137
6138			// We have multiple items at the same distance from the event. Now sort by smallest
6139			if (nearestItems.length > 1) {
6140				nearestItems.sort(function(a, b) {
6141					var sizeA = a.getArea();
6142					var sizeB = b.getArea();
6143					var ret = sizeA - sizeB;
6144
6145					if (ret === 0) {
6146						// if equal sort by dataset index
6147						ret = a._datasetIndex - b._datasetIndex;
6148					}
6149
6150					return ret;
6151				});
6152			}
6153
6154			// Return only 1 item
6155			return nearestItems.slice(0, 1);
6156		},
6157
6158		/**
6159		 * x mode returns the elements that hit-test at the current x coordinate
6160		 * @function Chart.Interaction.modes.x
6161		 * @param chart {chart} the chart we are returning items from
6162		 * @param e {Event} the event we are find things at
6163		 * @param options {IInteractionOptions} options to use
6164		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6165		 */
6166		x: function(chart, e, options) {
6167			var position = getRelativePosition(e, chart);
6168			var items = [];
6169			var intersectsItem = false;
6170
6171			parseVisibleItems(chart, function(element) {
6172				if (element.inXRange(position.x)) {
6173					items.push(element);
6174				}
6175
6176				if (element.inRange(position.x, position.y)) {
6177					intersectsItem = true;
6178				}
6179			});
6180
6181			// If we want to trigger on an intersect and we don't have any items
6182			// that intersect the position, return nothing
6183			if (options.intersect && !intersectsItem) {
6184				items = [];
6185			}
6186			return items;
6187		},
6188
6189		/**
6190		 * y mode returns the elements that hit-test at the current y coordinate
6191		 * @function Chart.Interaction.modes.y
6192		 * @param chart {chart} the chart we are returning items from
6193		 * @param e {Event} the event we are find things at
6194		 * @param options {IInteractionOptions} options to use
6195		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6196		 */
6197		y: function(chart, e, options) {
6198			var position = getRelativePosition(e, chart);
6199			var items = [];
6200			var intersectsItem = false;
6201
6202			parseVisibleItems(chart, function(element) {
6203				if (element.inYRange(position.y)) {
6204					items.push(element);
6205				}
6206
6207				if (element.inRange(position.x, position.y)) {
6208					intersectsItem = true;
6209				}
6210			});
6211
6212			// If we want to trigger on an intersect and we don't have any items
6213			// that intersect the position, return nothing
6214			if (options.intersect && !intersectsItem) {
6215				items = [];
6216			}
6217			return items;
6218		}
6219	}
6220};
6221
6222},{"46":46}],30:[function(require,module,exports){
6223'use strict';
6224
6225var defaults = require(26);
6226
6227defaults._set('global', {
6228	responsive: true,
6229	responsiveAnimationDuration: 0,
6230	maintainAspectRatio: true,
6231	events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
6232	hover: {
6233		onHover: null,
6234		mode: 'nearest',
6235		intersect: true,
6236		animationDuration: 400
6237	},
6238	onClick: null,
6239	defaultColor: 'rgba(0,0,0,0.1)',
6240	defaultFontColor: '#666',
6241	defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
6242	defaultFontSize: 12,
6243	defaultFontStyle: 'normal',
6244	showLines: true,
6245
6246	// Element defaults defined in element extensions
6247	elements: {},
6248
6249	// Layout options such as padding
6250	layout: {
6251		padding: {
6252			top: 0,
6253			right: 0,
6254			bottom: 0,
6255			left: 0
6256		}
6257	}
6258});
6259
6260module.exports = function() {
6261
6262	// Occupy the global variable of Chart, and create a simple base class
6263	var Chart = function(item, config) {
6264		this.construct(item, config);
6265		return this;
6266	};
6267
6268	Chart.Chart = Chart;
6269
6270	return Chart;
6271};
6272
6273},{"26":26}],31:[function(require,module,exports){
6274'use strict';
6275
6276var helpers = require(46);
6277
6278function filterByPosition(array, position) {
6279	return helpers.where(array, function(v) {
6280		return v.position === position;
6281	});
6282}
6283
6284function sortByWeight(array, reverse) {
6285	array.forEach(function(v, i) {
6286		v._tmpIndex_ = i;
6287		return v;
6288	});
6289	array.sort(function(a, b) {
6290		var v0 = reverse ? b : a;
6291		var v1 = reverse ? a : b;
6292		return v0.weight === v1.weight ?
6293			v0._tmpIndex_ - v1._tmpIndex_ :
6294			v0.weight - v1.weight;
6295	});
6296	array.forEach(function(v) {
6297		delete v._tmpIndex_;
6298	});
6299}
6300
6301/**
6302 * @interface ILayoutItem
6303 * @prop {String} position - The position of the item in the chart layout. Possible values are
6304 * 'left', 'top', 'right', 'bottom', and 'chartArea'
6305 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
6306 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
6307 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
6308 * @prop {Function} update - Takes two parameters: width and height. Returns size of item
6309 * @prop {Function} getPadding -  Returns an object with padding on the edges
6310 * @prop {Number} width - Width of item. Must be valid after update()
6311 * @prop {Number} height - Height of item. Must be valid after update()
6312 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
6313 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
6314 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
6315 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
6316 */
6317
6318// The layout service is very self explanatory.  It's responsible for the layout within a chart.
6319// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
6320// It is this service's responsibility of carrying out that layout.
6321module.exports = {
6322	defaults: {},
6323
6324	/**
6325	 * Register a box to a chart.
6326	 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
6327	 * @param {Chart} chart - the chart to use
6328	 * @param {ILayoutItem} item - the item to add to be layed out
6329	 */
6330	addBox: function(chart, item) {
6331		if (!chart.boxes) {
6332			chart.boxes = [];
6333		}
6334
6335		// initialize item with default values
6336		item.fullWidth = item.fullWidth || false;
6337		item.position = item.position || 'top';
6338		item.weight = item.weight || 0;
6339
6340		chart.boxes.push(item);
6341	},
6342
6343	/**
6344	 * Remove a layoutItem from a chart
6345	 * @param {Chart} chart - the chart to remove the box from
6346	 * @param {Object} layoutItem - the item to remove from the layout
6347	 */
6348	removeBox: function(chart, layoutItem) {
6349		var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
6350		if (index !== -1) {
6351			chart.boxes.splice(index, 1);
6352		}
6353	},
6354
6355	/**
6356	 * Sets (or updates) options on the given `item`.
6357	 * @param {Chart} chart - the chart in which the item lives (or will be added to)
6358	 * @param {Object} item - the item to configure with the given options
6359	 * @param {Object} options - the new item options.
6360	 */
6361	configure: function(chart, item, options) {
6362		var props = ['fullWidth', 'position', 'weight'];
6363		var ilen = props.length;
6364		var i = 0;
6365		var prop;
6366
6367		for (; i < ilen; ++i) {
6368			prop = props[i];
6369			if (options.hasOwnProperty(prop)) {
6370				item[prop] = options[prop];
6371			}
6372		}
6373	},
6374
6375	/**
6376	 * Fits boxes of the given chart into the given size by having each box measure itself
6377	 * then running a fitting algorithm
6378	 * @param {Chart} chart - the chart
6379	 * @param {Number} width - the width to fit into
6380	 * @param {Number} height - the height to fit into
6381	 */
6382	update: function(chart, width, height) {
6383		if (!chart) {
6384			return;
6385		}
6386
6387		var layoutOptions = chart.options.layout || {};
6388		var padding = helpers.options.toPadding(layoutOptions.padding);
6389		var leftPadding = padding.left;
6390		var rightPadding = padding.right;
6391		var topPadding = padding.top;
6392		var bottomPadding = padding.bottom;
6393
6394		var leftBoxes = filterByPosition(chart.boxes, 'left');
6395		var rightBoxes = filterByPosition(chart.boxes, 'right');
6396		var topBoxes = filterByPosition(chart.boxes, 'top');
6397		var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
6398		var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');
6399
6400		// Sort boxes by weight. A higher weight is further away from the chart area
6401		sortByWeight(leftBoxes, true);
6402		sortByWeight(rightBoxes, false);
6403		sortByWeight(topBoxes, true);
6404		sortByWeight(bottomBoxes, false);
6405
6406		// Essentially we now have any number of boxes on each of the 4 sides.
6407		// Our canvas looks like the following.
6408		// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
6409		// B1 is the bottom axis
6410		// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
6411		// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
6412		// an error will be thrown.
6413		//
6414		// |----------------------------------------------------|
6415		// |                  T1 (Full Width)                   |
6416		// |----------------------------------------------------|
6417		// |    |    |                 T2                  |    |
6418		// |    |----|-------------------------------------|----|
6419		// |    |    | C1 |                           | C2 |    |
6420		// |    |    |----|                           |----|    |
6421		// |    |    |                                     |    |
6422		// | L1 | L2 |           ChartArea (C0)            | R1 |
6423		// |    |    |                                     |    |
6424		// |    |    |----|                           |----|    |
6425		// |    |    | C3 |                           | C4 |    |
6426		// |    |----|-------------------------------------|----|
6427		// |    |    |                 B1                  |    |
6428		// |----------------------------------------------------|
6429		// |                  B2 (Full Width)                   |
6430		// |----------------------------------------------------|
6431		//
6432		// What we do to find the best sizing, we do the following
6433		// 1. Determine the minimum size of the chart area.
6434		// 2. Split the remaining width equally between each vertical axis
6435		// 3. Split the remaining height equally between each horizontal axis
6436		// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
6437		// 5. Adjust the sizes of each axis based on it's minimum reported size.
6438		// 6. Refit each axis
6439		// 7. Position each axis in the final location
6440		// 8. Tell the chart the final location of the chart area
6441		// 9. Tell any axes that overlay the chart area the positions of the chart area
6442
6443		// Step 1
6444		var chartWidth = width - leftPadding - rightPadding;
6445		var chartHeight = height - topPadding - bottomPadding;
6446		var chartAreaWidth = chartWidth / 2; // min 50%
6447		var chartAreaHeight = chartHeight / 2; // min 50%
6448
6449		// Step 2
6450		var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
6451
6452		// Step 3
6453		var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
6454
6455		// Step 4
6456		var maxChartAreaWidth = chartWidth;
6457		var maxChartAreaHeight = chartHeight;
6458		var minBoxSizes = [];
6459
6460		function getMinimumBoxSize(box) {
6461			var minSize;
6462			var isHorizontal = box.isHorizontal();
6463
6464			if (isHorizontal) {
6465				minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
6466				maxChartAreaHeight -= minSize.height;
6467			} else {
6468				minSize = box.update(verticalBoxWidth, maxChartAreaHeight);
6469				maxChartAreaWidth -= minSize.width;
6470			}
6471
6472			minBoxSizes.push({
6473				horizontal: isHorizontal,
6474				minSize: minSize,
6475				box: box,
6476			});
6477		}
6478
6479		helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
6480
6481		// If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
6482		var maxHorizontalLeftPadding = 0;
6483		var maxHorizontalRightPadding = 0;
6484		var maxVerticalTopPadding = 0;
6485		var maxVerticalBottomPadding = 0;
6486
6487		helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
6488			if (horizontalBox.getPadding) {
6489				var boxPadding = horizontalBox.getPadding();
6490				maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
6491				maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
6492			}
6493		});
6494
6495		helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
6496			if (verticalBox.getPadding) {
6497				var boxPadding = verticalBox.getPadding();
6498				maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
6499				maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
6500			}
6501		});
6502
6503		// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
6504		// be if the axes are drawn at their minimum sizes.
6505		// Steps 5 & 6
6506		var totalLeftBoxesWidth = leftPadding;
6507		var totalRightBoxesWidth = rightPadding;
6508		var totalTopBoxesHeight = topPadding;
6509		var totalBottomBoxesHeight = bottomPadding;
6510
6511		// Function to fit a box
6512		function fitBox(box) {
6513			var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
6514				return minBox.box === box;
6515			});
6516
6517			if (minBoxSize) {
6518				if (box.isHorizontal()) {
6519					var scaleMargin = {
6520						left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
6521						right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
6522						top: 0,
6523						bottom: 0
6524					};
6525
6526					// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
6527					// on the margin. Sometimes they need to increase in size slightly
6528					box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
6529				} else {
6530					box.update(minBoxSize.minSize.width, maxChartAreaHeight);
6531				}
6532			}
6533		}
6534
6535		// Update, and calculate the left and right margins for the horizontal boxes
6536		helpers.each(leftBoxes.concat(rightBoxes), fitBox);
6537
6538		helpers.each(leftBoxes, function(box) {
6539			totalLeftBoxesWidth += box.width;
6540		});
6541
6542		helpers.each(rightBoxes, function(box) {
6543			totalRightBoxesWidth += box.width;
6544		});
6545
6546		// Set the Left and Right margins for the horizontal boxes
6547		helpers.each(topBoxes.concat(bottomBoxes), fitBox);
6548
6549		// Figure out how much margin is on the top and bottom of the vertical boxes
6550		helpers.each(topBoxes, function(box) {
6551			totalTopBoxesHeight += box.height;
6552		});
6553
6554		helpers.each(bottomBoxes, function(box) {
6555			totalBottomBoxesHeight += box.height;
6556		});
6557
6558		function finalFitVerticalBox(box) {
6559			var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
6560				return minSize.box === box;
6561			});
6562
6563			var scaleMargin = {
6564				left: 0,
6565				right: 0,
6566				top: totalTopBoxesHeight,
6567				bottom: totalBottomBoxesHeight
6568			};
6569
6570			if (minBoxSize) {
6571				box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
6572			}
6573		}
6574
6575		// Let the left layout know the final margin
6576		helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
6577
6578		// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
6579		totalLeftBoxesWidth = leftPadding;
6580		totalRightBoxesWidth = rightPadding;
6581		totalTopBoxesHeight = topPadding;
6582		totalBottomBoxesHeight = bottomPadding;
6583
6584		helpers.each(leftBoxes, function(box) {
6585			totalLeftBoxesWidth += box.width;
6586		});
6587
6588		helpers.each(rightBoxes, function(box) {
6589			totalRightBoxesWidth += box.width;
6590		});
6591
6592		helpers.each(topBoxes, function(box) {
6593			totalTopBoxesHeight += box.height;
6594		});
6595		helpers.each(bottomBoxes, function(box) {
6596			totalBottomBoxesHeight += box.height;
6597		});
6598
6599		// We may be adding some padding to account for rotated x axis labels
6600		var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
6601		totalLeftBoxesWidth += leftPaddingAddition;
6602		totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);
6603
6604		var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
6605		totalTopBoxesHeight += topPaddingAddition;
6606		totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);
6607
6608		// Figure out if our chart area changed. This would occur if the dataset layout label rotation
6609		// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
6610		// without calling `fit` again
6611		var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
6612		var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
6613
6614		if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
6615			helpers.each(leftBoxes, function(box) {
6616				box.height = newMaxChartAreaHeight;
6617			});
6618
6619			helpers.each(rightBoxes, function(box) {
6620				box.height = newMaxChartAreaHeight;
6621			});
6622
6623			helpers.each(topBoxes, function(box) {
6624				if (!box.fullWidth) {
6625					box.width = newMaxChartAreaWidth;
6626				}
6627			});
6628
6629			helpers.each(bottomBoxes, function(box) {
6630				if (!box.fullWidth) {
6631					box.width = newMaxChartAreaWidth;
6632				}
6633			});
6634
6635			maxChartAreaHeight = newMaxChartAreaHeight;
6636			maxChartAreaWidth = newMaxChartAreaWidth;
6637		}
6638
6639		// Step 7 - Position the boxes
6640		var left = leftPadding + leftPaddingAddition;
6641		var top = topPadding + topPaddingAddition;
6642
6643		function placeBox(box) {
6644			if (box.isHorizontal()) {
6645				box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
6646				box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
6647				box.top = top;
6648				box.bottom = top + box.height;
6649
6650				// Move to next point
6651				top = box.bottom;
6652
6653			} else {
6654
6655				box.left = left;
6656				box.right = left + box.width;
6657				box.top = totalTopBoxesHeight;
6658				box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
6659
6660				// Move to next point
6661				left = box.right;
6662			}
6663		}
6664
6665		helpers.each(leftBoxes.concat(topBoxes), placeBox);
6666
6667		// Account for chart width and height
6668		left += maxChartAreaWidth;
6669		top += maxChartAreaHeight;
6670
6671		helpers.each(rightBoxes, placeBox);
6672		helpers.each(bottomBoxes, placeBox);
6673
6674		// Step 8
6675		chart.chartArea = {
6676			left: totalLeftBoxesWidth,
6677			top: totalTopBoxesHeight,
6678			right: totalLeftBoxesWidth + maxChartAreaWidth,
6679			bottom: totalTopBoxesHeight + maxChartAreaHeight
6680		};
6681
6682		// Step 9
6683		helpers.each(chartAreaBoxes, function(box) {
6684			box.left = chart.chartArea.left;
6685			box.top = chart.chartArea.top;
6686			box.right = chart.chartArea.right;
6687			box.bottom = chart.chartArea.bottom;
6688
6689			box.update(maxChartAreaWidth, maxChartAreaHeight);
6690		});
6691	}
6692};
6693
6694},{"46":46}],32:[function(require,module,exports){
6695'use strict';
6696
6697var defaults = require(26);
6698var helpers = require(46);
6699
6700defaults._set('global', {
6701	plugins: {}
6702});
6703
6704/**
6705 * The plugin service singleton
6706 * @namespace Chart.plugins
6707 * @since 2.1.0
6708 */
6709module.exports = {
6710	/**
6711	 * Globally registered plugins.
6712	 * @private
6713	 */
6714	_plugins: [],
6715
6716	/**
6717	 * This identifier is used to invalidate the descriptors cache attached to each chart
6718	 * when a global plugin is registered or unregistered. In this case, the cache ID is
6719	 * incremented and descriptors are regenerated during following API calls.
6720	 * @private
6721	 */
6722	_cacheId: 0,
6723
6724	/**
6725	 * Registers the given plugin(s) if not already registered.
6726	 * @param {Array|Object} plugins plugin instance(s).
6727	 */
6728	register: function(plugins) {
6729		var p = this._plugins;
6730		([]).concat(plugins).forEach(function(plugin) {
6731			if (p.indexOf(plugin) === -1) {
6732				p.push(plugin);
6733			}
6734		});
6735
6736		this._cacheId++;
6737	},
6738
6739	/**
6740	 * Unregisters the given plugin(s) only if registered.
6741	 * @param {Array|Object} plugins plugin instance(s).
6742	 */
6743	unregister: function(plugins) {
6744		var p = this._plugins;
6745		([]).concat(plugins).forEach(function(plugin) {
6746			var idx = p.indexOf(plugin);
6747			if (idx !== -1) {
6748				p.splice(idx, 1);
6749			}
6750		});
6751
6752		this._cacheId++;
6753	},
6754
6755	/**
6756	 * Remove all registered plugins.
6757	 * @since 2.1.5
6758	 */
6759	clear: function() {
6760		this._plugins = [];
6761		this._cacheId++;
6762	},
6763
6764	/**
6765	 * Returns the number of registered plugins?
6766	 * @returns {Number}
6767	 * @since 2.1.5
6768	 */
6769	count: function() {
6770		return this._plugins.length;
6771	},
6772
6773	/**
6774	 * Returns all registered plugin instances.
6775	 * @returns {Array} array of plugin objects.
6776	 * @since 2.1.5
6777	 */
6778	getAll: function() {
6779		return this._plugins;
6780	},
6781
6782	/**
6783	 * Calls enabled plugins for `chart` on the specified hook and with the given args.
6784	 * This method immediately returns as soon as a plugin explicitly returns false. The
6785	 * returned value can be used, for instance, to interrupt the current action.
6786	 * @param {Object} chart - The chart instance for which plugins should be called.
6787	 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
6788	 * @param {Array} [args] - Extra arguments to apply to the hook call.
6789	 * @returns {Boolean} false if any of the plugins return false, else returns true.
6790	 */
6791	notify: function(chart, hook, args) {
6792		var descriptors = this.descriptors(chart);
6793		var ilen = descriptors.length;
6794		var i, descriptor, plugin, params, method;
6795
6796		for (i = 0; i < ilen; ++i) {
6797			descriptor = descriptors[i];
6798			plugin = descriptor.plugin;
6799			method = plugin[hook];
6800			if (typeof method === 'function') {
6801				params = [chart].concat(args || []);
6802				params.push(descriptor.options);
6803				if (method.apply(plugin, params) === false) {
6804					return false;
6805				}
6806			}
6807		}
6808
6809		return true;
6810	},
6811
6812	/**
6813	 * Returns descriptors of enabled plugins for the given chart.
6814	 * @returns {Array} [{ plugin, options }]
6815	 * @private
6816	 */
6817	descriptors: function(chart) {
6818		var cache = chart.$plugins || (chart.$plugins = {});
6819		if (cache.id === this._cacheId) {
6820			return cache.descriptors;
6821		}
6822
6823		var plugins = [];
6824		var descriptors = [];
6825		var config = (chart && chart.config) || {};
6826		var options = (config.options && config.options.plugins) || {};
6827
vendor: 9,640 bytes, lines 6828-7056
6828		this._plugins.concat(config.plugins || []).forEach(function(plugin) {
6829			var idx = plugins.indexOf(plugin);
6830			if (idx !== -1) {
6831				return;
6832			}
6833
6834			var id = plugin.id;
6835			var opts = options[id];
6836			if (opts === false) {
6837				return;
6838			}
6839
6840			if (opts === true) {
6841				opts = helpers.clone(defaults.global.plugins[id]);
6842			}
6843
6844			plugins.push(plugin);
6845			descriptors.push({
6846				plugin: plugin,
6847				options: opts || {}
6848			});
6849		});
6850
6851		cache.descriptors = descriptors;
6852		cache.id = this._cacheId;
6853		return descriptors;
6854	},
6855
6856	/**
6857	 * Invalidates cache for the given chart: descriptors hold a reference on plugin option,
6858	 * but in some cases, this reference can be changed by the user when updating options.
6859	 * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
6860	 * @private
6861	 */
6862	_invalidate: function(chart) {
6863		delete chart.$plugins;
6864	}
6865};
6866
6867/**
6868 * Plugin extension hooks.
6869 * @interface IPlugin
6870 * @since 2.1.0
6871 */
6872/**
6873 * @method IPlugin#beforeInit
6874 * @desc Called before initializing `chart`.
6875 * @param {Chart.Controller} chart - The chart instance.
6876 * @param {Object} options - The plugin options.
6877 */
6878/**
6879 * @method IPlugin#afterInit
6880 * @desc Called after `chart` has been initialized and before the first update.
6881 * @param {Chart.Controller} chart - The chart instance.
6882 * @param {Object} options - The plugin options.
6883 */
6884/**
6885 * @method IPlugin#beforeUpdate
6886 * @desc Called before updating `chart`. If any plugin returns `false`, the update
6887 * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
6888 * @param {Chart.Controller} chart - The chart instance.
6889 * @param {Object} options - The plugin options.
6890 * @returns {Boolean} `false` to cancel the chart update.
6891 */
6892/**
6893 * @method IPlugin#afterUpdate
6894 * @desc Called after `chart` has been updated and before rendering. Note that this
6895 * hook will not be called if the chart update has been previously cancelled.
6896 * @param {Chart.Controller} chart - The chart instance.
6897 * @param {Object} options - The plugin options.
6898 */
6899/**
6900 * @method IPlugin#beforeDatasetsUpdate
6901 * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
6902 * the datasets update is cancelled until another `update` is triggered.
6903 * @param {Chart.Controller} chart - The chart instance.
6904 * @param {Object} options - The plugin options.
6905 * @returns {Boolean} false to cancel the datasets update.
6906 * @since version 2.1.5
6907*/
6908/**
6909 * @method IPlugin#afterDatasetsUpdate
6910 * @desc Called after the `chart` datasets have been updated. Note that this hook
6911 * will not be called if the datasets update has been previously cancelled.
6912 * @param {Chart.Controller} chart - The chart instance.
6913 * @param {Object} options - The plugin options.
6914 * @since version 2.1.5
6915 */
6916/**
6917 * @method IPlugin#beforeDatasetUpdate
6918 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
6919 * returns `false`, the datasets update is cancelled until another `update` is triggered.
6920 * @param {Chart} chart - The chart instance.
6921 * @param {Object} args - The call arguments.
6922 * @param {Number} args.index - The dataset index.
6923 * @param {Object} args.meta - The dataset metadata.
6924 * @param {Object} options - The plugin options.
6925 * @returns {Boolean} `false` to cancel the chart datasets drawing.
6926 */
6927/**
6928 * @method IPlugin#afterDatasetUpdate
6929 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
6930 * that this hook will not be called if the datasets update has been previously cancelled.
6931 * @param {Chart} chart - The chart instance.
6932 * @param {Object} args - The call arguments.
6933 * @param {Number} args.index - The dataset index.
6934 * @param {Object} args.meta - The dataset metadata.
6935 * @param {Object} options - The plugin options.
6936 */
6937/**
6938 * @method IPlugin#beforeLayout
6939 * @desc Called before laying out `chart`. If any plugin returns `false`,
6940 * the layout update is cancelled until another `update` is triggered.
6941 * @param {Chart.Controller} chart - The chart instance.
6942 * @param {Object} options - The plugin options.
6943 * @returns {Boolean} `false` to cancel the chart layout.
6944 */
6945/**
6946 * @method IPlugin#afterLayout
6947 * @desc Called after the `chart` has been layed out. Note that this hook will not
6948 * be called if the layout update has been previously cancelled.
6949 * @param {Chart.Controller} chart - The chart instance.
6950 * @param {Object} options - The plugin options.
6951 */
6952/**
6953 * @method IPlugin#beforeRender
6954 * @desc Called before rendering `chart`. If any plugin returns `false`,
6955 * the rendering is cancelled until another `render` is triggered.
6956 * @param {Chart.Controller} chart - The chart instance.
6957 * @param {Object} options - The plugin options.
6958 * @returns {Boolean} `false` to cancel the chart rendering.
6959 */
6960/**
6961 * @method IPlugin#afterRender
6962 * @desc Called after the `chart` has been fully rendered (and animation completed). Note
6963 * that this hook will not be called if the rendering has been previously cancelled.
6964 * @param {Chart.Controller} chart - The chart instance.
6965 * @param {Object} options - The plugin options.
6966 */
6967/**
6968 * @method IPlugin#beforeDraw
6969 * @desc Called before drawing `chart` at every animation frame specified by the given
6970 * easing value. If any plugin returns `false`, the frame drawing is cancelled until
6971 * another `render` is triggered.
6972 * @param {Chart.Controller} chart - The chart instance.
6973 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6974 * @param {Object} options - The plugin options.
6975 * @returns {Boolean} `false` to cancel the chart drawing.
6976 */
6977/**
6978 * @method IPlugin#afterDraw
6979 * @desc Called after the `chart` has been drawn for the specific easing value. Note
6980 * that this hook will not be called if the drawing has been previously cancelled.
6981 * @param {Chart.Controller} chart - The chart instance.
6982 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6983 * @param {Object} options - The plugin options.
6984 */
6985/**
6986 * @method IPlugin#beforeDatasetsDraw
6987 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
6988 * the datasets drawing is cancelled until another `render` is triggered.
6989 * @param {Chart.Controller} chart - The chart instance.
6990 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6991 * @param {Object} options - The plugin options.
6992 * @returns {Boolean} `false` to cancel the chart datasets drawing.
6993 */
6994/**
6995 * @method IPlugin#afterDatasetsDraw
6996 * @desc Called after the `chart` datasets have been drawn. Note that this hook
6997 * will not be called if the datasets drawing has been previously cancelled.
6998 * @param {Chart.Controller} chart - The chart instance.
6999 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7000 * @param {Object} options - The plugin options.
7001 */
7002/**
7003 * @method IPlugin#beforeDatasetDraw
7004 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
7005 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
7006 * is cancelled until another `render` is triggered.
7007 * @param {Chart} chart - The chart instance.
7008 * @param {Object} args - The call arguments.
7009 * @param {Number} args.index - The dataset index.
7010 * @param {Object} args.meta - The dataset metadata.
7011 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7012 * @param {Object} options - The plugin options.
7013 * @returns {Boolean} `false` to cancel the chart datasets drawing.
7014 */
7015/**
7016 * @method IPlugin#afterDatasetDraw
7017 * @desc Called after the `chart` datasets at the given `args.index` have been drawn
7018 * (datasets are drawn in the reverse order). Note that this hook will not be called
7019 * if the datasets drawing has been previously cancelled.
7020 * @param {Chart} chart - The chart instance.
7021 * @param {Object} args - The call arguments.
7022 * @param {Number} args.index - The dataset index.
7023 * @param {Object} args.meta - The dataset metadata.
7024 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7025 * @param {Object} options - The plugin options.
7026 */
7027/**
7028 * @method IPlugin#beforeTooltipDraw
7029 * @desc Called before drawing the `tooltip`. If any plugin returns `false`,
7030 * the tooltip drawing is cancelled until another `render` is triggered.
7031 * @param {Chart} chart - The chart instance.
7032 * @param {Object} args - The call arguments.
7033 * @param {Object} args.tooltip - The tooltip.
7034 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7035 * @param {Object} options - The plugin options.
7036 * @returns {Boolean} `false` to cancel the chart tooltip drawing.
7037 */
7038/**
7039 * @method IPlugin#afterTooltipDraw
7040 * @desc Called after drawing the `tooltip`. Note that this hook will not
7041 * be called if the tooltip drawing has been previously cancelled.
7042 * @param {Chart} chart - The chart instance.
7043 * @param {Object} args - The call arguments.
7044 * @param {Object} args.tooltip - The tooltip.
7045 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7046 * @param {Object} options - The plugin options.
7047 */
7048/**
7049 * @method IPlugin#beforeEvent
7050 * @desc Called before processing the specified `event`. If any plugin returns `false`,
7051 * the event will be discarded.
7052 * @param {Chart.Controller} chart - The chart instance.
7053 * @param {IEvent} event - The event object.
7054 * @param {Object} options - The plugin options.
7055 */
7056/**
7057 * @method IPlugin#afterEvent
7058 * @desc Called after the `event` has been consumed. Note that this hook
7059 * will not be called if the `event` has been previously discarded.
7060 * @param {Chart.Controller} chart - The chart instance.
7061 * @param {IEvent} event - The event object.
7062 * @param {Object} options - The plugin options.
7063 */
7064/**
7065 * @method IPlugin#resize
7066 * @desc Called after the chart as been resized.
7067 * @param {Chart.Controller} chart - The chart instance.
7068 * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
7069 * @param {Object} options - The plugin options.
7070 */
7071/**
7072 * @method IPlugin#destroy
7073 * @desc Called after the chart as been destroyed.
7074 * @param {Chart.Controller} chart - The chart instance.
7075 * @param {Object} options - The plugin options.
7076 */
7077
7078},{"26":26,"46":46}],33:[function(require,module,exports){
7079'use strict';
7080
7081var defaults = require(26);
7082var Element = require(27);
7083var helpers = require(46);
7084var Ticks = require(35);
7085
7086defaults._set('scale', {
7087	display: true,
7088	position: 'left',
7089	offset: false,
7090
7091	// grid line settings
7092	gridLines: {
7093		display: true,
7094		color: 'rgba(0, 0, 0, 0.1)',
7095		lineWidth: 1,
7096		drawBorder: true,
7097		drawOnChartArea: true,
7098		drawTicks: true,
7099		tickMarkLength: 10,
7100		zeroLineWidth: 1,
7101		zeroLineColor: 'rgba(0,0,0,0.25)',
7102		zeroLineBorderDash: [],
7103		zeroLineBorderDashOffset: 0.0,
7104		offsetGridLines: false,
7105		borderDash: [],
7106		borderDashOffset: 0.0
7107	},
7108
7109	// scale label
7110	scaleLabel: {
7111		// display property
7112		display: false,
7113
7114		// actual label
7115		labelString: '',
7116
7117		// line height
7118		lineHeight: 1.2,
7119
7120		// top/bottom padding
7121		padding: {
7122			top: 4,
7123			bottom: 4
7124		}
7125	},
7126
7127	// label settings
7128	ticks: {
7129		beginAtZero: false,
7130		minRotation: 0,
7131		maxRotation: 50,
7132		mirror: false,
7133		padding: 0,
7134		reverse: false,
7135		display: true,
7136		autoSkip: true,
7137		autoSkipPadding: 0,
7138		labelOffset: 0,
7139		// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
7140		callback: Ticks.formatters.values,
7141		minor: {},
7142		major: {}
7143	}
7144});
7145
7146function labelsFromTicks(ticks) {
7147	var labels = [];
7148	var i, ilen;
7149
7150	for (i = 0, ilen = ticks.length; i < ilen; ++i) {
7151		labels.push(ticks[i].label);
7152	}
7153
7154	return labels;
7155}
7156
7157function getLineValue(scale, index, offsetGridLines) {
7158	var lineValue = scale.getPixelForTick(index);
7159
7160	if (offsetGridLines) {
7161		if (index === 0) {
7162			lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
7163		} else {
7164			lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
7165		}
7166	}
7167	return lineValue;
7168}
7169
7170function computeTextSize(context, tick, font) {
7171	return helpers.isArray(tick) ?
7172		helpers.longestText(context, font, tick) :
7173		context.measureText(tick).width;
7174}
7175
7176function parseFontOptions(options) {
7177	var valueOrDefault = helpers.valueOrDefault;
7178	var globalDefaults = defaults.global;
7179	var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
7180	var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
7181	var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
7182
7183	return {
7184		size: size,
7185		style: style,
7186		family: family,
7187		font: helpers.fontString(size, style, family)
7188	};
7189}
7190
7191function parseLineHeight(options) {
7192	return helpers.options.toLineHeight(
7193		helpers.valueOrDefault(options.lineHeight, 1.2),
7194		helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
7195}
7196
7197module.exports = Element.extend({
7198	/**
7199	 * Get the padding needed for the scale
7200	 * @method getPadding
7201	 * @private
7202	 * @returns {Padding} the necessary padding
7203	 */
7204	getPadding: function() {
7205		var me = this;
7206		return {
7207			left: me.paddingLeft || 0,
7208			top: me.paddingTop || 0,
7209			right: me.paddingRight || 0,
7210			bottom: me.paddingBottom || 0
7211		};
7212	},
7213
7214	/**
7215	 * Returns the scale tick objects ({label, major})
7216	 * @since 2.7
7217	 */
7218	getTicks: function() {
7219		return this._ticks;
7220	},
7221
7222	// These methods are ordered by lifecyle. Utilities then follow.
7223	// Any function defined here is inherited by all scale types.
7224	// Any function can be extended by the scale type
7225
7226	mergeTicksOptions: function() {
7227		var ticks = this.options.ticks;
7228		if (ticks.minor === false) {
7229			ticks.minor = {
7230				display: false
7231			};
7232		}
7233		if (ticks.major === false) {
7234			ticks.major = {
7235				display: false
7236			};
7237		}
7238		for (var key in ticks) {
7239			if (key !== 'major' && key !== 'minor') {
7240				if (typeof ticks.minor[key] === 'undefined') {
7241					ticks.minor[key] = ticks[key];
7242				}
7243				if (typeof ticks.major[key] === 'undefined') {
7244					ticks.major[key] = ticks[key];
7245				}
7246			}
7247		}
7248	},
7249	beforeUpdate: function() {
7250		helpers.callback(this.options.beforeUpdate, [this]);
7251	},
7252
7253	update: function(maxWidth, maxHeight, margins) {
7254		var me = this;
7255		var i, ilen, labels, label, ticks, tick;
7256
7257		// Update Lifecycle - Probably don't want to ever extend or over
7257write this function ;)
7258		me.beforeUpdate();
7259
7260		// Absorb the master measurements
7261		me.maxWidth = maxWidth;
7262		me.maxHeight = maxHeight;
7263		me.margins = helpers.extend({
7264			left: 0,
7265			right: 0,
7266			top: 0,
7267			bottom: 0
7268		}, margins);
7269		me.longestTextCache = me.longestTextCache || {};
7270
7271		// Dimensions
7272		me.beforeSetDimensions();
7273		me.setDimensions();
7274		me.afterSetDimensions();
7275
7276		// Data min/max
7277		me.beforeDataLimits();
7278		me.determineDataLimits();
7279		me.afterDataLimits();
7280
7281		// Ticks - `this.ticks` is now DEPRECATED!
7282		// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
7283		// and must not be accessed directly from outside this class. `this.ticks` being
7284		// around for long time and not marked as private, we can't change its structure
7285		// without unexpected breaking changes. If you need to access the scale ticks,
7286		// use scale.getTicks() instead.
7287
7288		me.beforeBuildTicks();
7289
7290		// New implementations should return an array of objects but for BACKWARD COMPAT,
7291		// we still support no return (`this.ticks` internally set by calling this method).
7292		ticks = me.buildTicks() || [];
7293
7294		me.afterBuildTicks();
7295
7296		me.beforeTickToLabelConversion();
7297
7298		// New implementations should return the formatted tick labels but for BACKWARD
7299		// COMPAT, we still support no return (`this.ticks` internally changed by calling
7300		// this method and supposed to contain only string values).
7301		labels = me.convertTicksToLabels(ticks) || me.ticks;
7302
7303		me.afterTickToLabelConversion();
7304
7305		me.ticks = labels;   // BACKWARD COMPATIBILITY
7306
7307		// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!
7308
7309		// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
7310		for (i = 0, ilen = labels.length; i < ilen; ++i) {
7311			label = labels[i];
7312			tick = ticks[i];
7313			if (!tick) {
7314				ticks.push(tick = {
7315					label: label,
7316					major: false
7317				});
7318			} else {
7319				tick.label = label;
7320			}
7321		}
7322
7323		me._ticks = ticks;
7324
7325		// Tick Rotation
7326		me.beforeCalculateTickRotation();
7327		me.calculateTickRotation();
7328		me.afterCalculateTickRotation();
7329		// Fit
7330		me.beforeFit();
7331		me.fit();
7332		me.afterFit();
7333		//
7334		me.afterUpdate();
7335
7336		return me.minSize;
7337
7338	},
7339	afterUpdate: function() {
7340		helpers.callback(this.options.afterUpdate, [this]);
7341	},
7342
7343	//
7344
7345	beforeSetDimensions: function() {
7346		helpers.callback(this.options.beforeSetDimensions, [this]);
7347	},
7348	setDimensions: function() {
7349		var me = this;
7350		// Set the unconstrained dimension before label rotation
7351		if (me.isHorizontal()) {
7352			// Reset position before calculating rotation
7353			me.width = me.maxWidth;
7354			me.left = 0;
7355			me.right = me.width;
7356		} else {
7357			me.height = me.maxHeight;
7358
7359			// Reset position before calculating rotation
7360			me.top = 0;
7361			me.bottom = me.height;
7362		}
7363
7364		// Reset padding
7365		me.paddingLeft = 0;
7366		me.paddingTop = 0;
7367		me.paddingRight = 0;
7368		me.paddingBottom = 0;
7369	},
7370	afterSetDimensions: function() {
7371		helpers.callback(this.options.afterSetDimensions, [this]);
7372	},
7373
7374	// Data limits
7375	beforeDataLimits: function() {
7376		helpers.callback(this.options.beforeDataLimits, [this]);
7377	},
7378	determineDataLimits: helpers.noop,
7379	afterDataLimits: function() {
7380		helpers.callback(this.options.afterDataLimits, [this]);
7381	},
7382
7383	//
7384	beforeBuildTicks: function() {
7385		helpers.callback(this.options.beforeBuildTicks, [this]);
7386	},
7387	buildTicks: helpers.noop,
7388	afterBuildTicks: function() {
7389		helpers.callback(this.options.afterBuildTicks, [this]);
7390	},
7391
7392	beforeTickToLabelConversion: function() {
7393		helpers.callback(this.options.beforeTickToLabelConversion, [this]);
7394	},
7395	convertTicksToLabels: function() {
7396		var me = this;
7397		// Convert ticks to strings
7398		var tickOpts = me.options.ticks;
7399		me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
7400	},
7401	afterTickToLabelConversion: function() {
7402		helpers.callback(this.options.afterTickToLabelConversion, [this]);
7403	},
7404
7405	//
7406
7407	beforeCalculateTickRotation: function() {
7408		helpers.callback(this.options.beforeCalculateTickRotation, [this]);
7409	},
7410	calculateTickRotation: function() {
7411		var me = this;
7412		var context = me.ctx;
7413		var tickOpts = me.options.ticks;
7414		var labels = labelsFromTicks(me._ticks);
7415
7416		// Get the width of each grid by calculating the difference
7417		// between x offsets between 0 and 1.
7418		var tickFont = parseFontOptions(tickOpts);
7419		context.font = tickFont.font;
7420
7421		var labelRotation = tickOpts.minRotation || 0;
7422
7423		if (labels.length && me.options.display && me.isHorizontal()) {
7424			var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
7425			var labelWidth = originalLabelWidth;
7426			var cosRotation, sinRotation;
7427
7428			// Allow 3 pixels x2 padding either side for label readability
7429			var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
7430
7431			// Max label rotation can be set or default to 90 - also act as a loop counter
7432			while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
7433				var angleRadians = helpers.toRadians(labelRotation);
7434				cosRotation = Math.cos(angleRadians);
7435				sinRotation = Math.sin(angleRadians);
7436
7437				if (sinRotation * originalLabelWidth > me.maxHeight) {
7438					// go back one step
7439					labelRotation--;
7440					break;
7441				}
7442
7443				labelRotation++;
7444				labelWidth = cosRotation * originalLabelWidth;
7445			}
7446		}
7447
7448		me.labelRotation = labelRotation;
7449	},
7450	afterCalculateTickRotation: function() {
7451		helpers.callback(this.options.afterCalculateTickRotation, [this]);
7452	},
7453
7454	//
7455
7456	beforeFit: function() {
7457		helpers.callback(this.options.beforeFit, [this]);
7458	},
7459	fit: function() {
7460		var me = this;
7461		// Reset
7462		var minSize = me.minSize = {
7463			width: 0,
7464			height: 0
7465		};
7466
7467		var labels = labelsFromTicks(me._ticks);
7468
7469		var opts = me.options;
7470		var tickOpts = opts.ticks;
7471		var scaleLabelOpts = opts.scaleLabel;
7472		var gridLineOpts = opts.gridLines;
7473		var display = opts.display;
7474		var isHorizontal = me.isHorizontal();
7475
7476		var tickFont = parseFontOptions(tickOpts);
7477		var tickMarkLength = opts.gridLines.tickMarkLength;
7478
7479		// Width
7480		if (isHorizontal) {
7481			// subtract the margins to line up with the chartArea if we are a full width scale
7482			minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
7483		} else {
7484			minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7485		}
7486
7487		// height
7488		if (isHorizontal) {
7489			minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7490		} else {
7491			minSize.height = me.maxHeight; // fill all the height
7492		}
7493
7494		// Are we showing a title for the scale?
7495		if (scaleLabelOpts.display && display) {
7496			var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
7497			var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
7498			var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;
7499
7500			if (isHorizontal) {
7501				minSize.height += deltaHeight;
7502			} else {
7503				minSize.width += deltaHeight;
7504			}
7505		}
7506
7507		// Don't bother fitting the ticks if we are not showing them
7508		if (tickOpts.display && display) {
7509			var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
7510			var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
7511			var lineSpace = tickFont.size * 0.5;
7512			var tickPadding = me.options.ticks.padding;
7513
7514			if (isHorizontal) {
7515				// A horizontal axis is more constrained by the height.
7516				me.longestLabelWidth = largestTextWidth;
7517
7518				var angleRadians = helpers.toRadians(me.labelRotation);
7519				var cosRotation = Math.cos(angleRadians);
7520				var sinRotation = Math.sin(angleRadians);
7521
7522				// TODO - improve this calculation
7523				var labelHeight = (sinRotation * largestTextWidth)
7524					+ (tickFont.size * tallestLabelHeightInLines)
7525					+ (lineSpace * (tallestLabelHeightInLines - 1))
7526					+ lineSpace; // padding
7527
7528				minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
7529
7530				me.ctx.font = tickFont.font;
7531				var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
7532				var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);
7533
7534				// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
7535				// which means that the right padding is dominated by the font height
7536				if (me.labelRotation !== 0) {
7537					me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
7538					me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
7539				} else {
7540					me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
7541					me.paddingRight = lastLabelWidth / 2 + 3;
7542				}
7543			} else {
7544				// A vertical axis is more constrained by the width. Labels are the
7545				// dominant factor here, so get that length first and account for padding
7546				if (tickOpts.mirror) {
7547					largestTextWidth = 0;
7548				} else {
7549					// use lineSpace for consistency with horizontal axis
7550					// tickPadding is not implemented for horizontal
7551					largestTextWidth += tickPadding + lineSpace;
7552				}
7553
7554				minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);
7555
7556				me.paddingTop = tickFont.size / 2;
7557				me.paddingBottom = tickFont.size / 2;
7558			}
7559		}
7560
7561		me.handleMargins();
7562
7563		me.width = minSize.width;
7564		me.height = minSize.height;
7565	},
7566
7567	/**
7568	 * Handle margins and padding interactions
7569	 * @private
7570	 */
7571	handleMargins: function() {
7572		var me = this;
7573		if (me.margins) {
7574			me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
7575			me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
7576			me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
7577			me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
7578		}
7579	},
7580
7581	afterFit: function() {
7582		helpers.callback(this.options.afterFit, [this]);
7583	},
7584
7585	// Shared Methods
7586	isHorizontal: function() {
7587		return this.options.position === 'top' || this.options.position === 'bottom';
7588	},
7589	isFullWidth: function() {
7590		return (this.options.fullWidth);
7591	},
7592
7593	// 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
7594	getRightValue: function(rawValue) {
7595		// Null and undefined values first
7596		if (helpers.isNullOrUndef(rawValue)) {
7597			return NaN;
7598		}
7599		// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
7600		if (typeof rawValue === 'number' && !isFinite(rawValue)) {
7601			return NaN;
7602		}
7603		// If it is in fact an object, dive in one more level
7604		if (rawValue) {
7605			if (this.isHorizontal()) {
7606				if (rawValue.x !== undefined) {
7607					return this.getRightValue(rawValue.x);
7608				}
7609			} else if (rawValue.y !== undefined) {
7610				return this.getRightValue(rawValue.y);
7611			}
7612		}
7613
7614		// Value is good, return it
7615		return rawValue;
7616	},
7617
7618	/**
7619	 * Used to get the value to display in the tooltip for the data at the given index
7620	 * @param index
7621	 * @param datasetIndex
7622	 */
7623	getLabelForIndex: helpers.noop,
7624
7625	/**
7626	 * Returns the location of the given data point. Value can either be an index or a numerical value
7627	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7628	 * @param value
7629	 * @param index
7630	 * @param datasetIndex
7631	 */
7632	getPixelForValue: helpers.noop,
7633
7634	/**
7635	 * Used to get the data value from a given pixel. This is the inverse of getPixelForValue
7636	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7637	 * @param pixel
7638	 */
7639	getValueForPixel: helpers.noop,
7640
7641	/**
7642	 * Returns the location of the tick at the given index
7643	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7644	 */
7645	getPixelForTick: function(index) {
7646		var me = this;
7647		var offset = me.options.offset;
7648		if (me.isHorizontal()) {
7649			var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7650			var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
7651			var pixel = (tickWidth * index) + me.paddingLeft;
7652
7653			if (offset) {
7654				pixel += tickWidth / 2;
7655			}
7656
7657			var finalVal = me.left + Math.round(pixel);
7658			finalVal += me.isFullWidth() ? me.margins.left : 0;
7659			return finalVal;
7660		}
7661		var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
7662		return me.top + (index * (innerHeight / (me._ticks.length - 1)));
7663	},
7664
7665	/**
7666	 * Utility for getting the pixel location of a percentage of scale
7667	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7668	 */
7669	getPixelForDecimal: function(decimal) {
7670		var me = this;
7671		if (me.isHorizontal()) {
7672			var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7673			var valueOffset = (innerWidth * decimal) + me.paddingLeft;
7674
7675			var finalVal = me.left + Math.round(valueOffset);
7676			finalVal += me.isFullWidth() ? me.margins.left : 0;
7677			return finalVal;
7678		}
7679		return me.top + (decimal * me.height);
7680	},
7681
7682	/**
7683	 * Returns the pixel for the minimum chart value
7684	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7685	 */
7686	getBasePixel: function() {
7687		return this.getPixelForValue(this.getBaseValue());
7688	},
7689
7690	getBaseValue: function() {
7691		var me = this;
7692		var min = me.min;
7693		var max = me.max;
7694
7695		return me.beginAtZero ? 0 :
7696			min < 0 && max < 0 ? max :
7697			min > 0 && max > 0 ? min :
7698			0;
7699	},
7700
7701	/**
7702	 * Returns a subset of ticks to be plotted to avoid overlapping labels.
7703	 * @private
7704	 */
7705	_autoSkip: function(ticks) {
7706		var skipRatio;
7707		var me = this;
7708		var isHorizontal = me.isHorizontal();
7709		var optionTicks = me.options.ticks.minor;
7710		var tickCount = ticks.length;
7711		var labelRotationRadians = helpers.toRadians(me.labelRotation);
7712		var cosRotation = Math.cos(labelRotationRadians);
7713		var longestRotatedLabel = me.longestLabelWidth * cosRotation;
7714		var result = [];
7715		var i, tick, shouldSkip;
7716
7717		// figure out the maximum number of gridlines to show
7718		var maxTicks;
7719		if (optionTicks.maxTicksLimit) {
7720			maxTicks = optionTicks.maxTicksLimit;
7721		}
7722
7723		if (isHorizontal) {
7724			skipRatio = false;
7725
7726			if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
7727				skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
7728			}
7729
7730			// if they defined a max number of optionTicks,
7731			// increase skipRatio until that number is met
7732			if (maxTicks && tickCount > maxTicks) {
7733				skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
7734			}
7735		}
7736
7737		for (i = 0; i < tickCount; i++) {
7738			tick = ticks[i];
7739
7740			// Since we always show the last tick,we need may need to hide the last shown one before
7741			shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
7742			if (shouldSkip && i !== tickCount - 1) {
7743				// leave tick in place but make sure it's not displayed (#4635)
7744				delete tick.label;
7745			}
7746			result.push(tick);
7747		}
7748		return result;
7749	},
7750
7751	// Actually draw the scale on the canvas
7752	// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
7753	draw: function(chartArea) {
7754		var me = this;
7755		var options = me.options;
7756		if (!options.display) {
7757			return;
7758		}
7759
7760		var context = me.ctx;
7761		var globalDefaults = defaults.global;
7762		var optionTicks = options.ticks.minor;
7763		var optionMajorTicks = options.ticks.major || optionTicks;
7764		var gridLines = options.gridLines;
7765		var scaleLabel = options.scaleLabel;
7766
7767		var isRotated = me.labelRotation !== 0;
7768		var isHorizontal = me.isHorizontal();
7769
7770		var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
7771		var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
7772		var tickFont = parseFontOptions(optionTicks);
7773		var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
7774		var majorTickFont = parseFontOptions(optionMajorTicks);
7775
7776		var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
7777
7778		var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
7779		var scaleLabelFont = parseFontOptions(scaleLabel);
7780		var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
7781		var labelRotationRadians = helpers.toRadians(me.labelRotation);
7782
7783		var itemsToDraw = [];
7784
7785		var axisWidth = me.options.gridLines.lineWidth;
7786		var xTickStart = options.position === 'right' ? me.left : me.right - axisWidth - tl;
7787		var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
7788		var yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth;
7789		var yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth;
7790
7791		helpers.each(ticks, function(tick, index) {
7792			// autoskipper skipped this tick (#4635)
7793			if (helpers.isNullOrUndef(tick.label)) {
7794				return;
7795			}
7796
7797			var label = tick.label;
7798			var lineWidth, lineColor, borderDash, borderDashOffset;
7799			if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
7800				// Draw the first index specially
7801				lineWidth = gridLines.zeroLineWidth;
7802				lineColor = gridLines.zeroLineColor;
7803				borderDash = gridLines.zeroLineBorderDash;
7804				borderDashOffset = gridLines.zeroLineBorderDashOffset;
7805			} else {
7806				lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
7807				lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
7808				borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
7809				borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
7810			}
7811
7812			// Common properties
7813			var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
7814			var textAlign = 'middle';
7815			var textBaseline = 'middle';
7816			var tickPadding = optionTicks.padding;
7817
7818			if (isHorizontal) {
7819				var labelYOffset = tl + tickPadding;
7820
7821				if (options.position === 'bottom') {
7822					// bottom
7823					textBaseline = !isRotated ? 'top' : 'middle';
7824					textAlign = !isRotated ? 'center' : 'right';
7825					labelY = me.top + labelYOffset;
7826				} else {
7827					// top
7828					textBaseline = !isRotated ? 'bottom' : 'middle';
7829					textAlign = !isRotated ? 'center' : 'left';
7830					labelY = me.bottom - labelYOffset;
7831				}
7832
7833				var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
7834				if (xLineValue < me.left) {
7835					lineColor = 'rgba(0,0,0,0)';
7836				}
7837				xLineValue += helpers.aliasPixel(lineWidth);
7838
7839				labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
7840
7841				tx1 = tx2 = x1 = x2 = xLineValue;
7842				ty1 = yTickStart;
7843				ty2 = yTickEnd;
7844				y1 = chartArea.top;
7845				y2 = chartArea.bottom + axisWidth;
7846			} else {
7847				var isLeft = options.position === 'left';
7848				var labelXOffset;
7849
7850				if (optionTicks.mirror) {
7851					textAlign = isLeft ? 'left' : 'right';
7852					labelXOffset = tickPadding;
7853				} else {
7854					textAlign = isLeft ? 'right' : 'left';
7855					labelXOffset = tl + tickPadding;
7856				}
7857
7858				labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
7859
7860				var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
7861				if (yLineValue < me.top) {
7862					lineColor = 'rgba(0,0,0,0)';
7863				}
7864				yLineValue += helpers.aliasPixel(lineWidth);
7865
7866				labelY = me.getPixelForTick(index) + optionTicks.labelOffset;
7867
7868				tx1 = xTickStart;
7869				tx2 = xTickEnd;
7870				x1 = chartArea.left;
7871				x2 = chartArea.right + axisWidth;
7872				ty1 = ty2 = y1 = y2 = yLineValue;
7873			}
7874
7875			itemsToDraw.push({
7876				tx1: tx1,
7877				ty1: ty1,
7878				tx2: tx2,
7879				ty2: ty2,
7880				x1: x1,
7881				y1: y1,
7882				x2: x2,
7883				y2: y2,
7884				labelX: labelX,
7885				labelY: labelY,
7886				glWidth: lineWidth,
7887				glColor: lineColor,
7888				glBorderDash: borderDash,
7889				glBorderDashOffset: borderDashOffset,
7890				rotation: -1 * labelRotationRadians,
7891				label: label,
7892				major: tick.major,
7893				textBaseline: textBaseline,
7894				textAlign: textAlign
7895			});
7896		});
7897
7898		// Draw all of the tick labels, tick marks, and grid lines at the correct places
7899		helpers.each(itemsToDraw, function(itemToDraw) {
7900			if (gridLines.display) {
7901				context.save();
7902				context.lineWidth = itemToDraw.glWidth;
7903				context.strokeStyle = itemToDraw.glColor;
7904				if (context.setLineDash) {
7905					context.setLineDash(itemToDraw.glBorderDash);
7906					context.lineDashOffset = itemToDraw.glBorderDashOffset;
7907				}
7908
7909				context.beginPath();
7910
7911				if (gridLines.drawTicks) {
7912					context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
7913					context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
7914				}
7915
7916				if (gridLines.drawOnChartArea) {
7917					context.moveTo(itemToDraw.x1, itemToDraw.y1);
7918					context.lineTo(itemToDraw.x2, itemToDraw.y2);
7919				}
7920
7921				context.stroke();
7922				context.restore();
7923			}
7924
7925			if (optionTicks.display) {
7926				// Make sure we draw text in the correct color and font
7927				context.save();
7928				context.translate(itemToDraw.labelX, itemToDraw.labelY);
7929				context.rotate(itemToDraw.rotation);
7930				context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
7931				context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
7932				context.textBaseline = itemToDraw.textBaseline;
7933				context.textAlign = itemToDraw.textAlign;
7934
7935				var label = itemToDraw.label;
7936				if (helpers.isArray(label)) {
7937					var lineCount = label.length;
7938					var lineHeight = tickFont.size * 1.5;
7939					var y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2;
7940
7941					for (var i = 0; i < lineCount; ++i) {
7942						// We just make sure the multiline element is a string here..
7943						context.fillText('' + label[i], 0, y);
7944						// apply same lineSpacing as calculated @ L#320
7945						y += lineHeight;
7946					}
7947				} else {
7948					context.fillText(label, 0, 0);
7949				}
7950				context.restore();
7951			}
7952		});
7953
7954		if (scaleLabel.display) {
7955			// Draw the scale label
7956			var scaleLabelX;
7957			var scaleLabelY;
7958			var rotation = 0;
7959			var halfLineHeight = parseLineHeight(scaleLabel) / 2;
7960
7961			if (isHorizontal) {
7962				scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
7963				scaleLabelY = options.position === 'bottom'
7964					? me.bottom - halfLineHeight - scaleLabelPadding.bottom
7965					: me.top + halfLineHeight + scaleLabelPadding.top;
7966			} else {
7967				var isLeft = options.position === 'left';
7968				scaleLabelX = isLeft
7969					? me.left + halfLineHeight + scaleLabelPadding.top
7970					: me.right - halfLineHeight - scaleLabelPadding.top;
7971				scaleLabelY = me.top + ((me.bottom - me.top) / 2);
7972				rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
7973			}
7974
7975			context.save();
7976			context.translate(scaleLabelX, scaleLabelY);
7977			context.rotate(rotation);
7978			context.textAlign = 'center';
7979			context.textBaseline = 'middle';
7980			context.fillStyle = scaleLabelFontColor; // render in correct colour
7981			context.font = scaleLabelFont.font;
7982			context.fillText(scaleLabel.labelString, 0, 0);
7983			context.restore();
7984		}
7985
7986		if (gridLines.drawBorder) {
7987			// Draw the line at the edge of the axis
7988			context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
7989			context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
7990			var x1 = me.left;
7991			var x2 = me.right + axisWidth;
7992			var y1 = me.top;
7993			var y2 = me.bottom + axisWidth;
7994
7995			var aliasPixel = helpers.aliasPixel(context.lineWidth);
7996			if (isHorizontal) {
7997				y1 = y2 = options.position === 'top' ? me.bottom : me.top;
7998				y1 += aliasPixel;
7999				y2 += aliasPixel;
8000			} else {
8001				x1 = x2 = options.position === 'left' ? me.right : me.left;
8002				x1 += aliasPixel;
8003				x2 += aliasPixel;
8004			}
8005
8006			context.beginPath();
8007			context.moveTo(x1, y1);
8008			context.lineTo(x2, y2);
8009			context.stroke();
8010		}
8011	}
8012});
8013
8014},{"26":26,"27":27,"35":35,"46":46}],34:[function(require,module,exports){
8015'use strict';
8016
8017var defaults = require(26);
8018var helpers = require(46);
8019var layouts = require(31);
8020
8021module.exports = {
8022	// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
8023	// use the new chart options to grab the correct scale
8024	constructors: {},
8025	// Use a registration function so that we can move to an ES6 map when we no longer need to support
8026	// old browsers
8027
8028	// Scale config defaults
8029	defaults: {},
8030	registerScaleType: function(type, scaleConstructor, scaleDefaults) {
8031		this.constructors[type] = scaleConstructor;
8032		this.defaults[type] = helpers.clone(scaleDefaults);
8033	},
8034	getScaleConstructor: function(type) {
8035		return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
8036	},
8037	getScaleDefaults: function(type) {
8038		// Return the scale defaults merged with the global settings so that we always use the latest ones
8039		return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
8040	},
8041	updateScaleDefaults: function(type, additions) {
8042		var me = this;
8043		if (me.defaults.hasOwnProperty(type)) {
8044			me.defaults[type] = helpers.extend(me.defaults[type], additions);
8045		}
8046	},
8047	addScalesToLayout: function(chart) {
8048		// Adds each scale to the chart.boxes array to be sized accordingly
8049		helpers.each(chart.scales, function(scale) {
8050			// Set ILayoutItem parameters for backwards compatibility
8051			scale.fullWidth = scale.options.fullWidth;
8052			scale.position = scale.options.position;
8053			scale.weight = scale.options.weight;
8054			layouts.addBox(chart, scale);
8055		});
8056	}
8057};
8058
8059},{"26":26,"31":31,"46":46}],35:[function(require,module,exports){
8060'use strict';
8061
8062var helpers = require(46);
8063
8064/**
8065 * Namespace to hold static tick generation functions
8066 * @namespace Chart.Ticks
8067 */
8068module.exports = {
8069	/**
8070	 * Namespace to hold formatters for different types of ticks
8071	 * @namespace Chart.Ticks.formatters
8072	 */
8073	formatters: {
8074		/**
8075		 * Formatter for value labels
8076		 * @method Chart.Ticks.formatters.values
8077		 * @param value the value to display
8078		 * @return {String|Array} the label to display
8079		 */
8080		values: function(value) {
8081			return helpers.isArray(value) ? value : '' + value;
8082		},
8083
8084		/**
8085		 * Formatter for linear numeric ticks
8086		 * @method Chart.Ticks.formatters.linear
8087		 * @param tickValue {Number} the value to be formatted
8088		 * @param index {Number} the position of the tickValue parameter in the ticks array
8089		 * @param ticks {Array<Number>} the list of ticks being converted
8090		 * @return {String} string representation of the tickValue parameter
8091		 */
8092		linear: function(tickValue, index, ticks) {
8093			// If we have lots of ticks, don't use the ones
8094			var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
8095
8096			// If we have a number like 2.5 as the delta, figure out how many decimal places we need
8097			if (Math.abs(delta) > 1) {
8098				if (tickValue !== Math.floor(tickValue)) {
8099					// not an integer
8100					delta = tickValue - Math.floor(tickValue);
8101				}
8102			}
8103
8104			var logDelta = helpers.log10(Math.abs(delta));
8105			var tickString = '';
8106
8107			if (tickValue !== 0) {
8108				var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
8109				if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
8110					var logTick = helpers.log10(Math.abs(tickValue));
8111					tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta));
8112				} else {
8113					var numDecimal = -1 * Math.floor(logDelta);
8114					numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
8115					tickString = tickValue.toFixed(numDecimal);
8116				}
8117			} else {
8118				tickString = '0'; // never show decimal places for 0
8119			}
8120
8121			return tickString;
8122		},
8123
8124		logarithmic: function(tickValue, index, ticks) {
8125			var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
8126
8127			if (tickValue === 0) {
8128				return '0';
8129			} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
8130				return tickValue.toExponential();
8131			}
8132			return '';
8133		}
8134	}
8135};
8136
8137},{"46":46}],36:[function(require,module,exports){
8138'use strict';
8139
8140var defaults = require(26);
8141var Element = require(27);
8142var helpers = require(46);
8143
8144defaults._set('global', {
8145	tooltips: {
8146		enabled: true,
8147		custom: null,
8148		mode: 'nearest',
8149		position: 'average',
8150		intersect: true,
8151		backgroundColor: 'rgba(0,0,0,0.8)',
8152		titleFontStyle: 'bold',
8153		titleSpacing: 2,
8154		titleMarginBottom: 6,
8155		titleFontColor: '#fff',
8156		titleAlign: 'left',
8157		bodySpacing: 2,
8158		bodyFontColor: '#fff',
8159		bodyAlign: 'left',
8160		footerFontStyle: 'bold',
8161		footerSpacing: 2,
8162		footerMarginTop: 6,
8163		footerFontColor: '#fff',
8164		footerAlign: 'left',
8165		yPadding: 6,
8166		xPadding: 6,
8167		caretPadding: 2,
8168		caretSize: 5,
8169		cornerRadius: 6,
8170		multiKeyBackground: '#fff',
8171		displayColors: true,
8172		borderColor: 'rgba(0,0,0,0)',
8173		borderWidth: 0,
8174		callbacks: {
8175			// Args are: (tooltipItems, data)
8176			beforeTitle: helpers.noop,
8177			title: function(tooltipItems, data) {
8178				// Pick first xLabel for now
8179				var title = '';
8180				var labels = data.labels;
8181				var labelCount = labels ? labels.length : 0;
8182
8183				if (tooltipItems.length > 0) {
8184					var item = tooltipItems[0];
8185
8186					if (item.xLabel) {
8187						title = item.xLabel;
8188					} else if (labelCount > 0 && item.index < labelCount) {
8189						title = labels[item.index];
8190					}
8191				}
8192
8193				return title;
8194			},
8195			afterTitle: helpers.noop,
8196
8197			// Args are: (tooltipItems, data)
8198			beforeBody: helpers.noop,
8199
8200			// Args are: (tooltipItem, data)
8201			beforeLabel: helpers.noop,
8202			label: function(tooltipItem, data) {
8203				var label = data.datasets[tooltipItem.datasetIndex].label || '';
8204
8205				if (label) {
8206					label += ': ';
8207				}
8208				label += tooltipItem.yLabel;
8209				return label;
8210			},
8211			labelColor: function(tooltipItem, chart) {
8212				var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
8213				var activeElement = meta.data[tooltipItem.index];
8214				var view = activeElement._view;
8215				return {
8216					borderColor: view.borderColor,
8217					backgroundColor: view.backgroundColor
8218				};
8219			},
8220			labelTextColor: function() {
8221				return this._options.bodyFontColor;
8222			},
8223			afterLabel: helpers.noop,
8224
8225			// Args are: (tooltipItems, data)
8226			afterBody: helpers.noop,
8227
8228			// Args are: (tooltipItems, data)
8229			beforeFooter: helpers.noop,
8230			footer: helpers.noop,
8231			afterFooter: helpers.noop
8232		}
8233	}
8234});
8235
8236var positioners = {
8237	/**
8238	 * Average mode places the tooltip at the average position of the elements shown
8239	 * @function Chart.Tooltip.positioners.average
8240	 * @param elements {ChartElement[]} the elements being displayed in the tooltip
8241	 * @returns {Point} tooltip position
8242	 */
8243	average: function(elements) {
8244		if (!elements.length) {
8245			return false;
8246		}
8247
8248		var i, len;
8249		var x = 0;
8250		var y = 0;
8251		var count = 0;
8252
8253		for (i = 0, len = elements.length; i < len; ++i) {
8254			var el = elements[i];
8255			if (el && el.hasValue()) {
8256				var pos = el.tooltipPosition();
8257				x += pos.x;
8258				y += pos.y;
8259				++count;
8260			}
8261		}
8262
8263		return {
8264			x: Math.round(x / count),
8265			y: Math.round(y / count)
8266		};
8267	},
8268
8269	/**
8270	 * Gets the tooltip position nearest of the item nearest to the event position
8271	 * @function Chart.Tooltip.positioners.nearest
8272	 * @param elements {Chart.Element[]} the tooltip elements
8273	 * @param eventPosition {Point} the position of the event in canvas coordinates
8274	 * @returns {Point} the tooltip position
8275	 */
8276	nearest: function(elements, eventPosition) {
8277		var x = eventPosition.x;
8278		var y = eventPosition.y;
8279		var minDistance = Number.POSITIVE_INFINITY;
8280		var i, len, nearestElement;
8281
8282		for (i = 0, len = elements.length; i < len; ++i) {
8283			var el = elements[i];
8284			if (el && el.hasValue()) {
8285				var center = el.getCenterPoint();
8286				var d = helpers.distanceBetweenPoints(eventPosition, center);
8287
8288				if (d < minDistance) {
8289					minDistance = d;
8290					nearestElement = el;
8291				}
8292			}
8293		}
8294
8295		if (nearestElement) {
8296			var tp = nearestElement.tooltipPosition();
8297			x = tp.x;
8298			y = tp.y;
8299		}
8300
8301		return {
8302			x: x,
8303			y: y
8304		};
8305	}
8306};
8307
8308/**
8309 * Helper method to merge the opacity into a color
8310 */
vendor: 14,554 bytes, lines 8311-8779
8311function mergeOpacity(colorString, opacity) {
8312	var color = helpers.color(colorString);
8313	return color.alpha(opacity * color.alpha()).rgbaString();
8314}
8315
8316// Helper to push or concat based on if the 2nd parameter is an array or not
8317function pushOrConcat(base, toPush) {
8318	if (toPush) {
8319		if (helpers.isArray(toPush)) {
8320			// base = base.concat(toPush);
8321			Array.prototype.push.apply(base, toPush);
8322		} else {
8323			base.push(toPush);
8324		}
8325	}
8326
8327	return base;
8328}
8329
8330/**
8331 * Returns array of strings split by newline
8332 * @param {String} value - The value to split by newline.
8333 * @returns {Array} value if newline present - Returned from String split() method
8334 * @function
8335 */
8336function splitNewlines(str) {
8337	if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
8338		return str.split('\n');
8339	}
8340	return str;
8341}
8342
8343
8344// Private helper to create a tooltip item model
8345// @param element : the chart element (point, arc, bar) to create the tooltip item for
8346// @return : new tooltip item
8347function createTooltipItem(element) {
8348	var xScale = element._xScale;
8349	var yScale = element._yScale || element._scale; // handle radar || polarArea charts
8350	var index = element._index;
8351	var datasetIndex = element._datasetIndex;
8352
8353	return {
8354		xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
8355		yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
8356		index: index,
8357		datasetIndex: datasetIndex,
8358		x: element._model.x,
8359		y: element._model.y
8360	};
8361}
8362
8363/**
8364 * Helper to get the reset model for the tooltip
8365 * @param tooltipOpts {Object} the tooltip options
8366 */
8367function getBaseModel(tooltipOpts) {
8368	var globalDefaults = defaults.global;
8369	var valueOrDefault = helpers.valueOrDefault;
8370
8371	return {
8372		// Positioning
8373		xPadding: tooltipOpts.xPadding,
8374		yPadding: tooltipOpts.yPadding,
8375		xAlign: tooltipOpts.xAlign,
8376		yAlign: tooltipOpts.yAlign,
8377
8378		// Body
8379		bodyFontColor: tooltipOpts.bodyFontColor,
8380		_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
8381		_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
8382		_bodyAlign: tooltipOpts.bodyAlign,
8383		bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
8384		bodySpacing: tooltipOpts.bodySpacing,
8385
8386		// Title
8387		titleFontColor: tooltipOpts.titleFontColor,
8388		_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
8389		_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
8390		titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
8391		_titleAlign: tooltipOpts.titleAlign,
8392		titleSpacing: tooltipOpts.titleSpacing,
8393		titleMarginBottom: tooltipOpts.titleMarginBottom,
8394
8395		// Footer
8396		footerFontColor: tooltipOpts.footerFontColor,
8397		_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
8398		_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
8399		footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
8400		_footerAlign: tooltipOpts.footerAlign,
8401		footerSpacing: tooltipOpts.footerSpacing,
8402		footerMarginTop: tooltipOpts.footerMarginTop,
8403
8404		// Appearance
8405		caretSize: tooltipOpts.caretSize,
8406		cornerRadius: tooltipOpts.cornerRadius,
8407		backgroundColor: tooltipOpts.backgroundColor,
8408		opacity: 0,
8409		legendColorBackground: tooltipOpts.multiKeyBackground,
8410		displayColors: tooltipOpts.displayColors,
8411		borderColor: tooltipOpts.borderColor,
8412		borderWidth: tooltipOpts.borderWidth
8413	};
8414}
8415
8416/**
8417 * Get the size of the tooltip
8418 */
8419function getTooltipSize(tooltip, model) {
8420	var ctx = tooltip._chart.ctx;
8421
8422	var height = model.yPadding * 2; // Tooltip Padding
8423	var width = 0;
8424
8425	// Count of all lines in the body
8426	var body = model.body;
8427	var combinedBodyLength = body.reduce(function(count, bodyItem) {
8428		return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
8429	}, 0);
8430	combinedBodyLength += model.beforeBody.length + model.afterBody.length;
8431
8432	var titleLineCount = model.title.length;
8433	var footerLineCount = model.footer.length;
8434	var titleFontSize = model.titleFontSize;
8435	var bodyFontSize = model.bodyFontSize;
8436	var footerFontSize = model.footerFontSize;
8437
8438	height += titleLineCount * titleFontSize; // Title Lines
8439	height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
8440	height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
8441	height += combinedBodyLength * bodyFontSize; // Body Lines
8442	height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
8443	height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
8444	height += footerLineCount * (footerFontSize); // Footer Lines
8445	height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
8446
8447	// Title width
8448	var widthPadding = 0;
8449	var maxLineWidth = function(line) {
8450		width = Math.max(width, ctx.measureText(line).width + widthPadding);
8451	};
8452
8453	ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
8454	helpers.each(model.title, maxLineWidth);
8455
8456	// Body width
8457	ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
8458	helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
8459
8460	// Body lines may include some extra width due to the color box
8461	widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
8462	helpers.each(body, function(bodyItem) {
8463		helpers.each(bodyItem.before, maxLineWidth);
8464		helpers.each(bodyItem.lines, maxLineWidth);
8465		helpers.each(bodyItem.after, maxLineWidth);
8466	});
8467
8468	// Reset back to 0
8469	widthPadding = 0;
8470
8471	// Footer width
8472	ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
8473	helpers.each(model.footer, maxLineWidth);
8474
8475	// Add padding
8476	width += 2 * model.xPadding;
8477
8478	return {
8479		width: width,
8480		height: height
8481	};
8482}
8483
8484/**
8485 * Helper to get the alignment of a tooltip given the size
8486 */
8487function determineAlignment(tooltip, size) {
8488	var model = tooltip._model;
8489	var chart = tooltip._chart;
8490	var chartArea = tooltip._chart.chartArea;
8491	var xAlign = 'center';
8492	var yAlign = 'center';
8493
8494	if (model.y < size.height) {
8495		yAlign = 'top';
8496	} else if (model.y > (chart.height - size.height)) {
8497		yAlign = 'bottom';
8498	}
8499
8500	var lf, rf; // functions to determine left, right alignment
8501	var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
8502	var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
8503	var midX = (chartArea.left + chartArea.right) / 2;
8504	var midY = (chartArea.top + chartArea.bottom) / 2;
8505
8506	if (yAlign === 'center') {
8507		lf = function(x) {
8508			return x <= midX;
8509		};
8510		rf = function(x) {
8511			return x > midX;
8512		};
8513	} else {
8514		lf = function(x) {
8515			return x <= (size.width / 2);
8516		};
8517		rf = function(x) {
8518			return x >= (chart.width - (size.width / 2));
8519		};
8520	}
8521
8522	olf = function(x) {
8523		return x + size.width + model.caretSize + model.caretPadding > chart.width;
8524	};
8525	orf = function(x) {
8526		return x - size.width - model.caretSize - model.caretPadding < 0;
8527	};
8528	yf = function(y) {
8529		return y <= midY ? 'top' : 'bottom';
8530	};
8531
8532	if (lf(model.x)) {
8533		xAlign = 'left';
8534
8535		// Is tooltip too wide and goes over the right side of the chart.?
8536		if (olf(model.x)) {
8537			xAlign = 'center';
8538			yAlign = yf(model.y);
8539		}
8540	} else if (rf(model.x)) {
8541		xAlign = 'right';
8542
8543		// Is tooltip too wide and goes outside left edge of canvas?
8544		if (orf(model.x)) {
8545			xAlign = 'center';
8546			yAlign = yf(model.y);
8547		}
8548	}
8549
8550	var opts = tooltip._options;
8551	return {
8552		xAlign: opts.xAlign ? opts.xAlign : xAlign,
8553		yAlign: opts.yAlign ? opts.yAlign : yAlign
8554	};
8555}
8556
8557/**
8558 * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
8559 */
8560function getBackgroundPoint(vm, size, alignment, chart) {
8561	// Background Position
8562	var x = vm.x;
8563	var y = vm.y;
8564
8565	var caretSize = vm.caretSize;
8566	var caretPadding = vm.caretPadding;
8567	var cornerRadius = vm.cornerRadius;
8568	var xAlign = alignment.xAlign;
8569	var yAlign = alignment.yAlign;
8570	var paddingAndSize = caretSize + caretPadding;
8571	var radiusAndPadding = cornerRadius + caretPadding;
8572
8573	if (xAlign === 'right') {
8574		x -= size.width;
8575	} else if (xAlign === 'center') {
8576		x -= (size.width / 2);
8577		if (x + size.width > chart.width) {
8578			x = chart.width - size.width;
8579		}
8580		if (x < 0) {
8581			x = 0;
8582		}
8583	}
8584
8585	if (yAlign === 'top') {
8586		y += paddingAndSize;
8587	} else if (yAlign === 'bottom') {
8588		y -= size.height + paddingAndSize;
8589	} else {
8590		y -= (size.height / 2);
8591	}
8592
8593	if (yAlign === 'center') {
8594		if (xAlign === 'left') {
8595			x += paddingAndSize;
8596		} else if (xAlign === 'right') {
8597			x -= paddingAndSize;
8598		}
8599	} else if (xAlign === 'left') {
8600		x -= radiusAndPadding;
8601	} else if (xAlign === 'right') {
8602		x += radiusAndPadding;
8603	}
8604
8605	return {
8606		x: x,
8607		y: y
8608	};
8609}
8610
8611/**
8612 * Helper to build before and after body lines
8613 */
8614function getBeforeAfterBodyLines(callback) {
8615	return pushOrConcat([], splitNewlines(callback));
8616}
8617
8618var exports = module.exports = Element.extend({
8619	initialize: function() {
8620		this._model = getBaseModel(this._options);
8621		this._lastActive = [];
8622	},
8623
8624	// Get the title
8625	// Args are: (tooltipItem, data)
8626	getTitle: function() {
8627		var me = this;
8628		var opts = me._options;
8629		var callbacks = opts.callbacks;
8630
8631		var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
8632		var title = callbacks.title.apply(me, arguments);
8633		var afterTitle = callbacks.afterTitle.apply(me, arguments);
8634
8635		var lines = [];
8636		lines = pushOrConcat(lines, splitNewlines(beforeTitle));
8637		lines = pushOrConcat(lines, splitNewlines(title));
8638		lines = pushOrConcat(lines, splitNewlines(afterTitle));
8639
8640		return lines;
8641	},
8642
8643	// Args are: (tooltipItem, data)
8644	getBeforeBody: function() {
8645		return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
8646	},
8647
8648	// Args are: (tooltipItem, data)
8649	getBody: function(tooltipItems, data) {
8650		var me = this;
8651		var callbacks = me._options.callbacks;
8652		var bodyItems = [];
8653
8654		helpers.each(tooltipItems, function(tooltipItem) {
8655			var bodyItem = {
8656				before: [],
8657				lines: [],
8658				after: []
8659			};
8660			pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
8661			pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
8662			pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));
8663
8664			bodyItems.push(bodyItem);
8665		});
8666
8667		return bodyItems;
8668	},
8669
8670	// Args are: (tooltipItem, data)
8671	getAfterBody: function() {
8672		return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
8673	},
8674
8675	// Get the footer and beforeFooter and afterFooter lines
8676	// Args are: (tooltipItem, data)
8677	getFooter: function() {
8678		var me = this;
8679		var callbacks = me._options.callbacks;
8680
8681		var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
8682		var footer = callbacks.footer.apply(me, arguments);
8683		var afterFooter = callbacks.afterFooter.apply(me, arguments);
8684
8685		var lines = [];
8686		lines = pushOrConcat(lines, splitNewlines(beforeFooter));
8687		lines = pushOrConcat(lines, splitNewlines(footer));
8688		lines = pushOrConcat(lines, splitNewlines(afterFooter));
8689
8690		return lines;
8691	},
8692
8693	update: function(changed) {
8694		var me = this;
8695		var opts = me._options;
8696
8697		// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
8698		// 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
8699		// which breaks any animations.
8700		var existingModel = me._model;
8701		var model = me._model = getBaseModel(opts);
8702		var active = me._active;
8703
8704		var data = me._data;
8705
8706		// In the case where active.length === 0 we need to keep these at existing values for good animations
8707		var alignment = {
8708			xAlign: existingModel.xAlign,
8709			yAlign: existingModel.yAlign
8710		};
8711		var backgroundPoint = {
8712			x: existingModel.x,
8713			y: existingModel.y
8714		};
8715		var tooltipSize = {
8716			width: existingModel.width,
8717			height: existingModel.height
8718		};
8719		var tooltipPosition = {
8720			x: existingModel.caretX,
8721			y: existingModel.caretY
8722		};
8723
8724		var i, len;
8725
8726		if (active.length) {
8727			model.opacity = 1;
8728
8729			var labelColors = [];
8730			var labelTextColors = [];
8731			tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);
8732
8733			var tooltipItems = [];
8734			for (i = 0, len = active.length; i < len; ++i) {
8735				tooltipItems.push(createTooltipItem(active[i]));
8736			}
8737
8738			// If the user provided a filter function, use it to modify the tooltip items
8739			if (opts.filter) {
8740				tooltipItems = tooltipItems.filter(function(a) {
8741					return opts.filter(a, data);
8742				});
8743			}
8744
8745			// If the user provided a sorting function, use it to modify the tooltip items
8746			if (opts.itemSort) {
8747				tooltipItems = tooltipItems.sort(function(a, b) {
8748					return opts.itemSort(a, b, data);
8749				});
8750			}
8751
8752			// Determine colors for boxes
8753			helpers.each(tooltipItems, function(tooltipItem) {
8754				labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
8755				labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
8756			});
8757
8758
8759			// Build the Text Lines
8760			model.title = me.getTitle(tooltipItems, data);
8761			model.beforeBody = me.getBeforeBody(tooltipItems, data);
8762			model.body = me.getBody(tooltipItems, data);
8763			model.afterBody = me.getAfterBody(tooltipItems, data);
8764			model.footer = me.getFooter(tooltipItems, data);
8765
8766			// Initial positioning and colors
8767			model.x = Math.round(tooltipPosition.x);
8768			model.y = Math.round(tooltipPosition.y);
8769			model.caretPadding = opts.caretPadding;
8770			model.labelColors = labelColors;
8771			model.labelTextColors = labelTextColors;
8772
8773			// data points
8774			model.dataPoints = tooltipItems;
8775
8776			// We need to determine alignment of the tooltip
8777			tooltipSize = getTooltipSize(this, model);
8778			alignment = determineAlignment(this, tooltipSize);
8779			// Final Size and Position
8780			backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
8781		} else {
8782			model.opacity = 0;
8783		}
8784
8785		model.xAlign = alignment.xAlign;
8786		model.yAlign = alignment.yAlign;
8787		model.x = backgroundPoint.x;
8788		model.y = backgroundPoint.y;
8789		model.width = tooltipSize.width;
8790		model.height = tooltipSize.height;
8791
8792		// Point where the caret on the tooltip points to
8793		model.caretX = tooltipPosition.x;
8794		model.caretY = tooltipPosition.y;
8795
8796		me._model = model;
8797
8798		if (changed && opts.custom) {
8799			opts.custom.call(me, model);
8800		}
8801
8802		return me;
8803	},
8804
8805	drawCaret: function(tooltipPoint, size) {
8806		var ctx = this._chart.ctx;
8807		var vm = this._view;
8808		var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
8809
8810		ctx.lineTo(caretPosition.x1, caretPosition.y1);
8811		ctx.lineTo(caretPosition.x2, caretPosition.y2);
8812		ctx.lineTo(caretPosition.x3, caretPosition.y3);
8813	},
8814	getCaretPosition: function(tooltipPoint, size, vm) {
8815		var x1, x2, x3, y1, y2, y3;
8816		var caretSize = vm.caretSize;
8817		var cornerRadius = vm.cornerRadius;
8818		var xAlign = vm.xAlign;
8819		var yAlign = vm.yAlign;
8820		var ptX = tooltipPoint.x;
8821		var ptY = tooltipPoint.y;
8822		var width = size.width;
8823		var height = size.height;
8824
8825		if (yAlign === 'center') {
8826			y2 = ptY + (height / 2);
8827
8828			if (xAlign === 'left') {
8829				x1 = ptX;
8830				x2 = x1 - caretSize;
8831				x3 = x1;
8832
8833				y1 = y2 + caretSize;
8834				y3 = y2 - caretSize;
8835			} else {
8836				x1 = ptX + width;
8837				x2 = x1 + caretSize;
8838				x3 = x1;
8839
8840				y1 = y2 - caretSize;
8841				y3 = y2 + caretSize;
8842			}
8843		} else {
8844			if (xAlign === 'left') {
8845				x2 = ptX + cornerRadius + (caretSize);
8846				x1 = x2 - caretSize;
8847				x3 = x2 + caretSize;
8848			} else if (xAlign === 'right') {
8849				x2 = ptX + width - cornerRadius - caretSize;
8850				x1 = x2 - caretSize;
8851				x3 = x2 + caretSize;
8852			} else {
8853				x2 = vm.caretX;
8854				x1 = x2 - caretSize;
8855				x3 = x2 + caretSize;
8856			}
8857			if (yAlign === 'top') {
8858				y1 = ptY;
8859				y2 = y1 - caretSize;
8860				y3 = y1;
8861			} else {
8862				y1 = ptY + height;
8863				y2 = y1 + caretSize;
8864				y3 = y1;
8865				// invert drawing order
8866				var tmp = x3;
8867				x3 = x1;
8868				x1 = tmp;
8869			}
8870		}
8871		return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
8872	},
8873
8874	drawTitle: function(pt, vm, ctx, opacity) {
8875		var title = vm.title;
8876
8877		if (title.length) {
8878			ctx.textAlign = vm._titleAlign;
8879			ctx.textBaseline = 'top';
8880
8881			var titleFontSize = vm.titleFontSize;
8882			var titleSpacing = vm.titleSpacing;
8883
8884			ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
8885			ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
8886
8887			var i, len;
8888			for (i = 0, len = title.length; i < len; ++i) {
8889				ctx.fillText(title[i], pt.x, pt.y);
8890				pt.y += titleFontSize + titleSpacing; // Line Height and spacing
8891
8892				if (i + 1 === title.length) {
8893					pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
8894				}
8895			}
8896		}
8897	},
8898
8899	drawBody: function(pt, vm, ctx, opacity) {
8900		var bodyFontSize = vm.bodyFontSize;
8901		var bodySpacing = vm.bodySpacing;
8902		var body = vm.body;
8903
8904		ctx.textAlign = vm._bodyAlign;
8905		ctx.textBaseline = 'top';
8906		ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
8907
8908		// Before Body
8909		var xLinePadding = 0;
8910		var fillLineOfText = function(line) {
8911			ctx.fillText(line, pt.x + xLinePadding, pt.y);
8912			pt.y += bodyFontSize + bodySpacing;
8913		};
8914
8915		// Before body lines
8916		ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity);
8917		helpers.each(vm.beforeBody, fillLineOfText);
8918
8919		var drawColorBoxes = vm.displayColors;
8920		xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
8921
8922		// Draw body lines now
8923		helpers.each(body, function(bodyItem, i) {
8924			var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
8925			ctx.fillStyle = textColor;
8926			helpers.each(bodyItem.before, fillLineOfText);
8927
8928			helpers.each(bodyItem.lines, function(line) {
8929				// Draw Legend-like boxes if needed
8930				if (drawColorBoxes) {
8931					// Fill a white rect so that colours merge nicely if the opacity is < 1
8932					ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
8933					ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
8934
8935					// Border
8936					ctx.lineWidth = 1;
8937					ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
8938					ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
8939
8940					// Inner square
8941					ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
8942					ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
8943					ctx.fillStyle = textColor;
8944				}
8945
8946				fillLineOfText(line);
8947			});
8948
8949			helpers.each(bodyItem.after, fillLineOfText);
8950		});
8951
8952		// Reset back to 0 for after body
8953		xLinePadding = 0;
8954
8955		// After body lines
8956		helpers.each(vm.afterBody, fillLineOfText);
8957		pt.y -= bodySpacing; // Remove last body spacing
8958	},
8959
8960	drawFooter: function(pt, vm, ctx, opacity) {
8961		var footer = vm.footer;
8962
8963		if (footer.length) {
8964			pt.y += vm.footerMarginTop;
8965
8966			ctx.textAlign = vm._footerAlign;
8967			ctx.textBaseline = 'top';
8968
8969			ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
8970			ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
8971
8972			helpers.each(footer, function(line) {
8973				ctx.fillText(line, pt.x, pt.y);
8974				pt.y += vm.footerFontSize + vm.footerSpacing;
8975			});
8976		}
8977	},
8978
8979	drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
8980		ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
8981		ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
8982		ctx.lineWidth = vm.borderWidth;
8983		var xAlign = vm.xAlign;
8984		var yAlign = vm.yAlign;
8985		var x = pt.x;
8986		var y = pt.y;
8987		var width = tooltipSize.width;
8988		var height = tooltipSize.height;
8989		var radius = vm.cornerRadius;
8990
8991		ctx.beginPath();
8992		ctx.moveTo(x + radius, y);
8993		if (yAlign === 'top') {
8994			this.drawCaret(pt, tooltipSize);
8995		}
8996		ctx.lineTo(x + width - radius, y);
8997		ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
8998		if (yAlign === 'center' && xAlign === 'right') {
8999			this.drawCaret(pt, tooltipSize);
9000		}
9001		ctx.lineTo(x + width, y + height - radius);
9002		ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
9003		if (yAlign === 'bottom') {
9004			this.drawCaret(pt, tooltipSize);
9005		}
9006		ctx.lineTo(x + radius, y + height);
9007		ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
9008		if (yAlign === 'center' && xAlign === 'left') {
9009			this.drawCaret(pt, tooltipSize);
9010		}
9011		ctx.lineTo(x, y + radius);
9012		ctx.quadraticCurveTo(x, y, x + radius, y);
9013		ctx.closePath();
9014
9015		ctx.fill();
9016
9017		if (vm.borderWidth > 0) {
9018			ctx.stroke();
9019		}
9020	},
9021
9022	draw: function() {
9023		var ctx = this._chart.ctx;
9024		var vm = this._view;
9025
9026		if (vm.opacity === 0) {
9027			return;
9028		}
9029
9030		var tooltipSize = {
9031			width: vm.width,
9032			height: vm.height
9033		};
9034		var pt = {
9035			x: vm.x,
9036			y: vm.y
9037		};
9038
9039		// IE11/Edge does not like very small opacities, so snap to 0
9040		var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
9041
9042		// Truthy/falsey value for empty tooltip
9043		var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
9044
9045		if (this._options.enabled && hasTooltipContent) {
9046			// Draw Background
9047			this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
9048
9049			// Draw Title, Body, and Footer
9050			pt.x += vm.xPadding;
9051			pt.y += vm.yPadding;
9052
9053			// Titles
9054			this.drawTitle(pt, vm, ctx, opacity);
9055
9056			// Body
9057			this.drawBody(pt, vm, ctx, opacity);
9058
9059			// Footer
9060			this.drawFooter(pt, vm, ctx, opacity);
9061		}
9062	},
9063
9064	/**
9065	 * Handle an event
9066	 * @private
9067	 * @param {IEvent} event - The event to handle
9068	 * @returns {Boolean} true if the tooltip changed
9069	 */
9070	handleEvent: function(e) {
9071		var me = this;
9072		var options = me._options;
9073		var changed = false;
9074
9075		me._lastActive = me._lastActive || [];
9076
9077		// Find Active Elements for tooltips
9078		if (e.type === 'mouseout') {
9079			me._active = [];
9080		} else {
9081			me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
9082		}
9083
9084		// Remember Last Actives
9085		changed = !helpers.arrayEquals(me._active, me._lastActive);
9086
9087		// Only handle target event on tooltip change
9088		if (changed) {
9089			me._lastActive = me._active;
9090
9091			if (options.enabled || options.custom) {
9092				me._eventPosition = {
9093					x: e.x,
9094					y: e.y
9095				};
9096
9097				me.update(true);
9098				me.pivot();
9099			}
9100		}
9101
9102		return changed;
9103	}
9104});
9105
9106/**
9107 * @namespace Chart.Tooltip.positioners
9108 */
9109exports.positioners = positioners;
9110
9111
9112},{"26":26,"27":27,"46":46}],37:[function(require,module,exports){
9113'use strict';
9114
9115var defaults = require(26);
9116var Element = require(27);
9117var helpers = require(46);
9118
9119defaults._set('global', {
9120	elements: {
9121		arc: {
9122			backgroundColor: defaults.global.defaultColor,
9123			borderColor: '#fff',
9124			borderWidth: 2
9125		}
9126	}
9127});
9128
9129module.exports = Element.extend({
9130	inLabelRange: function(mouseX) {
9131		var vm = this._view;
9132
9133		if (vm) {
9134			return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
9135		}
9136		return false;
9137	},
9138
9139	inRange: function(chartX, chartY) {
9140		var vm = this._view;
9141
9142		if (vm) {
9143			var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
9144			var	angle = pointRelativePosition.angle;
9145			var distance = pointRelativePosition.distance;
9146
9147			// Sanitise angle range
9148			var startAngle = vm.startAngle;
9149			var endAngle = vm.endAngle;
9150			while (endAngle < startAngle) {
9151				endAngle += 2.0 * Math.PI;
9152			}
9153			while (angle > endAngle) {
9154				angle -= 2.0 * Math.PI;
9155			}
9156			while (angle < startAngle) {
9157				angle += 2.0 * Math.PI;
9158			}
9159
9160			// Check if within the range of the open/close angle
9161			var betweenAngles = (angle >= startAngle && angle <= endAngle);
9162			var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
9163
9164			return (betweenAngles && withinRadius);
9165		}
9166		return false;
9167	},
9168
9169	getCenterPoint: function() {
9170		var vm = this._view;
9171		var halfAngle = (vm.startAngle + vm.endAngle) / 2;
9172		var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
9173		return {
9174			x: vm.x + Math.cos(halfAngle) * halfRadius,
9175			y: vm.y + Math.sin(halfAngle) * halfRadius
9176		};
9177	},
9178
9179	getArea: function() {
9180		var vm = this._view;
9181		return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
9182	},
9183
9184	tooltipPosition: function() {
9185		var vm = this._view;
9186		var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
9187		var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
9188
9189		return {
9190			x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
9191			y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
9192		};
9193	},
9194
9195	draw: function() {
9196		var ctx = this._chart.ctx;
9197		var vm = this._view;
9198		var sA = vm.startAngle;
9199		var eA = vm.endAngle;
9200
9201		ctx.beginPath();
9202
9203		ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
9204		ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
9205
9206		ctx.closePath();
9207		ctx.strokeStyle = vm.borderColor;
9208		ctx.lineWidth = vm.borderWidth;
9209
9210		ctx.fillStyle = vm.backgroundColor;
9211
9212		ctx.fill();
9213		ctx.lineJoin = 'bevel';
9214
9215		if (vm.borderWidth) {
9216			ctx.stroke();
9217		}
9218	}
9219});
9220
9221},{"26":26,"27":27,"46":46}],38:[function(require,module,exports){
9222'use strict';
9223
9224var defaults = require(26);
9225var Element = require(27);
9226var helpers = require(46);
9227
9228var globalDefaults = defaults.global;
9229
9230defaults._set('global', {
9231	elements: {
9232		line: {
9233			tension: 0.4,
9234			backgroundColor: globalDefaults.defaultColor,
9235			borderWidth: 3,
9236			borderColor: globalDefaults.defaultColor,
9237			borderCapStyle: 'butt',
9238			borderDash: [],
9239			borderDashOffset: 0.0,
9240			borderJoinStyle: 'miter',
9241			capBezierPoints: true,
9242			fill: true, // do we fill in the area between the line and its base axis
9243		}
9244	}
9245});
9246
9247module.exports = Element.extend({
9248	draw: function() {
9249		var me = this;
9250		var vm = me._view;
9251		var ctx = me._chart.ctx;
9252		var spanGaps = vm.spanGaps;
9253		var points = me._children.slice(); // clone array
9254		var globalOptionLineElements = globalDefaults.elements.line;
9255		var lastDrawnIndex = -1;
9256		var index, current, previous, currentVM;
9257
9258		// If we are looping, adding the first point again
9259		if (me._loop && points.length) {
9260			points.push(points[0]);
9261		}
9262
9263		ctx.save();
9264
9265		// Stroke Line Options
9266		ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
9267
9268		// IE 9 and 10 do not support line dash
9269		if (ctx.setLineDash) {
9270			ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
9271		}
9272
9273		ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
9274		ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
9275		ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
9276		ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
9277
9278		// Stroke Line
9279		ctx.beginPath();
9280		lastDrawnIndex = -1;
9281
9282		for (index = 0; index < points.length; ++index) {
9283			current = points[index];
9284			previous = helpers.previousItem(points, index);
9285			currentVM = current._view;
9286
9287			// First point moves to it's starting position no matter what
9288			if (index === 0) {
9289				if (!currentVM.skip) {
9290					ctx.moveTo(currentVM.x, currentVM.y);
9291					lastDrawnIndex = index;
9292				}
9293			} else {
9294				previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
9295
9296				if (!currentVM.skip) {
9297					if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
9298						// There was a gap and this is the first point after the gap
9299						ctx.moveTo(currentVM.x, currentVM.y);
9300					} else {
9301						// Line to next point
9302						helpers.canvas.lineTo(ctx, previous._view, current._view);
9303					}
9304					lastDrawnIndex = index;
9305				}
9306			}
9307		}
9308
9309		ctx.stroke();
9310		ctx.restore();
9311	}
9312});
9313
9314},{"26":26,"27":27,"46":46}],39:[function(require,module,exports){
9315'use strict';
9316
9317var defaults = require(26);
9318var Element = require(27);
9319var helpers = require(46);
9320
9321var defaultColor = defaults.global.defaultColor;
9322
9323defaults._set('global', {
9324	elements: {
9325		point: {
9326			radius: 3,
9327			pointStyle: 'circle',
9328			backgroundColor: defaultColor,
9329			borderColor: defaultColor,
9330			borderWidth: 1,
9331			// Hover
9332			hitRadius: 1,
9333			hoverRadius: 4,
9334			hoverBorderWidth: 1
9335		}
9336	}
9337});
9338
9339function xRange(mouseX) {
9340	var vm = this._view;
9341	return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
9342}
9343
9344function yRange(mouseY) {
9345	var vm = this._view;
9346	return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
9347}
9348
9349module.exports = Element.extend({
9350	inRange: function(mouseX, mouseY) {
9351		var vm = this._view;
9352		return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
9353	},
9354
9355	inLabelRange: xRange,
9356	inXRange: xRange,
9357	inYRange: yRange,
9358
9359	getCenterPoint: function() {
9360		var vm = this._view;
9361		return {
9362			x: vm.x,
9363			y: vm.y
9364		};
9365	},
9366
9367	getArea: function() {
9368		return Math.PI * Math.pow(this._view.radius, 2);
9369	},
9370
9371	tooltipPosition: function() {
9372		var vm = this._view;
9373		return {
9374			x: vm.x,
9375			y: vm.y,
9376			padding: vm.radius + vm.borderWidth
9377		};
9378	},
9379
9380	draw: function(chartArea) {
9381		var vm = this._view;
9382		var model = this._model;
9383		var ctx = this._chart.ctx;
9384		var pointStyle = vm.pointStyle;
9385		var rotation = vm.rotation;
9386		var radius = vm.radius;
9387		var x = vm.x;
9388		var y = vm.y;
9389		var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
9390
9391		if (vm.skip) {
9392			return;
9393		}
9394
9395		// Clipping for Points.
9396		if (chartArea === undefined || (model.x >= chartArea.left && chartArea.right * errMargin >= model.x && model.y >= chartArea.top && chartArea.bottom * errMargin >= model.y)) {
9397			ctx.strokeStyle = vm.borderColor || defaultColor;
9398			ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
9399			ctx.fillStyle = vm.backgroundColor || defaultColor;
9400			helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
9401		}
9402	}
9403});
9404
9405},{"26":26,"27":27,"46":46}],40:[function(require,module,exports){
9406'use strict';
9407
9408var defaults = require(26);
9409var Element = require(27);
9410
9411defaults._set('global', {
9412	elements: {
9413		rectangle: {
9414			backgroundColor: defaults.global.defaultColor,
9415			borderColor: defaults.global.defaultColor,
9416			borderSkipped: 'bottom',
9417			borderWidth: 0
9418		}
9419	}
9420});
9421
9422function isVertical(bar) {
9423	return bar._view.width !== undefined;
9424}
9425
9426/**
9427 * Helper function to get the bounds of the bar regardless of the orientation
9428 * @param bar {Chart.Element.Rectangle} the bar
9429 * @return {Bounds} bounds of the bar
9430 * @private
9431 */
9432function getBarBounds(bar) {
9433	var vm = bar._view;
9434	var x1, x2, y1, y2;
9435
9436	if (isVertical(bar)) {
9437		// vertical
9438		var halfWidth = vm.width / 2;
9439		x1 = vm.x - halfWidth;
9440		x2 = vm.x + halfWidth;
9441		y1 = Math.min(vm.y, vm.base);
9442		y2 = Math.max(vm.y, vm.base);
9443	} else {
9444		// horizontal bar
9445		var halfHeight = vm.height / 2;
9446		x1 = Math.min(vm.x, vm.base);
9447		x2 = Math.max(vm.x, vm.base);
9448		y1 = vm.y - halfHeight;
9449		y2 = vm.y + halfHeight;
9450	}
9451
9452	return {
9453		left: x1,
9454		top: y1,
9455		right: x2,
9456		bottom: y2
9457	};
9458}
9459
9460module.exports = Element.extend({
9461	draw: function() {
9462		var ctx = this._chart.ctx;
9463		var vm = this._view;
9464		var left, right, top, bottom, signX, signY, borderSkipped;
9465		var borderWidth = vm.borderWidth;
9466
9467		if (!vm.horizontal) {
9468			// bar
9469			left = vm.x - vm.width / 2;
9470			right = vm.x + vm.width / 2;
9471			top = vm.y;
9472			bottom = vm.base;
9473			signX = 1;
9474			signY = bottom > top ? 1 : -1;
9475			borderSkipped = vm.borderSkipped || 'bottom';
9476		} else {
9477			// horizontal bar
9478			left = vm.base;
9479			right = vm.x;
9480			top = vm.y - vm.height / 2;
9481			bottom = vm.y + vm.height / 2;
9482			signX = right > left ? 1 : -1;
9483			signY = 1;
9484			borderSkipped = vm.borderSkipped || 'left';
9485		}
9486
9487		// Canvas doesn't allow us to stroke inside the width so we can
9488		// adjust the sizes to fit if we're setting a stroke on the line
9489		if (borderWidth) {
9490			// borderWidth shold be less than bar width and bar height.
9491			var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
9492			borderWidth = borderWidth > barSize ? barSize : borderWidth;
9493			var halfStroke = borderWidth / 2;
9494			// Adjust borderWidth when bar top position is near vm.base(zero).
9495			var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
9496			var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
9497			var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
9498			var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
9499			// not become a vertical line?
9500			if (borderLeft !== borderRight) {
9501				top = borderTop;
9502				bottom = borderBottom;
9503			}
9504			// not become a horizontal line?
9505			if (borderTop !== borderBottom) {
9506				left = borderLeft;
9507				right = borderRight;
9508			}
9509		}
9510
9511		ctx.beginPath();
9512		ctx.fillStyle = vm.backgroundColor;
9513		ctx.strokeStyle = vm.borderColor;
9514		ctx.lineWidth = borderWidth;
9515
9516		// Corner points, from bottom-left to bottom-right clockwise
9517		// | 1 2 |
9518		// | 0 3 |
9519		var corners = [
9520			[left, bottom],
9521			[left, top],
9522			[right, top],
9523			[right, bottom]
9524		];
9525
9526		// Find first (starting) corner with fallback to 'bottom'
9527		var borders = ['bottom', 'left', 'top', 'right'];
9528		var startCorner = borders.indexOf(borderSkipped, 0);
9529		if (startCorner === -1) {
9530			startCorner = 0;
9531		}
9532
9533		function cornerAt(index) {
9534			return corners[(startCorner + index) % 4];
9535		}
9536
9537		// Draw rectangle from 'startCorner'
9538		var corner = cornerAt(0);
9539		ctx.moveTo(corner[0], corner[1]);
9540
9541		for (var i = 1; i < 4; i++) {
9542			corner = cornerAt(i);
9543			ctx.lineTo(corner[0], corner[1]);
9544		}
9545
9546		ctx.fill();
9547		if (borderWidth) {
9548			ctx.stroke();
9549		}
9550	},
9551
9552	height: function() {
9553		var vm = this._view;
9554		return vm.base - vm.y;
9555	},
9556
9557	inRange: function(mouseX, mouseY) {
9558		var inRange = false;
9559
9560		if (this._view) {
9561			var bounds = getBarBounds(this);
9562			inRange = mouseX >
9562= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
9563		}
9564
9565		return inRange;
9566	},
9567
9568	inLabelRange: function(mouseX, mouseY) {
9569		var me = this;
9570		if (!me._view) {
9571			return false;
9572		}
9573
9574		var inRange = false;
9575		var bounds = getBarBounds(me);
9576
9577		if (isVertical(me)) {
9578			inRange = mouseX >= bounds.left && mouseX <= bounds.right;
9579		} else {
9580			inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
9581		}
9582
9583		return inRange;
9584	},
9585
9586	inXRange: function(mouseX) {
9587		var bounds = getBarBounds(this);
9588		return mouseX >= bounds.left && mouseX <= bounds.right;
9589	},
9590
9591	inYRange: function(mouseY) {
9592		var bounds = getBarBounds(this);
9593		return mouseY >= bounds.top && mouseY <= bounds.bottom;
9594	},
9595
9596	getCenterPoint: function() {
9597		var vm = this._view;
9598		var x, y;
9599		if (isVertical(this)) {
9600			x = vm.x;
9601			y = (vm.y + vm.base) / 2;
9602		} else {
9603			x = (vm.x + vm.base) / 2;
9604			y = vm.y;
9605		}
9606
9607		return {x: x, y: y};
9608	},
9609
9610	getArea: function() {
9611		var vm = this._view;
9612		return vm.width * Math.abs(vm.y - vm.base);
9613	},
9614
9615	tooltipPosition: function() {
9616		var vm = this._view;
9617		return {
9618			x: vm.x,
9619			y: vm.y
9620		};
9621	}
9622});
9623
9624},{"26":26,"27":27}],41:[function(require,module,exports){
9625'use strict';
9626
9627module.exports = {};
9628module.exports.Arc = require(37);
9629module.exports.Line = require(38);
9630module.exports.Point = require(39);
9631module.exports.Rectangle = require(40);
9632
9633},{"37":37,"38":38,"39":39,"40":40}],42:[function(require,module,exports){
9634'use strict';
9635
9636var helpers = require(43);
9637
9638/**
9639 * @namespace Chart.helpers.canvas
9640 */
9641var exports = module.exports = {
9642	/**
9643	 * Clears the entire canvas associated to the given `chart`.
9644	 * @param {Chart} chart - The chart for which to clear the canvas.
9645	 */
9646	clear: function(chart) {
9647		chart.ctx.clearRect(0, 0, chart.width, chart.height);
9648	},
9649
9650	/**
9651	 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
9652	 * given size (width, height) and the same `radius` for all corners.
9653	 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
9654	 * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
9655	 * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
9656	 * @param {Number} width - The rectangle's width.
9657	 * @param {Number} height - The rectangle's height.
9658	 * @param {Number} radius - The rounded amount (in pixels) for the four corners.
9659	 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
9660	 */
9661	roundedRect: function(ctx, x, y, width, height, radius) {
9662		if (radius) {
9663			// NOTE(SB) `epsilon` helps to prevent minor artifacts appearing
9664			// on Chrome when `r` is exactly half the height or the width.
9665			var epsilon = 0.0000001;
9666			var r = Math.min(radius, (height / 2) - epsilon, (width / 2) - epsilon);
9667
9668			ctx.moveTo(x + r, y);
9669			ctx.lineTo(x + width - r, y);
9670			ctx.arcTo(x + width, y, x + width, y + r, r);
9671			ctx.lineTo(x + width, y + height - r);
9672			ctx.arcTo(x + width, y + height, x + width - r, y + height, r);
9673			ctx.lineTo(x + r, y + height);
9674			ctx.arcTo(x, y + height, x, y + height - r, r);
9675			ctx.lineTo(x, y + r);
9676			ctx.arcTo(x, y, x + r, y, r);
9677			ctx.closePath();
9678			ctx.moveTo(x, y);
9679		} else {
9680			ctx.rect(x, y, width, height);
9681		}
9682	},
9683
9684	drawPoint: function(ctx, style, radius, x, y, rotation) {
9685		var type, edgeLength, xOffset, yOffset, height, size;
9686		rotation = rotation || 0;
9687
9688		if (style && typeof style === 'object') {
9689			type = style.toString();
9690			if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
9691				ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
9692				return;
9693			}
9694		}
9695
9696		if (isNaN(radius) || radius <= 0) {
9697			return;
9698		}
9699
9700		ctx.save();
9701		ctx.translate(x, y);
9702		ctx.rotate(rotation * Math.PI / 180);
9703		ctx.beginPath();
9704
9705		switch (style) {
9706		// Default includes circle
9707		default:
9708			ctx.arc(0, 0, radius, 0, Math.PI * 2);
9709			ctx.closePath();
9710			break;
9711		case 'triangle':
9712			edgeLength = 3 * radius / Math.sqrt(3);
9713			height = edgeLength * Math.sqrt(3) / 2;
9714			ctx.moveTo(-edgeLength / 2, height / 3);
9715			ctx.lineTo(edgeLength / 2, height / 3);
9716			ctx.lineTo(0, -2 * height / 3);
9717			ctx.closePath();
9718			break;
9719		case 'rect':
9720			size = 1 / Math.SQRT2 * radius;
9721			ctx.rect(-size, -size, 2 * size, 2 * size);
9722			break;
9723		case 'rectRounded':
9724			var offset = radius / Math.SQRT2;
9725			var leftX = -offset;
9726			var topY = -offset;
9727			var sideSize = Math.SQRT2 * radius;
9728
9729			// NOTE(SB) the rounded rect implementation changed to use `arcTo`
9730			// instead of `quadraticCurveTo` since it generates better results
9731			// when rect is almost a circle. 0.425 (instead of 0.5) produces
9732			// results visually closer to the previous impl.
9733			this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius * 0.425);
9734			break;
9735		case 'rectRot':
9736			size = 1 / Math.SQRT2 * radius;
9737			ctx.moveTo(-size, 0);
9738			ctx.lineTo(0, size);
9739			ctx.lineTo(size, 0);
9740			ctx.lineTo(0, -size);
9741			ctx.closePath();
9742			break;
9743		case 'cross':
9744			ctx.moveTo(0, radius);
9745			ctx.lineTo(0, -radius);
9746			ctx.moveTo(-radius, 0);
9747			ctx.lineTo(radius, 0);
9748			break;
9749		case 'crossRot':
9750			xOffset = Math.cos(Math.PI / 4) * radius;
9751			yOffset = Math.sin(Math.PI / 4) * radius;
9752			ctx.moveTo(-xOffset, -yOffset);
9753			ctx.lineTo(xOffset, yOffset);
9754			ctx.moveTo(-xOffset, yOffset);
9755			ctx.lineTo(xOffset, -yOffset);
9756			break;
9757		case 'star':
9758			ctx.moveTo(0, radius);
9759			ctx.lineTo(0, -radius);
9760			ctx.moveTo(-radius, 0);
9761			ctx.lineTo(radius, 0);
9762			xOffset = Math.cos(Math.PI / 4) * radius;
9763			yOffset = Math.sin(Math.PI / 4) * radius;
9764			ctx.moveTo(-xOffset, -yOffset);
9765			ctx.lineTo(xOffset, yOffset);
9766			ctx.moveTo(-xOffset, yOffset);
9767			ctx.lineTo(xOffset, -yOffset);
9768			break;
9769		case 'line':
9770			ctx.moveTo(-radius, 0);
9771			ctx.lineTo(radius, 0);
9772			break;
9773		case 'dash':
9774			ctx.moveTo(0, 0);
9775			ctx.lineTo(radius, 0);
9776			break;
9777		}
9778
9779		ctx.fill();
9780		ctx.stroke();
9781		ctx.restore();
9782	},
9783
9784	clipArea: function(ctx, area) {
9785		ctx.save();
9786		ctx.beginPath();
9787		ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
9788		ctx.clip();
9789	},
9790
9791	unclipArea: function(ctx) {
9792		ctx.restore();
9793	},
9794
9795	lineTo: function(ctx, previous, target, flip) {
9796		if (target.steppedLine) {
9797			if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
9798				ctx.lineTo(previous.x, target.y);
9799			} else {
9800				ctx.lineTo(target.x, previous.y);
9801			}
9802			ctx.lineTo(target.x, target.y);
9803			return;
9804		}
9805
9806		if (!target.tension) {
9807			ctx.lineTo(target.x, target.y);
9808			return;
9809		}
9810
9811		ctx.bezierCurveTo(
9812			flip ? previous.controlPointPreviousX : previous.controlPointNextX,
9813			flip ? previous.controlPointPreviousY : previous.controlPointNextY,
9814			flip ? target.controlPointNextX : target.controlPointPreviousX,
9815			flip ? target.controlPointNextY : target.controlPointPreviousY,
9816			target.x,
9817			target.y);
9818	}
9819};
9820
9821// DEPRECATIONS
9822
9823/**
9824 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
9825 * @namespace Chart.helpers.clear
9826 * @deprecated since version 2.7.0
9827 * @todo remove at version 3
9828 * @private
9829 */
9830helpers.clear = exports.clear;
9831
9832/**
9833 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
9834 * @namespace Chart.helpers.drawRoundedRectangle
9835 * @deprecated since version 2.7.0
9836 * @todo remove at version 3
9837 * @private
9838 */
9839helpers.drawRoundedRectangle = function(ctx) {
9840	ctx.beginPath();
9841	exports.roundedRect.apply(exports, arguments);
9842};
9843
9844},{"43":43}],43:[function(require,module,exports){
9845'use strict';
9846
9847/**
9848 * @namespace Chart.helpers
9849 */
9850var helpers = {
9851	/**
9852	 * An empty function that can be used, for example, for optional callback.
9853	 */
9854	noop: function() {},
9855
9856	/**
9857	 * Returns a unique id, sequentially generated from a global variable.
9858	 * @returns {Number}
9859	 * @function
9860	 */
9861	uid: (function() {
9862		var id = 0;
9863		return function() {
9864			return id++;
9865		};
9866	}()),
9867
9868	/**
9869	 * Returns true if `value` is neither null nor undefined, else returns false.
9870	 * @param {*} value - The value to test.
9871	 * @returns {Boolean}
9872	 * @since 2.7.0
9873	 */
9874	isNullOrUndef: function(value) {
9875		return value === null || typeof value === 'undefined';
9876	},
9877
9878	/**
9879	 * Returns true if `value` is an array, else returns false.
9880	 * @param {*} value - The value to test.
9881	 * @returns {Boolean}
9882	 * @function
9883	 */
9884	isArray: Array.isArray ? Array.isArray : function(value) {
9885		return Object.prototype.toString.call(value) === '[object Array]';
9886	},
9887
9888	/**
9889	 * Returns true if `value` is an object (excluding null), else returns false.
9890	 * @param {*} value - The value to test.
9891	 * @returns {Boolean}
9892	 * @since 2.7.0
9893	 */
9894	isObject: function(value) {
9895		return value !== null && Object.prototype.toString.call(value) === '[object Object]';
9896	},
9897
9898	/**
9899	 * Returns `value` if defined, else returns `defaultValue`.
9900	 * @param {*} value - The value to return if defined.
9901	 * @param {*} defaultValue - The value to return if `value` is undefined.
9902	 * @returns {*}
9903	 */
9904	valueOrDefault: function(value, defaultValue) {
9905		return typeof value === 'undefined' ? defaultValue : value;
9906	},
9907
9908	/**
9909	 * Returns value at the given `index` in array if defined, else returns `defaultValue`.
9910	 * @param {Array} value - The array to lookup for value at `index`.
9911	 * @param {Number} index - The index in `value` to lookup for value.
9912	 * @param {*} defaultValue - The value to return if `value[index]` is undefined.
9913	 * @returns {*}
9914	 */
9915	valueAtIndexOrDefault: function(value, index, defaultValue) {
9916		return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
9917	},
9918
9919	/**
9920	 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
9921	 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
9922	 * @param {Function} fn - The function to call.
9923	 * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
9924	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
9925	 * @returns {*}
9926	 */
9927	callback: function(fn, args, thisArg) {
9928		if (fn && typeof fn.call === 'function') {
9929			return fn.apply(thisArg, args);
9930		}
9931	},
9932
9933	/**
9934	 * Note(SB) for performance sake, this method should only be used when loopable type
9935	 * is unknown or in none intensive code (not called often and small loopable). Else
9936	 * it's preferable to use a regular for() loop and save extra function calls.
9937	 * @param {Object|Array} loopable - The object or array to be iterated.
9938	 * @param {Function} fn - The function to call for each item.
9939	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
9940	 * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
9941	 */
9942	each: function(loopable, fn, thisArg, reverse) {
9943		var i, len, keys;
9944		if (helpers.isArray(loopable)) {
9945			len = loopable.length;
9946			if (reverse) {
9947				for (i = len - 1; i >= 0; i--) {
9948					fn.call(thisArg, loopable[i], i);
9949				}
9950			} else {
9951				for (i = 0; i < len; i++) {
9952					fn.call(thisArg, loopable[i], i);
9953				}
9954			}
9955		} else if (helpers.isObject(loopable)) {
9956			keys = Object.keys(loopable);
9957			len = keys.length;
9958			for (i = 0; i < len; i++) {
9959				fn.call(thisArg, loopable[keys[i]], keys[i]);
9960			}
9961		}
9962	},
9963
9964	/**
9965	 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
9966	 * @see http://stackoverflow.com/a/14853974
9967	 * @param {Array} a0 - The array to compare
9968	 * @param {Array} a1 - The array to compare
9969	 * @returns {Boolean}
9970	 */
9971	arrayEquals: function(a0, a1) {
9972		var i, ilen, v0, v1;
9973
9974		if (!a0 || !a1 || a0.length !== a1.length) {
9975			return false;
9976		}
9977
9978		for (i = 0, ilen = a0.length; i < ilen; ++i) {
9979			v0 = a0[i];
9980			v1 = a1[i];
9981
9982			if (v0 instanceof Array && v1 instanceof Array) {
9983				if (!helpers.arrayEquals(v0, v1)) {
9984					return false;
9985				}
9986			} else if (v0 !== v1) {
9987				// NOTE: two different object instances will never be equal: {x:20} != {x:20}
9988				return false;
9989			}
9990		}
9991
9992		return true;
9993	},
9994
9995	/**
9996	 * Returns a deep copy of `source` without keeping references on objects and arrays.
9997	 * @param {*} source - The value to clone.
vendor: 10,304 bytes, lines 9998-10429
9998	 * @returns {*}
9999	 */
10000	clone: function(source) {
10001		if (helpers.isArray(source)) {
10002			return source.map(helpers.clone);
10003		}
10004
10005		if (helpers.isObject(source)) {
10006			var target = {};
10007			var keys = Object.keys(source);
10008			var klen = keys.length;
10009			var k = 0;
10010
10011			for (; k < klen; ++k) {
10012				target[keys[k]] = helpers.clone(source[keys[k]]);
10013			}
10014
10015			return target;
10016		}
10017
10018		return source;
10019	},
10020
10021	/**
10022	 * The default merger when Chart.helpers.merge is called without merger option.
10023	 * Note(SB): this method is also used by configMerge and scaleMerge as fallback.
10024	 * @private
10025	 */
10026	_merger: function(key, target, source, options) {
10027		var tval = target[key];
10028		var sval = source[key];
10029
10030		if (helpers.isObject(tval) && helpers.isObject(sval)) {
10031			helpers.merge(tval, sval, options);
10032		} else {
10033			target[key] = helpers.clone(sval);
10034		}
10035	},
10036
10037	/**
10038	 * Merges source[key] in target[key] only if target[key] is undefined.
10039	 * @private
10040	 */
10041	_mergerIf: function(key, target, source) {
10042		var tval = target[key];
10043		var sval = source[key];
10044
10045		if (helpers.isObject(tval) && helpers.isObject(sval)) {
10046			helpers.mergeIf(tval, sval);
10047		} else if (!target.hasOwnProperty(key)) {
10048			target[key] = helpers.clone(sval);
10049		}
10050	},
10051
10052	/**
10053	 * Recursively deep copies `source` properties into `target` with the given `options`.
10054	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
10055	 * @param {Object} target - The target object in which all sources are merged into.
10056	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
10057	 * @param {Object} [options] - Merging options:
10058	 * @param {Function} [options.merger] - The merge method (key, target, source, options)
10059	 * @returns {Object} The `target` object.
10060	 */
10061	merge: function(target, source, options) {
10062		var sources = helpers.isArray(source) ? source : [source];
10063		var ilen = sources.length;
10064		var merge, i, keys, klen, k;
10065
10066		if (!helpers.isObject(target)) {
10067			return target;
10068		}
10069
10070		options = options || {};
10071		merge = options.merger || helpers._merger;
10072
10073		for (i = 0; i < ilen; ++i) {
10074			source = sources[i];
10075			if (!helpers.isObject(source)) {
10076				continue;
10077			}
10078
10079			keys = Object.keys(source);
10080			for (k = 0, klen = keys.length; k < klen; ++k) {
10081				merge(keys[k], target, source, options);
10082			}
10083		}
10084
10085		return target;
10086	},
10087
10088	/**
10089	 * Recursively deep copies `source` properties into `target` *only* if not defined in target.
10090	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
10091	 * @param {Object} target - The target object in which all sources are merged into.
10092	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
10093	 * @returns {Object} The `target` object.
10094	 */
10095	mergeIf: function(target, source) {
10096		return helpers.merge(target, source, {merger: helpers._mergerIf});
10097	},
10098
10099	/**
10100	 * Applies the contents of two or more objects together into the first object.
10101	 * @param {Object} target - The target object in which all objects are merged into.
10102	 * @param {Object} arg1 - Object containing additional properties to merge in target.
10103	 * @param {Object} argN - Additional objects containing properties to merge in target.
10104	 * @returns {Object} The `target` object.
10105	 */
10106	extend: function(target) {
10107		var setFn = function(value, key) {
10108			target[key] = value;
10109		};
10110		for (var i = 1, ilen = arguments.length; i < ilen; ++i) {
10111			helpers.each(arguments[i], setFn);
10112		}
10113		return target;
10114	},
10115
10116	/**
10117	 * Basic javascript inheritance based on the model created in Backbone.js
10118	 */
10119	inherits: function(extensions) {
10120		var me = this;
10121		var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
10122			return me.apply(this, arguments);
10123		};
10124
10125		var Surrogate = function() {
10126			this.constructor = ChartElement;
10127		};
10128
10129		Surrogate.prototype = me.prototype;
10130		ChartElement.prototype = new Surrogate();
10131		ChartElement.extend = helpers.inherits;
10132
10133		if (extensions) {
10134			helpers.extend(ChartElement.prototype, extensions);
10135		}
10136
10137		ChartElement.__super__ = me.prototype;
10138		return ChartElement;
10139	}
10140};
10141
10142module.exports = helpers;
10143
10144// DEPRECATIONS
10145
10146/**
10147 * Provided for backward compatibility, use Chart.helpers.callback instead.
10148 * @function Chart.helpers.callCallback
10149 * @deprecated since version 2.6.0
10150 * @todo remove at version 3
10151 * @private
10152 */
10153helpers.callCallback = helpers.callback;
10154
10155/**
10156 * Provided for backward compatibility, use Array.prototype.indexOf instead.
10157 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
10158 * @function Chart.helpers.indexOf
10159 * @deprecated since version 2.7.0
10160 * @todo remove at version 3
10161 * @private
10162 */
10163helpers.indexOf = function(array, item, fromIndex) {
10164	return Array.prototype.indexOf.call(array, item, fromIndex);
10165};
10166
10167/**
10168 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
10169 * @function Chart.helpers.getValueOrDefault
10170 * @deprecated since version 2.7.0
10171 * @todo remove at version 3
10172 * @private
10173 */
10174helpers.getValueOrDefault = helpers.valueOrDefault;
10175
10176/**
10177 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
10178 * @function Chart.helpers.getValueAtIndexOrDefault
10179 * @deprecated since version 2.7.0
10180 * @todo remove at version 3
10181 * @private
10182 */
10183helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
10184
10185},{}],44:[function(require,module,exports){
10186'use strict';
10187
10188var helpers = require(43);
10189
10190/**
10191 * Easing functions adapted from Robert Penner's easing equations.
10192 * @namespace Chart.helpers.easingEffects
10193 * @see http://www.robertpenner.com/easing/
10194 */
10195var effects = {
10196	linear: function(t) {
10197		return t;
10198	},
10199
10200	easeInQuad: function(t) {
10201		return t * t;
10202	},
10203
10204	easeOutQuad: function(t) {
10205		return -t * (t - 2);
10206	},
10207
10208	easeInOutQuad: function(t) {
10209		if ((t /= 0.5) < 1) {
10210			return 0.5 * t * t;
10211		}
10212		return -0.5 * ((--t) * (t - 2) - 1);
10213	},
10214
10215	easeInCubic: function(t) {
10216		return t * t * t;
10217	},
10218
10219	easeOutCubic: function(t) {
10220		return (t = t - 1) * t * t + 1;
10221	},
10222
10223	easeInOutCubic: function(t) {
10224		if ((t /= 0.5) < 1) {
10225			return 0.5 * t * t * t;
10226		}
10227		return 0.5 * ((t -= 2) * t * t + 2);
10228	},
10229
10230	easeInQuart: function(t) {
10231		return t * t * t * t;
10232	},
10233
10234	easeOutQuart: function(t) {
10235		return -((t = t - 1) * t * t * t - 1);
10236	},
10237
10238	easeInOutQuart: function(t) {
10239		if ((t /= 0.5) < 1) {
10240			return 0.5 * t * t * t * t;
10241		}
10242		return -0.5 * ((t -= 2) * t * t * t - 2);
10243	},
10244
10245	easeInQuint: function(t) {
10246		return t * t * t * t * t;
10247	},
10248
10249	easeOutQuint: function(t) {
10250		return (t = t - 1) * t * t * t * t + 1;
10251	},
10252
10253	easeInOutQuint: function(t) {
10254		if ((t /= 0.5) < 1) {
10255			return 0.5 * t * t * t * t * t;
10256		}
10257		return 0.5 * ((t -= 2) * t * t * t * t + 2);
10258	},
10259
10260	easeInSine: function(t) {
10261		return -Math.cos(t * (Math.PI / 2)) + 1;
10262	},
10263
10264	easeOutSine: function(t) {
10265		return Math.sin(t * (Math.PI / 2));
10266	},
10267
10268	easeInOutSine: function(t) {
10269		return -0.5 * (Math.cos(Math.PI * t) - 1);
10270	},
10271
10272	easeInExpo: function(t) {
10273		return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
10274	},
10275
10276	easeOutExpo: function(t) {
10277		return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
10278	},
10279
10280	easeInOutExpo: function(t) {
10281		if (t === 0) {
10282			return 0;
10283		}
10284		if (t === 1) {
10285			return 1;
10286		}
10287		if ((t /= 0.5) < 1) {
10288			return 0.5 * Math.pow(2, 10 * (t - 1));
10289		}
10290		return 0.5 * (-Math.pow(2, -10 * --t) + 2);
10291	},
10292
10293	easeInCirc: function(t) {
10294		if (t >= 1) {
10295			return t;
10296		}
10297		return -(Math.sqrt(1 - t * t) - 1);
10298	},
10299
10300	easeOutCirc: function(t) {
10301		return Math.sqrt(1 - (t = t - 1) * t);
10302	},
10303
10304	easeInOutCirc: function(t) {
10305		if ((t /= 0.5) < 1) {
10306			return -0.5 * (Math.sqrt(1 - t * t) - 1);
10307		}
10308		return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
10309	},
10310
10311	easeInElastic: function(t) {
10312		var s = 1.70158;
10313		var p = 0;
10314		var a = 1;
10315		if (t === 0) {
10316			return 0;
10317		}
10318		if (t === 1) {
10319			return 1;
10320		}
10321		if (!p) {
10322			p = 0.3;
10323		}
10324		if (a < 1) {
10325			a = 1;
10326			s = p / 4;
10327		} else {
10328			s = p / (2 * Math.PI) * Math.asin(1 / a);
10329		}
10330		return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
10331	},
10332
10333	easeOutElastic: function(t) {
10334		var s = 1.70158;
10335		var p = 0;
10336		var a = 1;
10337		if (t === 0) {
10338			return 0;
10339		}
10340		if (t === 1) {
10341			return 1;
10342		}
10343		if (!p) {
10344			p = 0.3;
10345		}
10346		if (a < 1) {
10347			a = 1;
10348			s = p / 4;
10349		} else {
10350			s = p / (2 * Math.PI) * Math.asin(1 / a);
10351		}
10352		return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
10353	},
10354
10355	easeInOutElastic: function(t) {
10356		var s = 1.70158;
10357		var p = 0;
10358		var a = 1;
10359		if (t === 0) {
10360			return 0;
10361		}
10362		if ((t /= 0.5) === 2) {
10363			return 1;
10364		}
10365		if (!p) {
10366			p = 0.45;
10367		}
10368		if (a < 1) {
10369			a = 1;
10370			s = p / 4;
10371		} else {
10372			s = p / (2 * Math.PI) * Math.asin(1 / a);
10373		}
10374		if (t < 1) {
10375			return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
10376		}
10377		return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
10378	},
10379	easeInBack: function(t) {
10380		var s = 1.70158;
10381		return t * t * ((s + 1) * t - s);
10382	},
10383
10384	easeOutBack: function(t) {
10385		var s = 1.70158;
10386		return (t = t - 1) * t * ((s + 1) * t + s) + 1;
10387	},
10388
10389	easeInOutBack: function(t) {
10390		var s = 1.70158;
10391		if ((t /= 0.5) < 1) {
10392			return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
10393		}
10394		return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
10395	},
10396
10397	easeInBounce: function(t) {
10398		return 1 - effects.easeOutBounce(1 - t);
10399	},
10400
10401	easeOutBounce: function(t) {
10402		if (t < (1 / 2.75)) {
10403			return 7.5625 * t * t;
10404		}
10405		if (t < (2 / 2.75)) {
10406			return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
10407		}
10408		if (t < (2.5 / 2.75)) {
10409			return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
10410		}
10411		return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
10412	},
10413
10414	easeInOutBounce: function(t) {
10415		if (t < 0.5) {
10416			return effects.easeInBounce(t * 2) * 0.5;
10417		}
10418		return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
10419	}
10420};
10421
10422module.exports = {
10423	effects: effects
10424};
10425
10426// DEPRECATIONS
10427
10428/**
10429 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
10430 * @function Chart.helpers.easingEffects
10431 * @deprecated since version 2.7.0
10432 * @todo remove at version 3
10433 * @private
10434 */
10435helpers.easingEffects = effects;
10436
10437},{"43":43}],45:[function(require,module,exports){
10438'use strict';
10439
10440var helpers = require(43);
10441
10442/**
10443 * @alias Chart.helpers.options
10444 * @namespace
10445 */
10446module.exports = {
10447	/**
10448	 * Converts the given line height `value` in pixels for a specific font `size`.
10449	 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
10450	 * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
10451	 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
10452	 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
10453	 * @since 2.7.0
10454	 */
10455	toLineHeight: function(value, size) {
10456		var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
10457		if (!matches || matches[1] === 'normal') {
10458			return size * 1.2;
10459		}
10460
10461		value = +matches[2];
10462
10463		switch (matches[3]) {
10464		case 'px':
10465			return value;
10466		case '%':
10467			value /= 100;
10468			break;
10469		default:
10470			break;
10471		}
10472
10473		return size * value;
10474	},
10475
10476	/**
10477	 * Converts the given value into a padding object with pre-computed width/height.
10478	 * @param {Number|Object} value - If a number, set the value to all TRBL component,
10479	 *  else, if and object, use defined properties and sets undefined ones to 0.
10480	 * @returns {Object} The padding values (top, right, bottom, left, width, height)
10481	 * @since 2.7.0
10482	 */
10483	toPadding: function(value) {
10484		var t, r, b, l;
10485
10486		if (helpers.isObject(value)) {
10487			t = +value.top || 0;
10488			r = +value.right || 0;
10489			b = +value.bottom || 0;
10490			l = +value.left || 0;
10491		} else {
10492			t = r = b = l = +value || 0;
10493		}
10494
10495		return {
10496			top: t,
10497			right: r,
10498			bottom: b,
10499			left: l,
10500			height: t + b,
10501			width: l + r
10502		};
10503	},
10504
10505	/**
10506	 * Evaluates the given `inputs` sequentially and returns the first defined value.
10507	 * @param {Array[]} inputs - An array of values, falling back to the last value.
10508	 * @param {Object} [context] - If defined and the current value is a function, the value
10509	 * is called with `context` as first argument and the result becomes the new input.
10510	 * @param {Number} [index] - If defined and the current value is an array, the value
10511	 * at `index` become the new input.
10512	 * @since 2.7.0
10513	 */
10514	resolve: function(inputs, context, index) {
10515		var i, ilen, value;
10516
10517		for (i = 0, ilen = inputs.length; i < ilen; ++i) {
10518			value = inputs[i];
10519			if (value === undefined) {
10520				continue;
10521			}
10522			if (context !== undefined && typeof value === 'function') {
10523				value = value(context);
10524			}
10525			if (index !== undefined && helpers.isArray(value)) {
10526				value = value[index];
10527			}
10528			if (value !== undefined) {
10529				return value;
10530			}
10531		}
10532	}
10533};
10534
10535},{"43":43}],46:[function(require,module,exports){
10536'use strict';
10537
10538module.exports = require(43);
10539module.exports.easing = require(44);
10540module.exports.canvas = require(42);
10541module.exports.options = require(45);
10542
10543},{"42":42,"43":43,"44":44,"45":45}],47:[function(require,module,exports){
10544/**
10545 * Platform fallback implementation (minimal).
10546 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
10547 */
10548
10549module.exports = {
10550	acquireContext: function(item) {
10551		if (item && item.canvas) {
10552			// Support for any object associated to a canvas (including a context2d)
10553			item = item.canvas;
10554		}
10555
10556		return item && item.getContext('2d') || null;
10557	}
10558};
10559
10560},{}],48:[function(require,module,exports){
10561/**
10562 * Chart.Platform implementation for targeting a web browser
10563 */
10564
10565'use strict';
10566
10567var helpers = require(46);
10568
10569var EXPANDO_KEY = '$chartjs';
10570var CSS_PREFIX = 'chartjs-';
10571var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
10572var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
10573var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
10574
10575/**
10576 * DOM event types -> Chart.js event types.
10577 * Note: only events with different types are mapped.
10578 * @see https://developer.mozilla.org/en-US/docs/Web/Events
10579 */
10580var EVENT_TYPES = {
10581	touchstart: 'mousedown',
10582	touchmove: 'mousemove',
10583	touchend: 'mouseup',
10584	pointerenter: 'mouseenter',
10585	pointerdown: 'mousedown',
10586	pointermove: 'mousemove',
10587	pointerup: 'mouseup',
10588	pointerleave: 'mouseout',
10589	pointerout: 'mouseout'
10590};
10591
10592/**
10593 * The "used" size is the final value of a dimension property after all calculations have
10594 * been performed. This method uses the computed style of `element` but returns undefined
10595 * if the computed style is not expressed in pixels. That can happen in some cases where
10596 * `element` has a size relative to its parent and this last one is not yet displayed,
10597 * for example because of `display: none` on a parent node.
10598 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
10599 * @returns {Number} Size in pixels or undefined if unknown.
10600 */
10601function readUsedSize(element, property) {
10602	var value = helpers.getStyle(element, property);
10603	var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
10604	return matches ? Number(matches[1]) : undefined;
10605}
10606
10607/**
10608 * Initializes the canvas style and render size without modifying the canvas display size,
10609 * since responsiveness is handled by the controller.resize() method. The config is used
10610 * to determine the aspect ratio to apply in case no explicit height has been specified.
10611 */
10612function initCanvas(canvas, config) {
10613	var style = canvas.style;
10614
10615	// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
10616	// returns null or '' if no explicit value has been set to the canvas attribute.
10617	var renderHeight = canvas.getAttribute('height');
10618	var renderWidth = canvas.getAttribute('width');
10619
10620	// Chart.js modifies some canvas values that we want to restore on de
10620stroy
10621	canvas[EXPANDO_KEY] = {
10622		initial: {
10623			height: renderHeight,
10624			width: renderWidth,
10625			style: {
10626				display: style.display,
10627				height: style.height,
10628				width: style.width
10629			}
10630		}
10631	};
10632
10633	// Force canvas to display as block to avoid extra space caused by inline
10634	// elements, which would interfere with the responsive resize process.
10635	// https://github.com/chartjs/Chart.js/issues/2538
10636	style.display = style.display || 'block';
10637
10638	if (renderWidth === null || renderWidth === '') {
10639		var displayWidth = readUsedSize(canvas, 'width');
10640		if (displayWidth !== undefined) {
10641			canvas.width = displayWidth;
10642		}
10643	}
10644
10645	if (renderHeight === null || renderHeight === '') {
10646		if (canvas.style.height === '') {
10647			// If no explicit render height and style height, let's apply the aspect ratio,
10648			// which one can be specified by the user but also by charts as default option
10649			// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
10650			canvas.height = canvas.width / (config.options.aspectRatio || 2);
10651		} else {
10652			var displayHeight = readUsedSize(canvas, 'height');
10653			if (displayWidth !== undefined) {
10654				canvas.height = displayHeight;
10655			}
10656		}
10657	}
10658
10659	return canvas;
10660}
10661
10662/**
10663 * Detects support for options object argument in addEventListener.
10664 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
10665 * @private
10666 */
10667var supportsEventListenerOptions = (function() {
10668	var supports = false;
10669	try {
10670		var options = Object.defineProperty({}, 'passive', {
10671			get: function() {
10672				supports = true;
10673			}
10674		});
10675		window.addEventListener('e', null, options);
10676	} catch (e) {
10677		// continue regardless of error
10678	}
10679	return supports;
10680}());
10681
10682// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
10683// https://github.com/chartjs/Chart.js/issues/4287
10684var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
10685
10686function addEventListener(node, type, listener) {
10687	node.addEventListener(type, listener, eventListenerOptions);
10688}
10689
10690function removeEventListener(node, type, listener) {
10691	node.removeEventListener(type, listener, eventListenerOptions);
10692}
10693
10694function createEvent(type, chart, x, y, nativeEvent) {
10695	return {
10696		type: type,
10697		chart: chart,
10698		native: nativeEvent || null,
10699		x: x !== undefined ? x : null,
10700		y: y !== undefined ? y : null,
10701	};
10702}
10703
10704function fromNativeEvent(event, chart) {
10705	var type = EVENT_TYPES[event.type] || event.type;
10706	var pos = helpers.getRelativePosition(event, chart);
10707	return createEvent(type, chart, pos.x, pos.y, event);
10708}
10709
10710function throttled(fn, thisArg) {
10711	var ticking = false;
10712	var args = [];
10713
10714	return function() {
10715		args = Array.prototype.slice.call(arguments);
10716		thisArg = thisArg || this;
10717
10718		if (!ticking) {
10719			ticking = true;
10720			helpers.requestAnimFrame.call(window, function() {
10721				ticking = false;
10722				fn.apply(thisArg, args);
10723			});
10724		}
10725	};
10726}
10727
10728// Implementation based on https://github.com/marcj/css-element-queries
10729function createResizer(handler) {
10730	var resizer = document.createElement('div');
10731	var cls = CSS_PREFIX + 'size-monitor';
10732	var maxSize = 1000000;
10733	var style =
10734		'position:absolute;' +
10735		'left:0;' +
10736		'top:0;' +
10737		'right:0;' +
10738		'bottom:0;' +
10739		'overflow:hidden;' +
10740		'pointer-events:none;' +
10741		'visibility:hidden;' +
10742		'z-index:-1;';
10743
10744	resizer.style.cssText = style;
10745	resizer.className = cls;
10746	resizer.innerHTML =
10747		'<div class="' + cls + '-expand" style="' + style + '">' +
10748			'<div style="' +
10749				'position:absolute;' +
10750				'width:' + maxSize + 'px;' +
10751				'height:' + maxSize + 'px;' +
10752				'left:0;' +
10753				'top:0">' +
10754			'</div>' +
10755		'</div>' +
10756		'<div class="' + cls + '-shrink" style="' + style + '">' +
10757			'<div style="' +
10758				'position:absolute;' +
10759				'width:200%;' +
10760				'height:200%;' +
10761				'left:0; ' +
10762				'top:0">' +
10763			'</div>' +
10764		'</div>';
10765
10766	var expand = resizer.childNodes[0];
10767	var shrink = resizer.childNodes[1];
10768
10769	resizer._reset = function() {
10770		expand.scrollLeft = maxSize;
10771		expand.scrollTop = maxSize;
10772		shrink.scrollLeft = maxSize;
10773		shrink.scrollTop = maxSize;
10774	};
10775	var onScroll = function() {
10776		resizer._reset();
10777		handler();
10778	};
10779
10780	addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
10781	addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
10782
10783	return resizer;
10784}
10785
10786// https://davidwalsh.name/detect-node-insertion
10787function watchForRender(node, handler) {
10788	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
10789	var proxy = expando.renderProxy = function(e) {
10790		if (e.animationName === CSS_RENDER_ANIMATION) {
10791			handler();
10792		}
10793	};
10794
10795	helpers.each(ANIMATION_START_EVENTS, function(type) {
10796		addEventListener(node, type, proxy);
10797	});
10798
10799	// #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
10800	// is removed then added back immediately (same animation frame?). Accessing the
10801	// `offsetParent` property will force a reflow and re-evaluate the CSS animation.
10802	// https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
10803	// https://github.com/chartjs/Chart.js/issues/4737
10804	expando.reflow = !!node.offsetParent;
10805
10806	node.classList.add(CSS_RENDER_MONITOR);
10807}
10808
10809function unwatchForRender(node) {
10810	var expando = node[EXPANDO_KEY] || {};
10811	var proxy = expando.renderProxy;
10812
10813	if (proxy) {
10814		helpers.each(ANIMATION_START_EVENTS, function(type) {
10815			removeEventListener(node, type, proxy);
10816		});
10817
10818		delete expando.renderProxy;
10819	}
10820
10821	node.classList.remove(CSS_RENDER_MONITOR);
10822}
10823
10824function addResizeListener(node, listener, chart) {
10825	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
10826
10827	// Let's keep track of this added resizer and thus avoid DOM query when removing it.
10828	var resizer = expando.resizer = createResizer(throttled(function() {
10829		if (expando.resizer) {
10830			return listener(createEvent('resize', chart));
10831		}
10832	}));
10833
10834	// The resizer needs to be attached to the node parent, so we first need to be
10835	// sure that `node` is attached to the DOM before injecting the resizer element.
10836	watchForRender(node, function() {
10837		if (expando.resizer) {
10838			var container = node.parentNode;
10839			if (container && container !== resizer.parentNode) {
10840				container.insertBefore(resizer, container.firstChild);
10841			}
10842
10843			// The container size might have changed, let's reset the resizer state.
10844			resizer._reset();
10845		}
10846	});
10847}
10848
10849function removeResizeListener(node) {
10850	var expando = node[EXPANDO_KEY] || {};
10851	var resizer = expando.resizer;
10852
10853	delete expando.resizer;
10854	unwatchForRender(node);
10855
10856	if (resizer && resizer.parentNode) {
10857		resizer.parentNode.removeChild(resizer);
10858	}
10859}
10860
10861function injectCSS(platform, css) {
10862	// http://stackoverflow.com/q/3922139
10863	var style = platform._style || document.createElement('style');
10864	if (!platform._style) {
10865		platform._style = style;
10866		css = '/* Chart.js */\n' + css;
10867		style.setAttribute('type', 'text/css');
10868		document.getElementsByTagName('head')[0].appendChild(style);
10869	}
10870
10871	style.appendChild(document.createTextNode(css));
10872}
10873
10874module.exports = {
10875	/**
10876	 * This property holds whether this platform is enabled for the current environment.
10877	 * Currently used by platform.js to select the proper implementation.
10878	 * @private
10879	 */
10880	_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
10881
10882	initialize: function() {
10883		var keyframes = 'from{opacity:0.99}to{opacity:1}';
10884
10885		injectCSS(this,
10886			// DOM rendering detection
10887			// https://davidwalsh.name/detect-node-insertion
10888			'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
10889			'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
10890			'.' + CSS_RENDER_MONITOR + '{' +
10891				'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
10892				'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
10893			'}'
10894		);
10895	},
10896
10897	acquireContext: function(item, config) {
10898		if (typeof item === 'string') {
10899			item = document.getElementById(item);
10900		} else if (item.length) {
10901			// Support for array based queries (such as jQuery)
10902			item = item[0];
10903		}
10904
10905		if (item && item.canvas) {
10906			// Support for any object associated to a canvas (including a context2d)
10907			item = item.canvas;
10908		}
10909
10910		// To prevent canvas fingerprinting, some add-ons undefine the getContext
10911		// method, for example: https://github.com/kkapsner/CanvasBlocker
10912		// https://github.com/chartjs/Chart.js/issues/2807
10913		var context = item && item.getContext && item.getContext('2d');
10914
10915		// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
10916		// inside an iframe or when running in a protected environment. We could guess the
10917		// types from their toString() value but let's keep things flexible and assume it's
10918		// a sufficient condition if the item has a context2D which has item as `canvas`.
10919		// https://github.com/chartjs/Chart.js/issues/3887
10920		// https://github.com/chartjs/Chart.js/issues/4102
10921		// https://github.com/chartjs/Chart.js/issues/4152
10922		if (context && context.canvas === item) {
10923			initCanvas(item, config);
10924			return context;
10925		}
10926
10927		return null;
10928	},
10929
10930	releaseContext: function(context) {
10931		var canvas = context.canvas;
10932		if (!canvas[EXPANDO_KEY]) {
10933			return;
10934		}
10935
10936		var initial = canvas[EXPANDO_KEY].initial;
10937		['height', 'width'].forEach(function(prop) {
10938			var value = initial[prop];
10939			if (helpers.isNullOrUndef(value)) {
10940				canvas.removeAttribute(prop);
10941			} else {
10942				canvas.setAttribute(prop, value);
10943			}
10944		});
10945
10946		helpers.each(initial.style || {}, function(value, key) {
10947			canvas.style[key] = value;
10948		});
10949
10950		// The canvas render size might have been changed (and thus the state stack discarded),
10951		// we can't use save() and restore() to restore the initial state. So make sure that at
10952		// least the canvas context is reset to the default state by setting the canvas width.
10953		// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
10954		canvas.width = canvas.width;
10955
10956		delete canvas[EXPANDO_KEY];
10957	},
10958
10959	addEventListener: function(chart, type, listener) {
10960		var canvas = chart.canvas;
10961		if (type === 'resize') {
10962			// Note: the resize event is not supported on all browsers.
10963			addResizeListener(canvas, listener, chart);
10964			return;
10965		}
10966
10967		var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
10968		var proxies = expando.proxies || (expando.proxies = {});
10969		var proxy = proxies[chart.id + '_' + type] = function(event) {
10970			listener(fromNativeEvent(event, chart));
10971		};
10972
10973		addEventListener(canvas, type, proxy);
10974	},
10975
10976	removeEventListener: function(chart, type, listener) {
10977		var canvas = chart.canvas;
10978		if (type === 'resize') {
10979			// Note: the resize event is not supported on all browsers.
10980			removeResizeListener(canvas, listener);
10981			return;
10982		}
10983
10984		var expando = listener[EXPANDO_KEY] || {};
10985		var proxies = expando.proxies || {};
10986		var proxy = proxies[chart.id + '_' + type];
10987		if (!proxy) {
10988			return;
10989		}
10990
10991		removeEventListener(canvas, type, proxy);
10992	}
10993};
10994
10995// DEPRECATIONS
10996
10997/**
10998 * Provided for backward compatibility, use EventTarget.addEventListener instead.
10999 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
11000 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
11001 * @function Chart.helpers.addEvent
11002 * @deprecated since version 2.7.0
11003 * @todo remove at version 3
11004 * @private
11005 */
11006helpers.addEvent = addEventListener;
11007
11008/**
11009 * Provided for backward compatibility, use EventTarget.removeEventListener instead.
11010 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
11011 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
11012 * @function Chart.helpers.removeEvent
11013 * @deprecated since version 2.7.0
11014 * @todo remove at version 3
11015 * @private
11016 */
11017helpers.removeEvent = removeEventListener;
11018
11019},{"46":46}],49:[function(require,module,exports){
11020'use strict';
11021
11022var helpers = require(46);
11023var basic = require(47);
11024var dom = require(48);
11025
11026// @TODO Make possible to select another platform at build time.
11027var implementation = dom._enabled ? dom : basic;
11028
11029/**
11030 * @namespace Chart.platform
11031 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
11032 * @since 2.4.0
11033 */
11034module.exports = helpers.extend({
11035	/**
11036	 * @since 2.7.0
11037	 */
11038	initialize: function() {},
11039
11040	/**
11041	 * Called at chart construction time, returns a context2d instance implementing
11042	 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
11043	 * @param {*} item - The native item from which to acquire context (platform specific)
11044	 * @param {Object} options - The chart options
11045	 * @returns {CanvasRenderingContext2D} context2d instance
11046	 */
11047	acquireContext: function() {},
11048
11049	/**
11050	 * Called at chart destruction time, releases any resources associated to the context
11051	 * previously returned by the acquireContext() method.
11052	 * @param {CanvasRenderingContext2D} context - The context2d instance
11053	 * @returns {Boolean} true if the method succeeded, else false
11054	 */
11055	releaseContext: function() {},
11056
11057	/**
11058	 * Registers the specified listener on the given chart.
11059	 * @param {Chart} chart - Chart from which to listen for event
11060	 * @param {String} type - The ({@link IEvent}) type to listen for
11061	 * @param {Function} listener - Receives a notification (an object that implements
11062	 * the {@link IEvent} interface) when an event of the specified type occurs.
11063	 */
11064	addEventListener: function() {},
11065
11066	/**
11067	 * Removes the specified listener previously registered with addEventListener.
11068	 * @param {Chart} chart -Chart from which to remove the listener
11069	 * @param {String} type - The ({@link IEvent}) type to remove
11070	 * @param {Function} listener - The listener function to remove from the event target.
11071	 */
11072	removeEventListener: function() {}
11073
11074}, implementation);
11075
11076/**
11077 * @interface IPlatform
11078 * Allows abstracting platform dependencies away from the chart
11079 * @borrows Chart.platform.acquireContext as acquireContext
11080 * @borrows Chart.platform.releaseContext as releaseContext
11081 * @borrows Chart.platform.addEventListener as addEventListener
11082 * @borrows Chart.platform.removeEventListener as removeEventListener
11083 */
11084
11085/**
11086 * @interface IEvent
11087 * @prop {String} type - The event type name, possible values are:
11088 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
11089 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
11090 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
11091 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
11092 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
11093 */
11094
11095},{"46":46,"47":47,"48":48}],50:[function(require,module,exports){
11096'use strict';
11097
11098module.exports = {};
11099module.exports.filler = require(51);
11100module.exports.legend = require(52);
11101module.exports.title = require(53);
11102
11103},{"51":51,"52":52,"53":53}],51:[function(require,module,exports){
11104/**
11105 * Plugin based on discussion from the following Chart.js issues:
11106 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
11107 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
11108 */
11109
11110'use strict';
11111
11112var defaults = require(26);
11113var elements = require(41);
11114var helpers = require(46);
11115
11116defaults._set('global', {
11117	plugins: {
11118		filler: {
11119			propagate: true
11120		}
11121	}
11122});
11123
11124var mappers = {
11125	dataset: function(source) {
11126		var index = source.fill;
11127		var chart = source.chart;
11128		var meta = chart.getDatasetMeta(index);
11129		var visible = meta && chart.isDatasetVisible(index);
11130		var points = (visible && meta.dataset._children) || [];
11131		var length = points.length || 0;
11132
11133		return !length ? null : function(point, i) {
11134			return (i < length && points[i]._view) || null;
11135		};
11136	},
11137
11138	boundary: function(source) {
11139		var boundary = source.boundary;
11140		var x = boundary ? boundary.x : null;
11141		var y = boundary ? boundary.y : null;
11142
11143		return function(point) {
11144			return {
11145				x: x === null ? point.x : x,
11146				y: y === null ? point.y : y,
11147			};
11148		};
11149	}
11150};
11151
11152// @todo if (fill[0] === '#')
11153function decodeFill(el, index, count) {
11154	var model = el._model || {};
11155	var fill = model.fill;
11156	var target;
11157
11158	if (fill === undefined) {
11159		fill = !!model.backgroundColor;
11160	}
11161
11162	if (fill === false || fill === null) {
11163		return false;
11164	}
11165
11166	if (fill === true) {
11167		return 'origin';
11168	}
11169
11170	target = parseFloat(fill, 10);
11171	if (isFinite(target) && Math.floor(target) === target) {
11172		if (fill[0] === '-' || fill[0] === '+') {
11173			target = index + target;
11174		}
11175
11176		if (target === index || target < 0 || target >= count) {
11177			return false;
11178		}
11179
11180		return target;
11181	}
11182
11183	switch (fill) {
11184	// compatibility
11185	case 'bottom':
11186		return 'start';
11187	case 'top':
11188		return 'end';
11189	case 'zero':
11190		return 'origin';
11191	// supported boundaries
11192	case 'origin':
11193	case 'start':
11194	case 'end':
11195		return fill;
11196	// invalid fill values
11197	default:
11198		return false;
11199	}
11200}
11201
11202function computeBoundary(source) {
11203	var model = source.el._model || {};
11204	var scale = source.el._scale || {};
11205	var fill = source.fill;
11206	var target = null;
11207	var horizontal;
11208
11209	if (isFinite(fill)) {
11210		return null;
11211	}
11212
11213	// Backward compatibility: until v3, we still need to support boundary values set on
11214	// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
11215	// controllers might still use it (e.g. the Smith chart).
11216
11217	if (fill === 'start') {
11218		target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
11219	} else if (fill === 'end') {
11220		target = model.scaleTop === undefined ? scale.top : model.scaleTop;
11221	} else if (model.scaleZero !== undefined) {
11222		target = model.scaleZero;
11223	} else if (scale.getBasePosition) {
11224		target = scale.getBasePosition();
11225	} else if (scale.getBasePixel) {
11226		target = scale.getBasePixel();
11227	}
11228
11229	if (target !== undefined && target !== null) {
11230		if (target.x !== undefined && target.y !== undefined) {
11231			return target;
11232		}
11233
11234		if (typeof target === 'number' && isFinite(target)) {
11235			horizontal = scale.isHorizontal();
11236			return {
11237				x: horizontal ? target : null,
11238				y: horizontal ? null : target
11239			};
11240		}
11241	}
11242
11243	return null;
11244}
11245
11246function resolveTarget(sources, index, propagate) {
11247	var source = sources[index];
11248	var fill = source.fill;
11249	var visited = [index];
11250	var target;
11251
11252	if (!propagate) {
11253		return fill;
11254	}
11255
11256	while (fill !== false && visited.indexOf(fill) === -1) {
11257		if (!isFinite(fill)) {
11258			return fill;
11259		}
11260
11261		target = sources[fill];
11262		if (!target) {
11263			return false;
11264		}
11265
11266		if (target.visible) {
11267			return fill;
11268		}
11269
11270		visited.push(fill);
11271		fill = target.fill;
11272	}
11273
11274	return false;
11275}
11276
11277function createMapper(source) {
11278	var fill = source.fill;
11279	var type = 'dataset';
11280
11281	if (fill === false) {
11282		return null;
11283	}
11284
11285	if (!isFinite(fill)) {
11286		type = 'boundary';
11287	}
11288
11289	return mappers[type](source);
11290}
11291
11292function isDrawable(point) {
11293	return point && !point.skip;
11294}
11295
11296function drawArea(ctx, curve0, curve1, len0, len1) {
11297	var i;
11298
11299	if (!len0 || !len1) {
11300		return;
11301	}
11302
11303	// building first area curve (normal)
11304	ctx.moveTo(curve0[0].x, curve0[0].y);
11305	for (i = 1; i < len0; ++i) {
11306		helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
11307	}
11308
11309	// joining the two area curves
11310	ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
11311
11312	// building opposite area curve (reverse)
11313	for (i = len1 - 1; i > 0; --i) {
11314		helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
11315	}
11316}
11317
11318function doFill(ctx, points, mapper, view, color, loop) {
11319	var count = points.length;
11320	var span = view.spanGaps;
11321	var curve0 = [];
11322	var curve1 = [];
11323	var len0 = 0;
11324	var len1 = 0;
11325	var i, ilen, index, p0, p1, d0, d1;
11326
11327	ctx.beginPath();
11328
11329	for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
11330		index = i % count;
11331		p0 = points[index]._view;
11332		p1 = mapper(p0, index, view);
11333		d0 = isDrawable(p0);
11334		d1 = isDrawable(p1);
11335
11336		if (d0 && d1) {
11337			len0 = curve0.push(p0);
11338			len1 = curve1.push(p1);
11339		} else if (len0 && len1) {
11340			if (!span) {
11341				drawArea(ctx, curve0, curve1, len0, len1);
11342				len0 = len1 = 0;
11343				curve0 = [];
11344				curve1 = [];
11345			} else {
11346				if (d0) {
11347					curve0.push(p0);
11348				}
11349				if (d1) {
11350					curve1.push(p1);
11351				}
11352			}
11353		}
11354	}
11355
11356	drawArea(ctx, curve0, curve1, len0, len1);
11357
11358	ctx.closePath();
11359	ctx.fillStyle = color;
11360	ctx.fill();
11361}
11362
11363module.exports = {
11364	id: 'filler',
11365
11366	afterDatasetsUpdate: function(chart, options) {
11367		var count = (chart.data.datasets || []).length;
11368		var propagate = options.propagate;
11369		var sources = [];
11370		var meta, i, el, source;
11371
11372		for (i = 0; i < count; ++i) {
11373			meta = chart.getDatasetMeta(i);
11374			el = meta.dataset;
11375			source = null;
11376
11377			if (el && el._model && el instanceof elements.Line) {
11378				source = {
11379					visible: chart.isDatasetVisible(i),
11380					fill: decodeFill(el, i, count),
11381					chart: chart,
11382					el: el
11383				};
11384			}
11385
11386			meta.$filler = source;
11387			sources.push(source);
11388		}
11389
11390		for (i = 0; i < count; ++i) {
11391			source = sources[i];
11392			if (!source) {
11393				continue;
11394			}
11395
11396			source.fill = resolveTarget(sources, i, propagate);
11397			source.boundary = computeBoundary(source);
11398			source.mapper = createMapper(source);
11399		}
11400	},
11401
11402	beforeDatasetDraw: function(chart, args) {
11403		var meta = args.meta.$filler;
11404		if (!meta) {
11405			return;
11406		}
11407
11408		var ctx = chart.ctx;
11409		var el = meta.el;
11410		var view = el._view;
11411		var points = el._children || [];
11412		var mapper = meta.mapper;
11413		var color = view.backgroundColor || defaults.global.defaultColor;
11414
11415		if (mapper && color && points.length) {
11416			helpers.canvas.clipArea(ctx, chart.chartArea);
11417			doFill(ctx, points, mapper, view, color, el._loop);
11418			helpers.canvas.unclipArea(ctx);
11419		}
11420	}
11421};
11422
11423},{"26":26,"41":41,"46":46}],52:[function(require,module,exports){
11424'use strict';
11425
11426var defaults = require(26);
11427var Element = require(27);
11428var helpers = require(46);
11429var layouts = require(31);
11430
11431var noop = helpers.noop;
11432
11433defaults._set('global', {
11434	legend: {
11435		display: true,
11436		position: 'top',
11437		fullWidth: true,
11438		reverse: false,
11439		weight: 1000,
11440
11441		// a callback that will handle
11442		onClick: function(e, legendItem) {
11443			var index = legendItem.datasetIndex;
11444			var ci = this.chart;
11445			var meta = ci.getDatasetMeta(index);
11446
11447			// See controller.isDatasetVisible comment
11448			meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
11449
11450			// We hid a dataset ... rerender the chart
11451			ci.update();
11452		},
11453
11454		onHover: null,
11455
11456		labels: {
11457			boxWidth: 40,
11458			padding: 10,
11459			// Generates labels shown in the legend
11460			// Valid properties to return:
11461			// text : text to display
11462			// fillStyle : fill of coloured box
11463			// strokeStyle: stroke of coloured box
11464			// hidden : if this legend item refers to a hidden item
11465			// lineCap : cap style for line
11466			// lineDash
11467			// lineDashOffset :
11468			// lineJoin :
11469			// lineWidth :
11470			generateLabels: function(chart) {
11471				var data = chart.data;
11472				return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
11473					return {
11474						text: dataset.label,
11475						fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
11476						hidden: !chart.isDatasetVisible(i),
11477						lineCap: dataset.borderCapStyle,
11478						lineDash: dataset.borderDash,
11479						lineDashOffset: dataset.borderDashOffset,
11480						lineJoin: dataset.borderJoinStyle,
11481						lineWidth: dataset.borderWidth,
11482						strokeStyle: dataset.borderColor,
11483						pointStyle: dataset.pointStyle,
11484
11485						// Below is extra data used for toggling the datasets
11486						datasetIndex: i
11487					};
11488				}, this) : [];
11489			}
11490		}
11491	},
11492
11493	legendCallback: function(chart) {
11494		var text = [];
11495		text.push('<ul class="' + chart.id + '-legend">');
11496		for (var i = 0; i < chart.data.datasets.length; i++) {
11497			text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
11498			if (chart.data.datasets[i].label) {
11499				text.push(chart.data.datasets[i].label);
11500			}
11501			text.push('</li>');
11502		}
11503		text.push('</ul>');
11504		return text.join('');
11505	}
11506});
11507
11508/**
11509 * Helper function to get the box width based on the usePointStyle option
11510 * @param labelopts {Object} the label options on the legend
11511 * @param fontSize {Number} the label font size
11512 * @return {Number} width of the color box area
11513 */
11514function getBoxWidth(labelOpts, fontSize) {
11515	return labelOpts.usePointStyle ?
11516		fontSize * Math.SQRT2 :
11517		labelOpts.boxWidth;
11518}
11519
11520/**
11521 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
11522 */
11523var Legend = Element.extend({
11524
11525	initialize: function(config) {
11526		helpers.extend(this, config);
11527
11528		// Contains hit boxes for each dataset (in dataset order)
11529		this.legendHitBoxes = [];
11530
11531		// Are we in doughnut mode which has a different data type
11532		this.doughnutMode = false;
11533	},
11534
11535	// These methods are ordered by lifecycle. Utilities then follow.
11536	// Any function defined here is inherited by all legend types.
11537	// Any function can be extended by the legend type
11538
11539	beforeUpdate: noop,
11540	update: function(maxWidth, maxHeight, margins) {
11541		var me = this;
11542
11543		// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
11544		me.beforeUpdate();
11545
11546		// Absorb the master measurements
11547		me.maxWidth = maxWidth;
11548		me.maxHeight = maxHeight;
11549		me.margins = margins;
11550
11551		// Dimensions
11552		me.beforeSetDimensions();
11553		me.setDimensions();
11554		me.afterSetDimensions();
11555		// Labels
11556		me.beforeBuildLabels();
11557		me.buildLabels();
11558		me.afterBuildLabels();
11559
11560		// Fit
11561		me.beforeFit();
11562		me.fit();
11563		me.afterFit();
11564		//
11565		me.afterUpdate();
11566
11567		return me.minSize;
11568	},
11569	afterUpdate: noop,
11570
11571	//
11572
11573	beforeSetDimensions: noop,
11574	setDimensions: function() {
11575		var me = this;
11576		// Set the unconstrained dimension before label rotation
11577		if (me.isHorizontal()) {
11578			// Reset position before calculating rotation
11579			me.width = me.maxWidth;
11580			me.left = 0;
11581			me.right = me.width;
11582		} else {
11583			me.height = me.maxHeight;
11584
11585			// Reset position before calculating rotation
11586			me.top = 0;
11587			me.bottom = me.height;
11588		}
11589
11590		// Reset padding
11591		me.paddingLeft = 0;
11592		me.paddingTop = 0;
11593		me.paddingRight = 0;
11594		me.paddingBottom = 0;
11595
11596		// Reset minSize
11597		me.minSize = {
11598			width: 0,
11599			height: 0
11600		};
11601	},
11602	afterSetDimensions: noop,
11603
11604	//
11605
11606	beforeBuildLabels: noop,
11607	buildLabels: function() {
11608		var me = this;
11609		var labelOpts = me.options.labels || {};
11610		var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
11611
11612		if (labelOpts.filter) {
11613			legendItems = legendItems.filter(function(item) {
11614				return labelOpts.filter(item, me.chart.data);
11615			});
11616		}
11617
11618		if (me.options.reverse) {
11619			legendItems.reverse();
11620		}
11621
11622		me.legendItems = legendItems;
11623	},
11624	afterBuildLabels: noop,
11625
11626	//
11627
11628	beforeFit: noop,
11629	fit: function() {
11630		var me = this;
11631		var opts = me.options;
11632		var labelOpts = opts.labels;
11633		var display = opts.display;
11634
11635		var ctx = me.ctx;
11636
11637		var globalDefault = defaults.global;
11638		var valueOrDefault = helpers.valueOrDefault;
11639		var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
11640		var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
11641		var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
11642		var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
11643
11644		// Reset hit boxes
11645		var hitboxes = me.legendHitBoxes = [];
11646
11647		var minSize = me.minSize;
11648		var isHorizontal = me.isHorizontal();
11649
11650		if (isHorizontal) {
11651			minSize.width = me.maxWidth; // fill all the width
11652			minSize.height = display ? 10 : 0;
11653		} else {
11654			minSize.width = display ? 10 : 0;
11655			minSize.height = me.maxHeight; // fill all the height
11656		}
11657
11658		// Increase sizes here
11659		if (display) {
11660			ctx.font = labelFont;
11661
11662			if (isHorizontal) {
11663				// Labels
11664
11665				// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
11666				var lineWidths = me.lineWidths = [0];
11667				var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
11668
11669				ctx.textAlign = 'left';
11670				ctx.textBaseline = 'top';
11671
11672				helpers.each(me.legendItems, function(legendItem, i) {
11673					var boxWidth = getBoxWidth(labelOpts, fontSize);
11674					var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
11675
11676					if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
11677						totalHeight += fontSize + (labelOpts.padding);
11678						lineWidths[lineWidths.length] = me.left;
11679					}
11680
11681					// Store the hitbox width and height here. Final position will be updated in `draw`
11682					hitboxes[i] = {
11683						left: 0,
11684						top: 0,
11685						width: width,
11686						height: fontSize
11687					};
11688
11689					lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
11690				});
11691
11692				minSize.height += totalHeight;
11693
11694			} else {
11695				var vPadding = labelOpts.padding;
11696				var columnWidths = me.columnWidths = [];
11697				var totalWidth = labelOpts.padding;
11698				var currentColWidth = 0;
11699				var currentColHeight = 0;
11700				var itemHeight = fontSize + vPadding;
11701
11702				helpers.each(me.legendItems, function(legendItem, i) {
11703					var boxWidth = getBoxWidth(labelOpts, fontSize);
11704					var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
11705
11706					// If too tall, go to new column
11707					if (currentColHeight + itemHeight > minSize.height) {
11708						totalWidth += currentColWidth + labelOpts.padding;
11709						columnWidths.push(currentColWidth); // previous column width
11710
11711						currentColWidth = 0;
11712						currentColHeight = 0;
11713					}
11714
11715					// Get max width
11716					currentColWidth = Math.max(currentColWidth, itemWidth);
11717					currentColHeight += itemHeight;
11718
11719					// Store the hitbox width and height here. Final position will be updated in `draw`
11720					hitboxes[i] = {
11721						left: 0,
11722						top: 0,
11723						width: itemWidth,
11724						height: fontSize
11725					};
11726				});
11727
11728				totalWidth += currentColWidth;
11729				columnWidths.push(currentColWidth);
11730				minSize.width += totalWidth;
11731			}
11732		}
11733
11734		me.width = minSize.width;
11735		me.height = minSize.height;
11736	},
11737	afterFit: noop,
11738
11739	// Shared Methods
11740	isHorizontal: function() {
11741		return this.options.position === 'top' || this.options.position === 'bottom';
11742	},
11743
11744	// Actually draw the legend on the canvas
11745	draw: function() {
11746		var me = this;
11747		var opts = me.options;
11748		var labelOpts = opts.labels;
11749		var globalDefault = defaults.global;
11750		var lineDefault = globalDefault.elements.line;
11751		var legendWidth = me.width;
11752		var lineWidths = me.lineWidths;
11753
11754		if (opts.display) {
11755			var ctx = me.ctx;
11756			var valueOrDefault = helpers.valueOrDefault;
11757			var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
11758			var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
11759			var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
11760			var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
11761			var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
11762			var cursor;
11763
11764			// Canvas setup
11765			ctx.textAlign = 'left';
11766			ctx.textBaseline = 'middle';
11767			ctx.lineWidth = 0.5;
11768			ctx.strokeStyle = fontColor; // for strikethrough effect
11769			ctx.fillStyle = fontColor; // render in correct colour
11770			ctx.font = labelFont;
11771
11772			var boxWidth = getBoxWidth(labelOpts, fontSize);
11773			var hitboxes = me.legendHitBoxes;
11774
11775			// current position
11776			var drawLegendBox = function(x, y, legendItem) {
11777				if (isNaN(boxWidth) || boxWidth <= 0) {
11778					return;
11779				}
11780
11781				// Set the ctx for the box
11782				ctx.save();
11783
11784				ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
11785				ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
11786				ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
11787				ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
11788				ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
11789				ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
11790				var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
11791
11792				if (ctx.setLineDash) {
11793					// IE 9 and 10 do not support line dash
11794					ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
11795				}
11796
11797				if (opts.labels && opts.labels.usePointStyle) {
11798					// Recalculate x and y for drawPoint() because its expecting
11799					// x and y to be center of figure (instead of top left)
11800					var radius = fontSize * Math.SQRT2 / 2;
11801					var offSet = radius / Math.SQRT2;
11802					var centerX = x + offSet;
11803					var centerY = y + offSet;
11804
11805					// Draw pointStyle as legend symbol
11806					helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
11807				} else {
11808					// Draw box as legend symbol
11809					if (!isLineWidthZero) {
11810						ctx.strokeRect(x, y, boxWidth, fontSize);
11811					}
11812					ctx.fillRect(x, y, boxWidth, fontSize);
11813				}
11814
11815				ctx.restore();
11816			};
11817			var fillText = function(x, y, legendItem, textWidth) {
11818				var halfFontSize = fontSize / 2;
11819				var xLeft = boxWidth + halfFontSize + x;
11820				var yMiddle = y + halfFontSize;
11821
11822				ctx.fillText(legendItem.text, xLeft, yMiddle);
11823
11824				if (legendItem.hidden) {
11825					// Strikethrough the text if hidden
11826					ctx.beginPath();
11827					ctx.lineWidth = 2;
11828					ctx.moveTo(xLeft, yMiddle);
11829					ctx.lineTo(xLeft + textWidth, yMiddle);
11830					ctx.stroke();
11831				}
11832			};
11833
11834			// Horizontal
11835			var isHorizontal = me.isHorizontal();
11836			if (isHorizontal) {
11837				cursor = {
11838					x: me.left + ((legendWidth - lineWidths[0]) / 2),
11839					y: me.top + labelOpts.padding,
11840					line: 0
11841				};
11842			} else {
11843				cursor = {
11844					x: me.left + labelOpts.padding,
11845					y: me.top + labelOpts.padding,
11846					line: 0
11847				};
11848			}
11849
11850			var itemHeight = fontSize + labelOpts.padding;
11851			helpers.each(me.legendItems, function(legendItem, i) {
11852				var textWidth = ctx.measureText(legendItem.text).width;
11853				var width = boxWidth + (fontSize / 2) + textWidth;
11854				var x = cursor.x;
11855				var y = cursor.y;
11856
11857				if (isHorizontal) {
11858					if (x + width >= legendWidth) {
11859						y = cursor.y += itemHeight;
11860						cursor.line++;
11861						x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
11862					}
11863				} else if (y + itemHeight > me.bottom) {
11864					x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
11865					y = cursor.y = me.top + labelOpts.padding;
11866					cursor.line++;
11867				}
11868
11869				drawLegendBox(x, y, legendItem);
11870
11871				hitboxes[i].left = x;
11872				hitboxes[i].top = y;
11873
11874				// Fill the actual label
11875				fillText(x, y, legendItem, textWidth);
11876
11877				if (isHorizontal) {
11878					cursor.x += width + (labelOpts.padding);
11879				} else {
11880					cursor.y += itemHeight;
11881				}
11882
11883			});
11884		}
11885	},
11886
11887	/**
11888	 * Handle an event
11889	 * @private
11890	 * @param {IEvent} event - The event to handle
11891	 * @return {Boolean} true if a change occured
11892	 */
11893	handleEvent: function(e) {
11894		var me = this;
11895		var opts = me.options;
11896		var type = e.type === 'mouseup' ? 'click' : e.type;
11897		var changed = false;
11898
11899		if (type === 'mousemove') {
11900			if (!opts.onHover) {
11901				return;
11902			}
11903		} else if (type === 'click') {
11904			if (!opts.onClick) {
11905				return;
11906			}
11907		} else {
11908			return;
11909		}
11910
11911		// Chart event already has relative position in it
11912		var x = e.x;
11913		var y = e.y;
11914
11915		if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
11916			// See if we are touching one of the dataset boxes
11917			var lh = me.legendHitBoxes;
11918			for (var i = 0; i < lh.length; ++i) {
11919				var hitBox = lh[i];
11920
11921				if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
11922					// Touching an element
11923					if (type === 'click') {
11924						// use e.native for backwards compatibility
11925						opts.onClick.call(me, e.native, me.legendItems[i]);
11926						changed = true;
11927						break;
11928					} else if (type === 'mousemove') {
11929						// use e.native for backwards compatibility
11930						opts.onHover.call(me, e.native, me.legendItems[i]);
11931						changed = true;
11932						break;
11933					}
11934				}
11935			}
11936		}
11937
11938		return changed;
11939	}
11940});
11941
11942function createNewLegendAndAttach(chart, legendOpts) {
11943	var legend = new Legend({
11944		ctx: chart.ctx,
11945		options: legendOpts,
11946		chart: chart
11947	});
11948
11949	layouts.configure(chart, legend, legendOpts);
11950	layouts.addBox(chart, legend);
11951	chart.legend = legend;
11952}
11953
11954module.exports = {
11955	id: 'legend',
11956
11957	/**
11958	 * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
11959	 * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
11960	 * the plugin, which one will be re-exposed in the chart.js file.
11961	 * https://github.com/chartjs/Chart.js/pull/2640
11962	 * @private
11963	 */
11964	_element: Legend,
11965
11966	beforeInit: function(chart) {
11967		var legendOpts = chart.options.legend;
11968
11969		if (legendOpts) {
11970			createNewLegendAndAttach(chart, legendOpts);
11971		}
11972	},
11973
11974	beforeUpdate: function(chart) {
11975		var legendOpts = chart.options.legend;
11976		var legend = chart.legend;
11977
11978		if (legendOpts) {
11979			helpers.mergeIf(legendOpts, defaults.global.legend);
11980
11981			if (legend) {
11982				layouts.configure(chart, legend, legendOpts);
11983				legend.options = legendOpts;
11984			} else {
11985				createNewLegendAndAttach(chart, legendOpts);
11986			}
11987		} else if (legend) {
11988			layouts.removeBox(chart, legend);
11989			delete chart.legend;
11990		}
11991	},
11992
11993	afterEvent: function(chart, e) {
11994		var legend = chart.legend;
11995		if (legend) {
11996			legend.handleEvent(e);
11997		}
11998	}
11999};
12000
12001},{"26":26,"27":27,"31":31,"46":46}],53:[function(require,module,exports){
12002'use strict';
12003
12004var defaults = require(26);
12005var Element = require(27);
12006var helpers = require(46);
12007var layouts = require(31);
12008
12009var noop = helpers.noop;
12010
12011defaults._set('global', {
12012	title: {
12013		display: false,
12014		fontStyle: 'bold',
12015		fullWidth: true,
12016		lineHeight: 1.2,
12017		padding: 10,
12018		position: 'top',
12019		text: '',
12020		weight: 2000         // by default greater than legend (1000) to be above
12021	}
12022});
12023
12024/**
12025 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
12026 */
12027var Title = Element.extend({
12028	initialize: function(config) {
12029		var me = this;
12030		helpers.extend(me, config);
12031
12032		// Contains hit boxes for each dataset (in dataset order)
12033		me.legendHitBoxes = [];
12034	},
12035
12036	// These methods are ordered by lifecycle. Utilities then follow.
12037
12038	beforeUpdate: noop,
12039	update: function(maxWidth, maxHeight, margins) {
12040		var me = this;
12041
12042		// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
12043		me.beforeUpdate();
12044
12045		// Absorb the master measurements
12046		me.maxWidth = maxWidth;
12047		me.maxHeight = maxHeight;
12048		me.margins = margins;
12049
12050		// Dimensions
12051		me.beforeSetDimensions();
12052		me.setDimensions();
12053		me.afterSetDimensions();
12054		// Labels
12055		me.beforeBuildLabels();
12056		me.buildLabels();
12057		me.afterBuildLabels();
12058
12059		// Fit
12060		me.beforeFit();
12061		me.fit();
12062		me.afterFit();
12063		//
12064		me.afterUpdate();
12065
12066		return me.minSize;
12067
12068	},
12069	afterUpdate: noop,
12070
12071	//
12072
12073	beforeSetDimensions: noop,
12074	setDimensions: function() {
12075		var me = this;
12076		// Set the unconstrained dimension before label rotation
12077		if (me.isHorizontal()) {
12078			// Reset position before calculating rotation
12079			me.width = me.maxWidth;
12080			me.left = 0;
12081			me.right = me.width;
12082		} else {
12083			me.height = me.maxHeight;
12084
12085			// Reset position before calculating rotation
12086			me.top = 0;
12087			me.bottom = me.height;
12088		}
12089
12090		// Reset padding
12091		me.paddingLeft = 0;
12092		me.paddingTop = 0;
12093		me.paddingRight = 0;
12094		me.paddingBottom = 0;
12095
12096		// Reset minSize
12097		me.minSize = {
12098			width: 0,
12099			height: 0
12100		};
12101	},
12102	afterSetDimensions: noop,
12103
12104	//
12105
12106	beforeBuildLabels: noop,
12107	buildLabels: noop,
12108	afterBuildLabels: noop,
12109
12110	//
12111
12112	beforeFit: noop,
12113	fit: function() {
12114		var me = this;
12115		var valueOrDefault = helpers.valueOrDefault;
12116		var opts = me.options;
12117		var display = opts.display;
12118		var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
12119		var minSize = me.minSize;
12120		var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
12121		var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
12122		var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
12123
12124		if (me.isHorizontal()) {
12125			minSize.width = me.maxWidth; // fill all the width
12126			minSize.height = textSize;
12127		} else {
12128			minSize.width = textSize;
12129			minSize.height = me.maxHeight; // fill all the height
12130		}
12131
12132		me.width = minSize.width;
12133		me.height = minSize.height;
12134
12135	},
12136	afterFit: noop,
12137
12138	// Shared Methods
12139	isHorizontal: function() {
12140		var pos = this.options.position;
12141		return pos === 'top' || pos === 'bottom';
12142	},
12143
12144	// Actually draw the title block on the canvas
12145	draw: function() {
12146		var me = this;
12147		var ctx = me.ctx;
12148		var valueOrDefault = helpers.valueOrDefault;
12149		var opts = me.options;
12150		var globalDefaults = defaults.global;
12151
12152		if (opts.display) {
12153			var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
12154			var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
12155			var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
12156			var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
12157			var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
12158			var offset = lineHeight / 2 + opts.padding;
12159			var rotation = 0;
12160			var top = me.top;
12161			var left = me.left;
12162			var bottom = me.bottom;
12163			var right = me.right;
12164			var maxWidth, titleX, titleY;
12165
12166			ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
12167			ctx.font = titleFont;
12168
12169			// Horizontal
12170			if (me.isHorizontal()) {
12171				titleX = left + ((right - left) / 2); // midpoint of the width
12172				titleY = top + offset;
12173				maxWidth = right - left;
12174			} else {
12175				titleX = opts.position === 'left' ? left + offset : right - offset;
12176				titleY = top + ((bottom - top) / 2);
12177				maxWidth = bottom - top;
12178				rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
12179			}
12180
12181			ctx.save();
12182			ctx.translate(titleX, titleY);
12183			ctx.rotate(rotation);
12184			ctx.textAlign = 'center';
12185			ctx.textBaseline = 'middle';
12186
12187			var text = opts.text;
12188			if (helpers.isArray(text)) {
12189				var y = 0;
12190				for (var i = 0; i < text.length; ++i) {
12191					ctx.fillText(text[i], 0, y, maxWidth);
12192					y += lineHeight;
12193				}
12194			} else {
12195				ctx.fillText(text, 0, 0, maxWidth);
12196			}
12197
12198			ctx.restore();
12199		}
12200	}
12201});
12202
12203function createNewTitleBlockAndAttach(chart, titleOpts) {
12204	var title = new Title({
12205		ctx: chart.ctx,
12206		options: titleOpts,
12207		chart: chart
12208	});
12209
12210	layouts.configure(chart, title, titleOpts);
12211	layouts.addBox(chart, title);
12212	chart.titleBlock = title;
12213}
12214
12215module.exports = {
12216	id: 'title',
12217
12218	/**
12219	 * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
12220	 * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
12221	 * the plugin, which one will be re-exposed in the chart.js file.
12222	 * https://github.com/chartjs/Chart.js/pull/2640
12223	 * @private
12224	 */
12225	_element: Title,
12226
12227	beforeInit: function(chart) {
12228		var titleOpts = chart.options.title;
12229
12230		if (titleOpts) {
12231			createNewTitleBlockAndAttach(chart, titleOpts);
12232		}
12233	},
12234
12235	beforeUpdate: function(chart) {
12236		var titleOpts = chart.options.title;
12237		var titleBlock = chart.titleBlock;
12238
12239		if (titleOpts) {
12240			helpers.mergeIf(titleOpts, defaults.global.title);
12241
12242			if (titleBlock) {
12243				layouts.configure(chart, titleBlock, titleOpts);
12244				titleBlock.options = titleOpts;
12245			} else {
12246				createNewTitleBlockAndAttach(chart, titleOpts);
12247			}
12248		} else if (titleBlock) {
12249			layouts.removeBox(chart, titleBlock);
12250			delete chart.titleBlock;
12251		}
12252	}
12253};
12254
12255},{"26":26,"27":27,"31":31,"46":46}],54:[function(require,module,exports){
12256'use strict';
12257
12258var Scale = require(33);
12259var scaleService = require(34);
12260
12261module.exports = function() {
12262
12263	// Default config for a category scale
12264	var defaultConfig = {
12265		position: 'bottom'
12266	};
12267
12268	var DatasetScale = Scale.extend({
12269		/**
12270		* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
12271		* else fall back to data.labels
12272		* @private
12273		*/
12274		getLabels: function() {
12275			var data = this.chart.data;
12276			return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
12277		},
12278
12279		determineDataLimits: function() {
12280			var me = this;
12281			var labels = me.getLabels();
12282			me.minIndex = 0;
12283			me.maxIndex = labels.length - 1;
12284			var findIndex;
12285
12286			if (me.options.ticks.min !== undefined) {
12287				// user specified min value
12288				findIndex = labels.indexOf(me.options.ticks.min);
12289				me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
12290			}
12291
12292			if (me.options.ticks.max !== undefined) {
12293				// user specified max value
12294				findIndex = labels.indexOf(me.options.ticks.max);
12295				me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
12296			}
12297
12298			me.min = labels[me.minIndex];
12299			me.max = labels[me.maxIndex];
12300		},
12301
12302		buildTicks: function() {
12303			var me = this;
12304			var labels = me.getLabels();
12305			// If we are viewing some subset of labels, slice the original array
12306			me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
12307		},
12308
12309		getLabelForIndex: function(index, datasetIndex) {
12310			var me = this;
12311			var data = me.chart.data;
12312			var isHorizontal = me.isHorizontal();
12313
12314			if (data.yLabels && !isHorizontal) {
12315				return me.getRightValue(data.datasets[datasetIndex].data[index]);
12316			}
12317			return me.ticks[index - me.minIndex];
12318		},
12319
12320		// Used to get data value locations.  Value can either be an index or a numerical value
12321		getPixelForValue: function(value, index) {
12322			var me = this;
12323			var offset = me.options.offset;
12324			// 1 is added because we need the length but we have the indexes
12325			var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
12326
12327			// If value is a data object, then index is the index in the data array,
12328			// not the index of the scale. We need to change that.
12329			var valueCategory;
12330			if (value !== undefined && value !== null) {
12331				valueCategory = me.isHorizontal() ? value.x : value.y;
12332			}
12333			if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
12334				var labels = me.getLabels();
12335				value = valueCategory || value;
12336				var idx = labels.indexOf(value);
12337				index = idx !== -1 ? idx : index;
12338			}
12339
12340			if (me.isHorizontal()) {
12341				var valueWidth = me.width / offsetAmt;
12342				var widthOffset = (valueWidth * (index - me.minIndex));
12343
12344				if (offset) {
12345					widthOffset += (valueWidth / 2);
12346				}
12347
12348				return me.left + Math.round(widthOffset);
12349			}
12350			var valueHeight = me.height / offsetAmt;
12351			var heightOffset = (valueHeight * (index - me.minIndex));
12352
12353			if (offset) {
12354				heightOffset += (valueHeight / 2);
12355			}
12356
12357			return me.top + Math.round(heightOffset);
12358		},
12359		getPixelForTick: function(index) {
12360			return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
12361		},
12362		getValueForPixel: function(pixel) {
12363			var me = this;
12364			var offset = me.options.offset;
12365			var value;
12366			var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
12367			var horz = me.isHorizontal();
12368			var valueDimension = (horz ? me.width : me.height) / offsetAmt;
12369
12370			pixel -= horz ? me.left : me.top;
12371
12372			if (offset) {
12373				pixel -= (valueDimension / 2);
12374			}
12375
12376			if (pixel <= 0) {
12377				value = 0;
12378			} else {
12379				value = Math.round(pixel / valueDimension);
12380			}
12381
12382			return value + me.minIndex;
12383		},
12384		getBasePixel: function() {
12385			return this.bottom;
12386		}
12387	});
12388
12389	scaleService.registerScaleType('category', DatasetScale, defaultConfig);
12390};
12391
12392},{"33":33,"34":34}],55:[function(require,module,exports){
12393'use strict';
12394
12395var defaults = require(26);
12396var helpers = require(46);
12397var scaleService = require(34);
12398var Ticks = require(35);
12399
12400module.exports = function(Chart) {
12401
12402	var defaultConfig = {
12403		position: 'left',
12404		ticks: {
12405			callback: Ticks.formatters.linear
12406		}
12407	};
12408
12409	var LinearScale = Chart.LinearScaleBase.extend({
12410
12411		determineDataLimits: function() {
12412			var me = this;
12413			var opts = me.options;
12414			var chart = me.chart;
12415			var data = chart.data;
12416			var datasets = data.datasets;
12417			var isHorizontal = me.isHorizontal();
12418			var DEFAULT_MIN = 0;
12419			var DEFAULT_MAX = 1;
12420
12421			function IDMatches(meta) {
12422				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
12423			}
12424
12425			// First Calculate the range
12426			me.min = null;
12427			me.max = null;
12428
12429			var hasStacks = opts.stacked;
12430			if (hasStacks === undefined) {
12431				helpers.each(datasets, function(dataset, datasetIndex) {
12432					if (hasStacks) {
12433						return;
12434					}
12435
12436					var meta = chart.getDatasetMeta(datasetIndex);
12437					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
12438						meta.stack !== undefined) {
12439						hasStacks = true;
12440					}
12441				});
12442			}
12443
12444			if (opts.stacked || hasStacks) {
12445				var valuesPerStack = {};
12446
12447				helpers.each(datasets, function(dataset, datasetIndex) {
12448					var meta = chart.getDatasetMeta(datasetIndex);
12449					var key = [
12450						meta.type,
12451						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12452						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
12453						meta.stack
12454					].join('.');
12455
12456					if (valuesPerStack[key] === undefined) {
12457						valuesPerStack[key] = {
12458							positiveValues: [],
12459							negativeValues: []
12460						};
12461					}
12462
12463					// Store these per type
12464					var positiveValues = valuesPerStack[key].positiveValues;
12465					var negativeValues = valuesPerStack[key].negativeValues;
12466
12467					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12468						helpers.each(dataset.data, function(rawValue, index) {
12469							var value = +me.getRightValue(rawValue);
12470							if (isNaN(value) || meta.data[index].hidden) {
12471								return;
12472							}
12473
12474							positiveValues[index] = positiveValues[index] || 0;
12475							negativeValues[index] = negativeValues[index] || 0;
12476
12477							if (opts.relativePoints) {
12478								positiveValues[index] = 100;
12479							} else if (value < 0) {
12480								negativeValues[index] += value;
12481							} else {
12482								positiveValues[index] += value;
12483							}
12484						});
12485					}
12486				});
12487
12488				helpers.each(valuesPerStack, function(valuesForType) {
12489					var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
12490					var minVal = helpers.min(values);
12491					var maxVal = helpers.max(values);
12492					me.min = me.min === null ? minVal : Math.min(me.min, minVal);
12493					me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
12494				});
12495
12496			} else {
12497				helpers.each(datasets, function(dataset, datasetIndex) {
12498					var meta = chart.getDatasetMeta(datasetIndex);
12499					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12500						helpers.each(dataset.data, function(rawValue, index) {
12501							var value = +me.getRightValue(rawValue);
12502							if (isNaN(value) || meta.data[index].hidden) {
12503								return;
12504							}
12505
12506							if (me.min === null) {
12507								me.min = value;
12508							} else if (value < me.min) {
12509								me.min = value;
12510							}
12511
12512							if (me.max === null) {
12513								me.max = value;
12514							} else if (value > me.max) {
12515								me.max = value;
12516							}
12517						});
12518					}
12519				});
12520			}
12521
12522			me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
12523			me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
12524
12525			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
12526			this.handleTickRangeOptions();
12527		},
12528		getTickLimit: function() {
12529			var maxTicks;
12530			var me = this;
12531			var tickOpts = me.options.ticks;
12532
12533			if (me.isHorizontal()) {
12534				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
12535			} else {
12536				// The factor of 2 used to scale the font size has been experimentally determined.
12537				var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
12538				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
12539			}
12540
12541			return maxTicks;
12542		},
12543		// Called after the ticks are built. We need
12544		handleDirectionalChanges: function() {
12545			if (!this.isHorizontal()) {
12546				// We are in a vertical orientation. The top value is the highest. So reverse the array
12547				this.ticks.reverse();
12548			}
12549		},
12550		getLabelForIndex: function(index, datasetIndex) {
12551			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
12552		},
12553		// Utils
12554		getPixelForValue: function(value) {
12555			// This must be called after fit has been run so that
12556			// this.left, this.top, this.right, and this.bottom have been defined
12557			var me = this;
12558			var start = me.start;
12559
12560			var rightValue = +me.getRightValue(value);
12561			var pixel;
12562			var range = me.end - start;
12563
12564			if (me.isHorizontal()) {
12565				pixel = me.left + (me.width / range * (rightValue - start));
12566			} else {
12567				pixel = me.bottom - (me.height / range * (rightValue - start));
12568			}
12569			return pixel;
12570		},
12571		getValueForPixel: function(pixel) {
12572			var me = this;
12573			var isHorizontal = me.isHorizontal();
12574			var innerDimension = isHorizontal ? me.width : me.height;
12575			var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
12576			return me.start + ((me.end - me.start) * offset);
12577		},
12578		getPixelForTick: function(index) {
12579			return this.getPixelForValue(this.ticksAsNumbers[index]);
12580		}
12581	});
12582
12583	scaleService.registerScaleType('linear', LinearScale, defaultConfig);
12584};
12585
12586},{"26":26,"34":34,"35":35,"46":46}],56:[function(require,module,exports){
12587'use strict';
12588
12589var helpers = require(46);
12590var Scale = require(33);
12591
12592/**
12593 * Generate a set of linear ticks
12594 * @param generationOptions the options used to generate the ticks
12595 * @param dataRange the range of the data
12596 * @returns {Array<Number>} array of tick values
12597 */
12598function generateTicks(generationOptions, dataRange) {
12599	var ticks = [];
12600	// To get a "nice" value for the tick spacing, we will use the appropriately named
12601	// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
12602	// for details.
12603
12604	var factor;
12605	var precision;
12606	var spacing;
12607
12608	if (generationOptions.stepSize && generationOptions.stepSize > 0) {
12609		spacing = generationOptions.stepSize;
12610	} else {
12611		var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
12612		spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
12613
12614		precision = generationOptions.precision;
12615		if (precision !== undefined) {
12616			// If the user specified a precision, round to that number of decimal places
12617			factor = Math.pow(10, precision);
12618			spacing = Math.ceil(spacing * factor) / factor;
12619		}
12620	}
12621	var niceMin = Math.floor(dataRange.min / spacing) * spacing;
12622	var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
12623
12624	// If min, max and stepSize is set and they make an evenly spaced scale use it.
12625	if (!helpers.isNullOrUndef(generationOptions.min) && !helpers.isNullOrUndef(generationOptions.max) && generationOptions.stepSize) {
12626		// If very close to our whole number, use it.
12627		if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
12628			niceMin = generationOptions.min;
12629			niceMax = generationOptions.max;
12630		}
12631	}
12632
12633	var numSpaces = (niceMax - niceMin) / spacing;
12634	// If very close to our rounded value, use it.
12635	if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
12636		numSpaces = Math.round(numSpaces);
12637	} else {
12638		numSpaces = Math.ceil(numSpaces);
12639	}
12640
12641	precision = 1;
12642	if (spacing < 1) {
12643		precision = Math.pow(10, 1 - Math.floor(helpers.log10(spacing)));
12644		niceMin = Math.round(niceMin * precision) / precision;
12645		niceMax = Math.round(niceMax * precision) / precision;
12646	}
12647	ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
12648	for (var j = 1; j < numSpaces; ++j) {
12649		ticks.push(Math.round((niceMin + j * spacing) * precision) / precision);
12650	}
12651	ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
12652
12653	return ticks;
12654}
12655
12656module.exports = function(Chart) {
12657
12658	var noop = helpers.noop;
12659
12660	Chart.LinearScaleBase = Scale.extend({
12661		getRightValue: function(value) {
12662			if (typeof value === 'string') {
12663				return +value;
12664			}
12665			return Scale.prototype.getRightValue.call(this, value);
12666		},
12667
12668		handleTickRangeOptions: function() {
12669			var me = this;
12670			var opts = me.options;
12671			var tickOpts = opts.ticks;
12672
12673			// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
12674			// do nothing since that would make the chart weird. If the user really wants a weird chart
12675			// axis, they can manually override it
12676			if (tickOpts.beginAtZero) {
12677				var minSign = helpers.sign(me.min);
12678				var maxSign = helpers.sign(me.max);
12679
12680				if (minSign < 0 && maxSign < 0) {
12681					// move the top up to 0
12682					me.max = 0;
12683				} else if (minSign > 0 && maxSign > 0) {
12684					// move the bottom down to 0
12685					me.min = 0;
12686				}
12687			}
12688
12689			var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
12690			var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
12691
12692			if (tickOpts.min !== undefined) {
12693				me.min = tickOpts.min;
12694			} else if (tickOpts.suggestedMin !== undefined) {
12695				if (me.min === null) {
12696					me.min = tickOpts.suggestedMin;
12697				} else {
12698					me.min = Math.min(me.min, tickOpts.suggestedMin);
12699				}
12700			}
12701
12702			if (tickOpts.max !== undefined) {
12703				me.max = tickOpts.max;
12704			} else if (tickOpts.suggestedMax !== undefined) {
12705				if (me.max === null) {
12706					me.max = tickOpts.suggestedMax;
12707				} else {
12708					me.max = Math.max(me.max, tickOpts.suggestedMax);
12709				}
12710			}
12711
12712			if (setMin !== setMax) {
12713				// We set the min or the max but not both.
12714				// So ensure that our range is good
12715				// Inverted or 0 length range can happen when
12716				// ticks.min is set, and no datasets are visible
12717				if (me.min >= me.max) {
12718					if (setMin) {
12719						me.max = me.min + 1;
12720					} else {
12721						me.min = me.max - 1;
12722					}
12723				}
12724			}
12725
12726			if (me.min === me.max) {
12727				me.max++;
12728
12729				if (!tickOpts.beginAtZero) {
12730					me.min--;
12731				}
12732			}
12733		},
12734		getTickLimit: noop,
12735		handleDirectionalChanges: noop,
12736
12737		buildTicks: function() {
12738			var me = this;
12739			var opts = me.options;
12740			var tickOpts = opts.ticks;
12741
12742			// Figure out what the max number of ticks we can support it is based on the size of
12743			// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12744			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12745			// the graph. Make sure we always have at least 2 ticks
12746			var maxTicks = me.getTickLimit();
12747			maxTicks = Math.max(2, maxTicks);
12748
12749			var numericGeneratorOptions = {
12750				maxTicks: maxTicks,
12751				min: tickOpts.min,
12752				max: tickOpts.max,
12753				precision: tickOpts.precision,
12754				stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
12755			};
12756			var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
12757
12758			me.handleDirectionalChanges();
12759
12760			// At this point, we need to update our max and min given the tick values since we have expanded the
12761			// range of the scale
12762			me.max = helpers.max(ticks);
12763			me.min = helpers.min(ticks);
12764
12765			if (tickOpts.reverse) {
12766				ticks.reverse();
12767
12768				me.start = me.max;
12769				me.end = me.min;
12770			} else {
12771				me.start = me.min;
12772				me.end = me.max;
12773			}
12774		},
12775		convertTicksToLabels: function() {
12776			var me = this;
12777			me.ticksAsNumbers = me.ticks.slice();
12778			me.zeroLineIndex = me.ticks.indexOf(0);
12779
12780			Scale.prototype.convertTicksToLabels.call(me);
12781		}
12782	});
12783};
12784
12785},{"33":33,"46":46}],57:[function(require,module,exports){
12786'use strict';
12787
12788var helpers = require(46);
12789var Scale = require(33);
12790var scaleService = require(34);
12791var Ticks = require(35);
12792
12793/**
12794 * Generate a set of logarithmic ticks
12795 * @param generationOptions the options used to generate the ticks
12796 * @param dataRange the range of the data
12797 * @returns {Array<Number>} array of tick values
12798 */
12799function generateTicks(generationOptions, dataRange) {
12800	var ticks = [];
12801	var valueOrDefault = helpers.valueOrDefault;
12802
12803	// Figure out what the max number of ticks we can support it is based on the size of
12804	// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12805	// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12806	// the graph
12807	var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
12808
12809	var endExp = Math.floor(helpers.log10(dataRange.max));
12810	var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
12811	var exp, significand;
12812
12813	if (tickVal === 0) {
12814		exp = Math.floor(helpers.log10(dataRange.minNotZero));
12815		significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
12816
12817		ticks.push(tickVal);
12818		tickVal = significand * Math.pow(10, exp);
12819	} else {
12820		exp = Math.floor(helpers.log10(tickVal));
12821		significand = Math.floor(tickVal / Math.pow(10, exp));
12822	}
12823	var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
12824
12825	do {
12826		ticks.push(tickVal);
12827
12828		++significand;
12829		if (significand === 10) {
12830			significand = 1;
12831			++exp;
12832			precision = exp >= 0 ? 1 : precision;
12833		}
12834
12835		tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
12836	} while (exp < endExp || (exp === endExp && significand < endSignificand));
12837
12838	var lastTick = valueOrDefault(generationOptions.max, tickVal);
12839	ticks.push(lastTick);
12840
12841	return ticks;
12842}
12843
12844
12845module.exports = function(Chart) {
12846
12847	var defaultConfig = {
12848		position: 'left',
12849
12850		// label settings
12851		ticks: {
12852			callback: Ticks.formatters.logarithmic
12853		}
12854	};
12855
12856	var LogarithmicScale = Scale.extend({
12857		determineDataLimits: function() {
12858			var me = this;
12859			var opts = me.options;
12860			var chart = me.chart;
12861			var data = chart.data;
12862			var datasets = data.datasets;
12863			var isHorizontal = me.isHorizontal();
12864			function IDMatches(meta) {
12865				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
12866			}
12867
12868			// Calculate Range
12869			me.min = null;
12870			me.max = null;
12871			me.minNotZero = null;
12872
12873			var hasStacks = opts.stacked;
12874			if (hasStacks === undefined) {
12875				helpers.each(datasets, function(dataset, datasetIndex) {
12876					if (hasStacks) {
12877						return;
12878					}
12879
12880					var meta = chart.getDatasetMeta(datasetIndex);
12881					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
12882						meta.stack !== undefined) {
12883						hasStacks = true;
12884					}
12885				});
12886			}
12887
12888			if (opts.stacked || hasStacks) {
12889				var valuesPerStack = {};
12890
12891				helpers.each(datasets, function(dataset, datasetIndex) {
12892					var meta = chart.getDatasetMeta(datasetIndex);
12893					var key = [
12894						meta.type,
12895						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12896						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
12897						meta.stack
12898					].join('.');
12899
12900					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12901						if (valuesPerStack[key] === undefined) {
12902							valuesPerStack[key] = [];
12903						}
12904
12905						helpers.each(dataset.data, function(rawValue, index) {
12906							var values = valuesPerStack[key];
12907							var value = +me.getRightValue(rawValue);
12908							// invalid, hidden and negative values are ignored
12909							if (isNaN(value) || meta.data[index].hidden || value < 0) {
12910								return;
12911							}
12912							values[index] = values[index] || 0;
12913							values[index] += value;
12914						});
12915					}
12916				});
12917
12918				helpers.each(valuesPerStack, function(valuesForType) {
12919					if (valuesForType.length > 0) {
12920						var minVal = helpers.min(valuesForType);
12921						var maxVal = helpers.max(valuesForType);
12922						me.min = me.min === null ? minVal : Math.min(me.min, minVal);
12923						me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
12924					}
12925				});
12926
12927			} else {
12928				helpers.each(datasets, function(dataset, datasetIndex) {
12929					var meta = chart.getDatasetMeta(datasetIndex);
12930					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12931						helpers.each(dataset.data, function(rawValue, index) {
12932							var value = +me.getRightValue(rawValue);
12933							// invalid, hidden and negative values are ignored
12934							if (isNaN(value) || meta.data[index].hidden || value < 0) {
12935								return;
12936							}
12937
12938							if (me.min === null) {
12939								me.min = value;
12940							} else if (value < me.min) {
12941								me.min = value;
12942							}
12943
12944							if (me.max === null) {
12945								me.max = value;
12946							} else if (value > me.max) {
12947								me.max = value;
12948							}
12949
12950							if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
12951								me.minNotZero = value;
12952							}
12953						});
12954					}
12955				});
12956			}
12957
12958			// Common base implementation to handle ticks.min, ticks.max
12959			this.handleTickRangeOptions();
12960		},
12961		handleTickRangeOptions: function() {
12962			var me = this;
12963			var opts = me.options;
12964			var tickOpts = opts.ticks;
12965			var valueOrDefault = helpers.valueOrDefault;
12966			var DEFAULT_MIN = 1;
12967			var DEFAULT_MAX = 10;
12968
12969			me.min = valueOrDefault(tickOpts.min, me.min);
12970			me.max = valueOrDefault(tickOpts.max, me.max);
12971
12972			if (me.min === me.max) {
12973				if (me.min !== 0 && me.min !== null) {
12974					me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
12975					me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
12976				} else {
12977					me.min = DEFAULT_MIN;
12978					me.max = DEFAULT_MAX;
12979				}
12980			}
12981			if (me.min === null) {
12982				me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);
12983			}
12984			if (me.max === null) {
12985				me.max = me.min !== 0
12986					? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)
12987					: DEFAULT_MAX;
12988			}
12989			if (me.minNotZero === null) {
12990				if (me.min > 0) {
12991					me.minNotZero = me.min;
12992				} else if (me.max < 1) {
12993					me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));
12994				} else {
12995					me.minNotZero = DEFAULT_MIN;
12996				}
12997			}
12998		},
12999		buildTicks: function() {
13000			var me = this;
13001			var opts = me.options;
13002			var tickOpts = opts.ticks;
13003			var reverse = !me.isHorizontal();
13004
13005			var generationOptions = {
13006				min: tickOpts.min,
13007				max: tickOpts.max
13008			};
13009			var ticks = me.ticks = generateTicks(generationOptions, me);
13010
13011			// At this point, we need to update our max and min given the tick values since we have expanded the
13012			// range of the scale
13013			me.max = helpers.max(ticks);
13014			me.min = helpers.min(ticks);
13015
13016			if (tickOpts.reverse) {
13017				reverse = !reverse;
13018				me.start = me.max;
13019				me.end = me.min;
13020			} else {
13021				me.start = me.min;
13022				me.end = me.max;
13023			}
13024			if (reverse) {
13025				ticks.reverse();
13026			}
13027		},
13028		convertTicksToLabels: function() {
13029			this.tickValues = this.ticks.slice();
13030
13031			Scale.prototype.convertTicksToLabels.call(this);
13032		},
13033		// Get the correct tooltip label
13034		getLabelForIndex: function(index, datasetIndex) {
13035			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13036		},
13037		getPixelForTick: function(index) {
13038			return this.getPixelForValue(this.tickValues[index]);
13039		},
13040		/**
13041		 * Returns the value of the first tick.
13042		 * @param {Number} value - The minimum not zero value.
13043		 * @return {Number} The first tick value.
13044		 * @private
13045		 */
13046		_getFirstTickValue: function(value) {
13047			var exp = Math.floor(helpers.log10(value));
13048			var significand = Math.floor(value / Math.pow(10, exp));
13049
13050			return significand * Math.pow(10, exp);
13051		},
13052		getPixelForValue: function(value) {
13053			var me = this;
13054			var reverse = me.options.ticks.reverse;
13055			var log10 = helpers.log10;
13056			var firstTickValue = me._getFirstTickValue(me.minNotZero);
13057			var offset = 0;
13058			var innerDimension, pixel, start, end, sign;
13059
13060			value = +me.getRightValue(value);
13061			if (reverse) {
13062				start = me.end;
13063				end = me.start;
13064				sign = -1;
13065			} else {
13066				start = me.start;
13067				end = me.end;
13068				sign = 1;
13069			}
13070			if (me.isHorizontal()) {
13071				innerDimension = me.width;
13072				pixel = reverse ? me.right : me.left;
13073			} else {
13074				innerDimension = me.height;
13075				sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
13076				pixel = reverse ? me.top : me.bottom;
13077			}
13078			if (value !== start) {
13079				if (start === 0) { // include zero tick
13080					offset = helpers.getValueOrDefault(
13081						me.options.ticks.fontSize,
13082						Chart.defaults.global.defaultFontSize
13083					);
13084					innerDimension -= offset;
13085					start = firstTickValue;
13086				}
13087				if (value !== 0) {
13088					offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
13089				}
13090				pixel += sign * offset;
13091			}
13092			return pixel;
13093		},
13094		getValueForPixel: function(pixel) {
13095			var me = this;
13096			var reverse = me.options.ticks.reverse;
13097			var log10 = helpers.log10;
13098			var firstTickValue = me._getFirstTickValue(me.minNotZero);
13099			var innerDimension, start, end, value;
13100
13101			if (reverse) {
13102				start = me.end;
13103				end = me.start;
13104			} else {
13105				start = me.start;
13106				end = me.end;
13107			}
13108			if (me.isHorizontal()) {
13109				innerDimension = me.width;
13110				value = reverse ? me.right - pixel : pixel - me.left;
13111			} else {
13112				innerDimension = me.height;
13113				value = reverse ? pixel - me.top : me.bottom - pixel;
13114			}
13115			if (value !== start) {
13116				if (start === 0) { // include zero tick
13117					var offset = helpers.getValueOrDefault(
13118						me.options.ticks.fontSize,
13119						Chart.defaults.global.defaultFontSize
13120					);
13121					value -= offset;
13122					innerDimension -= offset;
13123					start = firstTickValue;
13124				}
13125				value *= log10(end) - log10(start);
13126				value /= innerDimension;
13127				value = Math.pow(10, log10(start) + value);
13128			}
13129			return value;
13130		}
13131	});
13132
13133	scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
13134};
13135
13136},{"33":33,"34":34,"35":35,"46":46}],58:[function(require,module,exports){
13137'use strict';
13138
13139var defaults = require(26);
13140var helpers = require(46);
13141var scaleService = require(34);
13142var Ticks = require(35);
13143
13144module.exports = function(Chart) {
13145
13146	var globalDefaults = defaults.global;
13147
13148	var defaultConfig = {
13149		display: true,
13150
13151		// Boolean - Whether to animate scaling the chart from the centre
13152		animate: true,
13153		position: 'chartArea',
13154
13155		angleLines: {
13156			display: true,
13157			color: 'rgba(0, 0, 0, 0.1)',
13158			lineWidth: 1
13159		},
13160
13161		gridLines: {
13162			circular: false
13163		},
13164
13165		// label settings
13166		ticks: {
13167			// Boolean - Show a backdrop to the scale label
13168			showLabelBackdrop: true,
13169
13170			// String - The colour of the label backdrop
13171			backdropColor: 'rgba(255,255,255,0.75)',
13172
13173			// Number - The backdrop padding above & below the label in pixels
13174			backdropPaddingY: 2,
13175
13176			// Number - The backdrop padding to the side of the label in pixels
13177			backdropPaddingX: 2,
13178
13179			callback: Ticks.formatters.linear
13180		},
13181
13182		pointLabels: {
13183			// Boolean - if true, show point labels
13184			display: true,
13185
13186			// Number - Point label font size in pixels
13187			fontSize: 10,
13188
13189			// Function - Used to convert point labels
13190			callback: function(label) {
13191				return label;
13192			}
13193		}
13194	};
13195
13196	function getValueCount(scale) {
13197		var opts = scale.options;
13198		return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
13199	}
13200
13201	function getPointLabelFontOptions(scale) {
13202		var pointLabelOptions = scale.options.pointLabels;
13203		var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
13204		var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
13205		var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
13206		var font = helpers.fontString(fontSize, fontStyle, fontFamily);
13207
13208		return {
13209			size: fontSize,
13210			style: fontStyle,
13211			family: fontFamily,
13212			font: font
13213		};
13214	}
13215
13216	function measureLabelSize(ctx, fontSize, label) {
13217		if (helpers.isArray(label)) {
13218			return {
13219				w: helpers.longestText(ctx, ctx.font, label),
13220				h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
13221			};
13222		}
13223
13224		return {
13225			w: ctx.measureText(label).width,
13226			h: fontSize
13227		};
13228	}
13229
13230	function determineLimits(angle, pos, size, min, max) {
13231		if (angle === min || angle === max) {
13232			return {
13233				start: pos - (size / 2),
13234				end: pos + (size / 2)
13235			};
13236		} else if (angle < min || angle > max) {
13237			return {
13238				start: pos - size - 5,
13239				end: pos
13240			};
13241		}
13242
13243		return {
13244			start: pos,
13245			end: pos + size + 5
13246		};
13247	}
13248
13249	/**
13250	 * Helper function to fit a radial linear scale with point labels
13251	 */
13252	function fitWithPointLabels(scale) {
13253		/*
13254		 * Right, this is really confusing and there is a lot of maths going on here
13255		 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
13256		 *
13257		 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
13258		 *
13259		 * Solution:
13260		 *
13261		 * We assume the radius of the polygon is half the size of the canvas at first
13262		 * at each index we check if the text overlaps.
13263		 *
13264		 * Where it does, we store that angle and that index.
13265		 *
13266		 * After finding the largest index and angle we calculate how much we need to remove
13267		 * from the shape radius to move the point inwards by that x.
13268		 *
13269		 * We average the left and right distances to get the maximum shape radius that can fit in the box
13270		 * along with labels.
13271		 *
13272		 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
13273		 * on each side, removing that from the size, halving it and adding the left x protrusion width.
13274		 *
13275		 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
13276		 * and position it in the most space efficient manner
13277		 *
13278		 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
13279		 */
13280
13281		var plFont = getPointLabelFontOptions(scale);
13282
13283		// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
13284		// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
13285		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
13286		var furthestLimits = {
13287			r: scale.width,
13288			l: 0,
13289			t: scale.height,
13290			b: 0
13291		};
13292		var furthestAngles = {};
13293		var i, textSize, pointPosition;
13294
13295		scale.ctx.font = plFont.font;
13296		scale._pointLabelSizes = [];
13297
13298		var valueCount = getValueCount(scale);
13299		for (i = 0; i < valueCount; i++) {
13300			pointPosition = scale.getPointPosition(i, largestPossibleRadius);
13301			textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
13302			scale._pointLabelSizes[i] = textSize;
13303
13304			// Add quarter circle to make degree 0 mean top of circle
13305			var angleRadians = scale.getIndexAngle(i);
13306			var angle = helpers.toDegrees(angleRadians) % 360;
13307			var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
13308			var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
13309
13310			if (hLimits.start < furthestLimits.l) {
13311				furthestLimits.l = hLimits.start;
13312				furthestAngles.l = angleRadians;
13313			}
13314
13315			if (hLimits.end > furthestLimits.r) {
13316				furthestLimits.r = hLimits.end;
13317				furthestAngles.r = angleRadians;
13318			}
13319
13320			if (vLimits.start < furthestLimits.t) {
13321				furthestLimits.t = vLimits.start;
13322				furthestAngles.t = angleRadians;
13323			}
13324
13325			if (vLimits.end > furthestLimits.b) {
13326				furthestLimits.b = vLimits.end;
13327				furthestAngles.b = angleRadians;
13328			}
13329		}
13330
13331		scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
13332	}
13333
13334	/**
13335	 * Helper function to fit a radial linear scale with no point labels
13336	 */
13337	function fit(scale) {
13338		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
13339		scale.drawingArea = Math.round(largestPossibleRadius);
13340		scale.setCenterPoint(0, 0, 0, 0);
13341	}
13342
13343	function getTextAlignForAngle(angle) {
13344		if (angle === 0 || angle === 180) {
13345			return 'center';
13346		} else if (angle < 180) {
13347			return 'left';
13348		}
13349
13350		return 'right';
13351	}
13352
13353	function fillText(ctx, text, position, fontSize) {
13354		if (helpers.isArray(text)) {
13355			var y = position.y;
13356			var spacing = 1.5 * fontSize;
13357
13358			for (var i = 0; i < text.length; ++i) {
13359				ctx.fillText(text[i], position.x, y);
13360				y += spacing;
13361			}
13362		} else {
13363			ctx.fillText(text, position.x, position.y);
13364		}
13365	}
13366
13367	function adjustPointPositionForLabelHeight(angle, textSize, position) {
13368		if (angle === 90 || angle === 270) {
13369			position.y -= (textSize.h / 2);
13370		} else if (angle > 270 || angle < 90) {
13371			position.y -= textSize.h;
13372		}
13373	}
13374
13375	function drawPointLabels(scale) {
13376		var ctx = scale.ctx;
13377		var opts = scale.options;
13378		var angleLineOpts = opts.angleLines;
13379		var pointLabelOpts = opts.pointLabels;
13380
13381		ctx.lineWidth = angleLineOpts.lineWidth;
13382		ctx.strokeStyle = angleLineOpts.color;
13383
13384		var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
13385
13386		// Point Label Font
13387		var plFont = getPointLabelFontOptions(scale);
13388
13389		ctx.textBaseline = 'top';
13390
13391		for (var i = getValueCount(scale) - 1; i >= 0; i--) {
13392			if (angleLineOpts.display) {
13393				var outerPosition = scale.getPointPosition(i, outerDistance);
13394				ctx.beginPath();
13395				ctx.moveTo(scale.xCenter, scale.yCenter);
13396				ctx.lineTo(outerPosition.x, outerPosition.y);
13397				ctx.stroke();
13398				ctx.closePath();
13399			}
13400
13401			if (pointLabelOpts.display) {
13402				// Extra 3px out for some label spacing
13403				var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
13404
13405				// Keep this in loop since we may support array properties here
13406				var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor);
13407				ctx.font = plFont.font;
13408				ctx.fillStyle = pointLabelFontColor;
13409
13410				var angleRadians = scale.getIndexAngle(i);
13411				var angle = helpers.toDegrees(angleRadians);
13412				ctx.textAlign = getTextAlignForAngle(angle);
13413				adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
13414				fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
13415			}
13416		}
13417	}
13418
13419	function drawRadiusLine(scale, gridLineOpts, radius, index) {
13420		var ctx = scale.ctx;
13421		ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
13422		ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
13423
13424		if (scale.options.gridLines.circular) {
13425			// Draw circular arcs between the points
13426			ctx.beginPath();
13427			ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
13428			ctx.closePath();
13429			ctx.stroke();
13430		} else {
13431			// Draw straight lines connecting each index
13432			var valueCount = getValueCount(scale);
13433
13434			if (valueCount === 0) {
13435				return;
13436			}
13437
13438			ctx.beginPath();
13439			var pointPosition = scale.getPointPosition(0, radius);
13440			ctx.moveTo(pointPosition.x, pointPosition.y);
13441
13442			for (var i = 1; i < valueCount; i++) {
13443				pointPosition = scale.getPointPosition(i, radius);
13444				ctx.lineTo(pointPosition.x, pointPosition.y);
13445			}
13446
13447			ctx.closePath();
13448			ctx.stroke();
13449		}
13450	}
13451
13452	function numberOrZero(param) {
13453		return helpers.isNumber(param) ? param : 0;
13454	}
13455
13456	var LinearRadialScale = Chart.LinearScaleBase.extend({
13457		setDimensions: function() {
13458			var me = this;
13459			var opts = me.options;
13460			var tickOpts = opts.ticks;
13461			// Set the unconstrained dimension before label rotation
13462			me.width = me.maxWidth;
13463			me.height = me.maxHeight;
13464			me.xCenter = Math.round(me.width / 2);
13465			me.yCenter = Math.round(me.height / 2);
13466
13467			var minSize = helpers.min([me.height, me.width]);
13468			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13469			me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
13470		},
13471		determineDataLimits: function() {
13472			var me = this;
13473			var chart = me.chart;
13474			var min = Number.POSITIVE_INFINITY;
13475			var max = Number.NEGATIVE_INFINITY;
13476
13477			helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
13478				if (chart.isDatasetVisible(datasetIndex)) {
13479					var meta = chart.getDatasetMeta(datasetIndex);
13480
13481					helpers.each(dataset.data, function(rawValue, index) {
13482						var value = +me.getRightValue(rawValue);
13483						if (isNaN(value) || meta.data[index].hidden) {
13484							return;
13485						}
13486
13487						min = Math.min(value, min);
13488						max = Math.max(value, max);
13489					});
13490				}
13491			});
13492
13493			me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
13494			me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
13495
13496			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
13497			me.handleTickRangeOptions();
13498		},
13499		getTickLimit: function() {
13500			var tickOpts = this.options.ticks;
13501			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13502			return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
13503		},
13504		convertTicksToLabels: function() {
13505			var me = this;
13506
13507			Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
13508
13509			// Point labels
13510			me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
13511		},
13512		getLabelForIndex: function(index, datasetIndex) {
13513			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13514		},
13515		fit: function() {
13516			if (this.options.pointLabels.display) {
13517				fitWithPointLabels(this);
13518			} else {
13519				fit(this);
13520			}
13521		},
13522		/**
13523		 * Set radius reductions and determine new radius and center point
13524		 * @private
13525		 */
13526		setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
13527			var me = this;
13528			var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
13529			var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
13530			var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
13531			var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
13532
13533			radiusReductionLeft = numberOrZero(radiusReductionLeft);
13534			radiusReductionRight = numberOrZero(radiusReductionRight);
13535			radiusReductionTop = numberOrZero(radiusReductionTop);
13536			radiusReductionBottom = numberOrZero(radiusReductionBottom);
13537
13538			me.drawingArea = Math.min(
13539				Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
13540				Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
13541			me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
13542		},
13543		setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
13544			var me = this;
13545			var maxRight = me.width - rightMovement - me.drawingArea;
13546			var maxLeft = leftMovement + me.drawingArea;
13547			var maxTop = topMovement + me.drawingArea;
13548			var maxBottom = me.height - bottomMovement - me.drawingArea;
13549
13550			me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
13551			me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
13552		},
13553
13554		getIndexAngle: function(index) {
13555			var angleMultiplier = (Math.PI * 2) / getValueCount(this);
13556			var startAngle = this.chart.options && this.chart.options.startAngle ?
13557				this.chart.options.startAngle :
13558				0;
13559
13560			var startAngleRadians = startAngle * Math.PI * 2 / 360;
13561
13562			// Start from the top instead of right, so remove a quarter of the circle
13563			return index * angleMultiplier + startAngleRadians;
13564		},
13565		getDistanceFromCenterForValue: function(value) {
13566			var me = this;
13567
13568			if (value === null) {
13569				return 0; // null always in center
13570			}
13571
13572			// Take into account half font size + the yPadding of the top value
13573			var scalingFactor = me.drawingArea / (me.max - me.min);
13574			if (me.options.ticks.reverse) {
13575				return (me.max - value) * scalingFactor;
13576			}
13577			return (value - me.min) * scalingFactor;
13578		},
13579		getPointPosition: function(index, distanceFromCenter) {
13580			var me = this;
13581			var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
13582			return {
13583				x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
13584				y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
13585			};
13586		},
13587		getPointPositionForValue: function(index, value) {
13588			return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
13589		},
13590
13591		getBasePosition: function() {
13592			var me = this;
13593			var min = me.min;
13594			var max = me.max;
13595
13596			return me.getPointPositionForValue(0,
13597				me.beginAtZero ? 0 :
13598				min < 0 && max < 0 ? max :
13599				min > 0 && max > 0 ? min :
13600				0);
13601		},
13602
13603		draw: function() {
13604			var me = this;
13605			var opts = me.options;
13606			var gridLineOpts = opts.gridLines;
13607			var tickOpts = opts.ticks;
13608			var valueOrDefault = helpers.valueOrDefault;
13609
13610			if (opts.display) {
13611				var ctx = me.ctx;
13612				var startAngle = this.getIndexAngle(0);
13613
13614				// Tick Font
13615				var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13616				var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
13617				var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
13618				var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
13619
13620				helpers.each(me.ticks, function(label, index) {
13621					// Don't draw a centre value (if it is minimum)
13622					if (index > 0 || tickOpts.reverse) {
13623						var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
13624
13625						// Draw circular lines around the scale
13626						if (gridLineOpts.display && index !== 0) {
13627							drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
13628						}
13629
13630						if (tickOpts.display) {
13631							var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
13632							ctx.font = tickLabelFont;
13633
13634							ctx.save();
13635							ctx.translate(me.xCenter, me.yCenter);
13636							ctx.rotate(startAngle);
13637
13638							if (tickOpts.showLabelBackdrop) {
13639								var labelWidth = ctx.measureText(label).width;
13640								ctx.fillStyle = tickOpts.backdropColor;
13641								ctx.fillRect(
13642									-labelWidth / 2 - tickOpts.backdropPaddingX,
13643									-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
13644									labelWidth + tickOpts.backdropPaddingX * 2,
13645									tickFontSize + tickOpts.backdropPaddingY * 2
13646								);
13647							}
13648
13649							ctx.textAlign = 'center';
13650							ctx.textBaseline = 'middle';
13651							ctx.fillStyle = tickFontColor;
13652							ctx.fillText(label, 0, -yCenterOffset);
13653							ctx.restore();
13654						}
13655					}
13656				});
13657
13658				if (opts.angleLines.display || opts.pointLabels.display) {
13659					drawPointLabels(me);
13660				}
13661			}
13662		}
13663	});
13664
13665	scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
13666};
13667
13668},{"26":26,"34":34,"35":35,"46":46}],59:[function(require,module,exports){
13669/* global window: false */
13670'use strict';
13671
13672var moment = require(1);
13673moment = typeof moment === 'function' ? moment : window.moment;
13674
13675var defaults = require(26);
13676var helpers = require(46);
13677var Scale = require(33);
13678var scaleService = require(34);
13679
13680// Integer constants are from the ES6 spec.
13681var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
13682var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
13683
13684var INTERVALS = {
13685	millisecond: {
13686		common: true,
13687		size: 1,
13688		steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
13689	},
13690	second: {
13691		common: true,
13692		size: 1000,
13693		steps: [1, 2, 5, 10, 15, 30]
13694	},
13695	minute: {
13696		common: true,
13697		size: 60000,
13698		steps: [1, 2, 5, 10, 15, 30]
13699	},
13700	hour: {
13701		common: true,
13702		size: 3600000,
13703		steps: [1, 2, 3, 6, 12]
13704	},
13705	day: {
13706		common: true,
13707		size: 86400000,
13708		steps: [1, 2, 5]
13709	},
13710	week: {
13711		common: false,
13712		size: 604800000,
13713		steps: [1, 2, 3, 4]
13714	},
13715	month: {
13716		common: true,
13717		size: 2.628e9,
13718		steps: [1, 2, 3]
13719	},
13720	quarter: {
13721		common: false,
13722		size: 7.884e9,
13723		steps: [1, 2, 3, 4]
13724	},
13725	year: {
13726		common: true,
13727		size: 3.154e10
13728	}
13729};
13730
13731var UNITS = Object.keys(INTERVALS);
13732
13733function sorter(a, b) {
13734	return a - b;
13735}
13736
13737function arrayUnique(items) {
13738	var hash = {};
13739	var out = [];
13740	var i, ilen, item;
13741
13742	for (i = 0, ilen = items.length; i < ilen; ++i) {
13743		item = items[i];
13744		if (!hash[item]) {
13745			hash[item] = true;
13746			out.push(item);
13747		}
13748	}
13749
13750	return out;
13751}
13752
13753/**
13754 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
13755 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
13756 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
13757 * extremity (left + width or top + height). Note that it would be more optimized to directly
13758 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
13759 * to create the lookup table. The table ALWAYS contains at least two items: min and max.
13760 *
13761 * @param {Number[]} timestamps - timestamps sorted from lowest to highest.
13762 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
13763 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
13764 * If 'series', timestamps will be positioned at the same distance from each other. In this
13765 * case, only timestamps that break the time linearity are registered, meaning that in the
13766 * best case, all timestamps are linear, the table contains only min and max.
13767 */
13768function buildLookupTable(timestamps, min, max, distribution) {
13769	if (distribution === 'linear' || !timestamps.length) {
13770		return [
13771			{time: min, pos: 0},
13772			{time: max, pos: 1}
13773		];
13774	}
13775
13776	var table = [];
13777	var items = [min];
13778	var i, ilen, prev, curr, next;
13779
13780	for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
13781		curr = timestamps[i];
13782		if (curr > min && curr < max) {
13783			items.push(curr);
13784		}
13785	}
13786
13787	items.push(max);
13788
13789	for (i = 0, ilen = items.length; i < ilen; ++i) {
13790		next = items[i + 1];
13791		prev = items[i - 1];
13792		curr = items[i];
13793
13794		// only add points that breaks the scale linearity
13795		if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
13796			table.push({time: curr, pos: i / (ilen - 1)});
13797		}
13798	}
13799
13800	return table;
13801}
13802
13803// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
13804function lookup(table, key, value) {
13805	var lo = 0;
13806	var hi = table.length - 1;
13807	var mid, i0, i1;
13808
13809	while (lo >= 0 && lo <= hi) {
13810		mid = (lo + hi) >> 1;
13811		i0 = table[mid - 1] || null;
13812		i1 = table[mid];
13813
13814		if (!i0) {
13815			// given value is outside table (before first item)
13816			return {lo: null, hi: i1};
13817		} else if (i1[key] < value) {
13818			lo = mid + 1;
13819		} else if (i0[key] > value) {
13820			hi = mid - 1;
13821		} else {
13822			return {lo: i0, hi: i1};
13823		}
13824	}
13825
13826	// given value is outside table (after last item)
13827	return {lo: i1, hi: null};
13828}
13829
13830/**
13831 * Linearly interpolates the given source `value` using the table items `skey` values and
13832 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
13833 * returns the position for a timestamp equal to 42. If value is out of bounds, values at
13834 * index [0, 1] or [n - 1, n] are used for the interpolation.
13835 */
13836function interpolate(table, skey, sval, tkey) {
13837	var range = lookup(table, skey, sval);
13838
13839	// Note: the lookup table ALWAYS contains at least 2 items (min and max)
13840	var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
13841	var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
13842
13843	var span = next[skey] - prev[skey];
13844	var ratio = span ? (sval - prev[skey]) / span : 0;
13845	var offset = (next[tkey] - prev[tkey]) * ratio;
13846
13847	return prev[tkey] + offset;
13848}
13849
13850/**
13851 * Convert the given value to a moment object using the given time options.
13852 * @see http://momentjs.com/docs/#/parsing/
13853 */
13854function momentify(value, options) {
13855	var parser = options.parser;
13856	var format = options.parser || options.format;
13857
13858	if (typeof parser === 'function') {
13859		return parser(value);
13860	}
13861
13862	if (typeof value === 'string' && typeof format === 'string') {
13863		return moment(value, format);
13864	}
13865
13866	if (!(value instanceof moment)) {
13867		value = moment(value);
13868	}
13869
13870	if (value.isValid()) {
13871		return value;
13872	}
13873
13874	// Labels are in an incompatible moment format and no `parser` has been provided.
13875	// The user might still use the deprecated `format` option to convert his inputs.
13876	if (typeof format === 'function') {
13877		return format(value);
13878	}
13879
13880	return value;
13881}
13882
13883function parse(input, scale) {
13884	if (helpers.isNullOrUndef(input)) {
13885		return null;
13886	}
13887
13888	var options = scale.options.time;
13889	var value = momentify(scale.getRightValue(input), options);
13890	if (!value.isValid()) {
13891		return null;
13892	}
13893
13894	if (options.round) {
13895		value.startOf(options.round);
13896	}
13897
13898	return value.valueOf();
13899}
13900
13901/**
13902 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
13903 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
13904 */
13905function determineStepSize(min, max, unit, capacity) {
13906	var range = max - min;
13907	var interval = INTERVALS[unit];
13908	var milliseconds = interval.size;
13909	var steps = interval.steps;
13910	var i, ilen, factor;
13911
13912	if (!steps) {
13913		return Math.ceil(range / (capacity * milliseconds));
13914	}
13915
13916	for (i = 0, ilen = steps.length; i < ilen; ++i) {
13917		factor = steps[i];
13918		if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
13919			break;
13920		}
13921	}
13922
13923	return factor;
13924}
13925
13926/**
13927 * Figures out what unit results in an appropriate number of auto-generated ticks
13928 */
13929function determineUnitForAutoTicks(minUnit, min, max, capacity) {
13930	var ilen = UNITS.length;
13931	var i, interval, factor;
13932
13933	for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
13934		interval = INTERVALS[UNITS[i]];
13935		factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;
13936
13937		if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
13938			return UNITS[i];
13939		}
13940	}
13941
13942	return UNITS[ilen - 1];
13943}
13944
13945/**
13946 * Figures out what unit to format a set of ticks with
13947 */
13948function determineUnitForFormatting(ticks, minUnit, min, max) {
13949	var duration = moment.duration(moment(max).diff(moment(min)));
13950	var ilen = UNITS.length;
13951	var i, unit;
13952
13953	for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) {
13954		unit = UNITS[i];
13955		if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) {
13956			return unit;
13957		}
13958	}
13959
13960	return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
13961}
13962
13963function determineMajorUnit(unit) {
13964	for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
13965		if (INTERVALS[UNITS[i]].common) {
13966			return UNITS[i];
13967		}
13968	}
13969}
13970
13971/**
13972 * Generates a maximum of `capacity` timestamps between min and max, rounded to the
13973 * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
13974 * Important: this method can return ticks outside the min and max range, it's the
13975 * responsibility of the calling code to clamp values if needed.
13976 */
13977function generate(min, max, capacity, options) {
13978	var timeOpts = options.time;
13979	var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
13980	var major = determineMajorUnit(minor);
13981	var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
13982	var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
13983	var majorTicksEnabled = options.ticks.major.enabled;
13984	var interval = INTERVALS[minor];
13985	var first = moment(min);
13986	var last = moment(max);
13987	var ticks = [];
13988	var time;
13989
13990	if (!stepSize) {
13991		stepSize = determineStepSize(min, max, minor, capacity);
13992	}
13993
13994	// For 'week' unit, handle the first day of week option
13995	if (weekday) {
13996		first = first.isoWeekday(weekday);
13997		last = last.isoWeekday(weekday);
13998	}
13999
14000	// Align first/last ticks on unit
14001	first = first.startOf(weekday ? 'day' : minor);
14002	last = last.startOf(weekday ? 'day' : minor);
14003
14004	// Make sure that the last tick include max
14005	if (last < max) {
14006		last.add(1, minor);
14007	}
14008
14009	time = moment(first);
14010
14011	if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
14012		// Align the first tick on the previous `minor` unit aligned on the `major` unit:
14013		// we first aligned time on the previous `major` unit then add the number of full
14014		// stepSize there is between first and the previous major time.
14015		time.startOf(major);
14016		time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
14017	}
14018
14019	for (; time < last; time.add(stepSize, minor)) {
14020		ticks.push(+time);
14021	}
14022
14023	ticks.push(+time);
14024
14025	return ticks;
14026}
14027
14028/**
14029 * Returns the right and left offsets from edges in the form of {left, right}.
14030 * Offsets are added when the `offset` option is true.
14031 */
14032function computeOffsets(table, ticks, min, max, options) {
14033	var left = 0;
14034	var right = 0;
14035	var upper, lower;
14036
14037	if (options.offset && ticks.length) {
14038		if (!options.time.min) {
14039			upper = ticks.length > 1 ? ticks[1] : max;
14040			lower = ticks[0];
14041			left = (
14042				interpolate(table, 'time', upper, 'pos') -
14043				interpolate(table, 'time', lower, 'pos')
14044			) / 2;
14045		}
14046		if (!options.time.max) {
14047			upper = ticks[ticks.length - 1];
14048			lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
14049			right = (
14050				interpolate(table, 'time', upper, 'pos') -
14051				interpolate(table, 'time', lower, 'pos')
14052			) / 2;
14053		}
14054	}
14055
14056	return {left: left, right: right};
14057}
14058
14059function ticksFromTimestamps(values, majorUnit) {
14060	var ticks = [];
14061	var i, ilen, value, major;
14062
14063	for (i = 0, ilen = values.length; i < ilen; ++i) {
14064		value = values[i];
14065		major = majorUnit ? value === +moment(value).startOf(majorUnit) : false;
14066
14067		ticks.push({
14068			value: value,
14069			major: major
14070		});
14071	}
14072
14073	return ticks;
14074}
14075
14076function determineLabelFormat(data, timeOpts) {
14077	var i, momentDate, hasTime;
14078	var ilen = data.length;
14079
14080	// find the label with the most parts (milliseconds, minutes, etc.)
14081	// format all labels with the same level of detail as the most specific label
14082	for (i = 0; i < ilen; i++) {
14083		momentDate = momentify(data[i], timeOpts);
14084		if (momentDate.millisecond() !== 0) {
14085			return 'MMM D, YYYY h:mm:ss.SSS a';
14086		}
14087		if (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) {
14088			hasTime = true;
14089		}
14090	}
14091	if (hasTime) {
14092		return 'MMM D, YYYY h:mm:ss a';
14093	}
14094	return 'MMM D, YYYY';
14095}
14096
14097module.exports = function() {
14098
14099	var defaultConfig = {
14100		position: 'bottom',
14101
14102		/**
14103		 * Data distribution along the scale:
14104		 * - 'linear': data are spread according to their time (distances can vary),
14105		 * - 'series': data are spread at the same distance from each other.
14106		 * @see https://github.com/chartjs/Chart.js/pull/4507
14107		 * @since 2.7.0
14108		 */
14109		distribution: 'linear',
14110
14111		/**
14112		 * Scale boundary strategy (bypassed by min/max time options)
14113		 * - `data`: make sure data are fully visible, ticks outside are removed
14114		 * - `ticks`: make sure ticks are fully visible, data outside are truncated
14115		 * @see https://github.com/chartjs/Chart.js/pull/4556
14116		 * @since 2.7.0
14117		 */
14118		bounds: 'data',
14119
14120		time: {
14121			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
14122			format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
14123			unit: false, // false == automatic or override with week, month, year, etc.
14124			round: false, // none, or override with week, month, year, etc.
14125			displayFormat: false, // DEPRECATED
14126			isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
14127			minUnit: 'millisecond',
14128
14129			// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
14130			displayFormats: {
14131				millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
14132				second: 'h:mm:ss a', // 11:20:01 AM
14133				minute: 'h:mm a', // 11:20 AM
14134				hour: 'hA', // 5PM
14135				day: 'MMM D', // Sep 4
14136				week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
14137				month: 'MMM YYYY', // Sept 2015
14138				quarter: '[Q]Q - YYYY', // Q3
14139				year: 'YYYY' // 2015
14140			},
14141		},
14142		ticks: {
14143			autoSkip: false,
14144
14145			/**
14146			 * Ticks generation input values:
14147			 * - 'auto': generates "optimal" ticks based on scale size and time options.
14148			 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
14149			 * - 'labels': generates ticks from user given `data.labels` values ONLY.
14150			 * @see https://github.com/chartjs/Chart.js/pull/4507
14151			 * @since 2.7.0
14152			 */
14153			source: 'auto',
14154
14155			major: {
14156				enabled: false
14157			}
14158		}
14159	};
14160
14161	var TimeScale = Scale.extend({
14162		initialize: function() {
14163			if (!moment) {
14164				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');
14165			}
14166
14167			this.mergeTicksOptions();
14168
14169			Scale.prototype.initialize.call(this);
14170		},
14171
14172		update: function() {
14173			var me = this;
14174			var options = me.options;
14175
14176			// DEPRECATIONS: output a message only one time per update
14177			if (options.time && options.time.format) {
14178				console.warn('options.time.format is deprecated and replaced by options.time.parser.');
14179			}
14180
14181			return Scale.prototype.update.apply(me, arguments);
14182		},
14183
14184		/**
14185		 * Allows data to be referenced via 't' attribute
14186		 */
14187		getRightValue: function(rawValue) {
14188			if (rawValue && rawValue.t !== undefined) {
14189				rawValue = rawValue.t;
14190			}
14191			return Scale.prototype.getRightValue.call(this, rawValue);
14192		},
14193
14194		determineDataLimits: function() {
14195			var me = this;
14196			var chart = me.chart;
14197			var timeOpts = me.options.time;
14198			var unit = timeOpts.unit || 'day';
14199			var min = MAX_INTEGER;
14200			var max = MIN_INTEGER;
14201			var timestamps = [];
14202			var datasets = [];
14203			var labels = [];
14204			var i, j, ilen, jlen, data, timestamp;
14205
14206			// Convert labels to timestamps
14207			for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
14208				labels.push(parse(chart.data.labels[i], me));
14209			}
14210
14211			// Convert data to timestamps
14212			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
14213				if (chart.isDatasetVisible(i)) {
14214					data = chart.data.datasets[i].data;
14215
14216					// Let's consider that all data have the same format.
14217					if (helpers.isObject(data[0])) {
14218						datasets[i] = [];
14219
14220						for (j = 0, jlen = data.length; j < jlen; ++j) {
14221							timestamp = parse(data[j], me);
14222							timestamps.push(timestamp);
14223							datasets[i][j] = timestamp;
14224						}
14225					} else {
14226						timestamps.push.apply(timestamps, labels);
14227						datasets[i] = labels.slice(0);
14228					}
14229				} else {
14230					datasets[i] = [];
14231				}
14232			}
14233
14234			if (labels.length) {
14235				// Sort labels **after** data have been converted
14236				labels = arrayUnique(labels).sort(sorter);
14237				min = Math.min(min, labels[0]);
14238				max = Math.max(max, labels[labels.length - 1]);
14239			}
14240
14241			if (timestamps.length) {
14242				timestamps = arrayUnique(timestamps).sort(sorter);
14243				min = Math.min(min, timestamps[0]);
14244				max = Math.max(max, timestamps[timestamps.length - 1]);
14245			}
14246
14247			min = parse(timeOpts.min, me) || min;
14248			max = parse(timeOpts.max, me) || max;
14249
14250			// In case there is no valid min/max, set limits based on unit time option
14251			min = min === MAX_INTEGER ? +moment().startOf(unit) : min;
14252			max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max;
14253
14254			// Make sure that max is strictly higher than min (required by the lookup table)
14255			me.min = Math.min(min, max);
14256			me.max = Math.max(min + 1, max);
14257
14258			// PRIVATE
14259			me._horizontal = me.isHorizontal();
14260			me._table = [];
14261			me._timestamps = {
14262				data: timestamps,
14263				datasets: datasets,
14264				labels: labels
14265			};
14266		},
14267
14268		buildTicks: function() {
14269			var me = this;
14270			var min = me.min;
14271			var max = me.max;
14272			var options = me.options;
14273			var timeOpts = options.time;
14274			var timestamps = [];
14275			var ticks = [];
14276			var i, ilen, timestamp;
14277
14278			switch (options.ticks.source) {
14279			case 'data':
14280				timestamps = me._timestamps.data;
14281				break;
14282			case 'labels':
14283				timestamps = me._timestamps.labels;
14284				break;
14285			case 'auto':
14286			default:
14287				timestamps = generate(min, max, me.getLabelCapacity(min), options);
14288			}
14289
14290			if (options.bounds === 'ticks' && timestamps.length) {
14291				min = timestamps[0];
14292				max = timestamps[timestamps.length - 1];
14293			}
14294
14295			// Enforce limits with user min/max options
14296			min = parse(timeOpts.min, me) || min;
14297			max = parse(timeOpts.max, me) || max;
14298
14299			// Remove ticks outside the min/max range
14300			for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
14301				timestamp = timestamps[i];
14302				if (timestamp >= min && timestamp <= max) {
14303					ticks.push(timestamp);
14304				}
14305			}
14306
14307			me.min = min;
14308			me.max = max;
14309
14310			// PRIVATE
14311			me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max);
14312			me._majorUnit = determineMajorUnit(me._unit);
14313			me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
14314			me._offsets = computeOffsets(me._table, ticks, min, max, options);
14315			me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);
14316
14317			return ticksFromTimestamps(ticks, me._majorUnit);
14318		},
14319
14320		getLabelForIndex: function(index, datasetIndex) {
14321			var me = this;
14322			var data = me.chart.data;
14323			var timeOpts = me.options.time;
14324			var label = data.labels && index < data.labels.length ? data.labels[index] : '';
14325			var value = data.datasets[datasetIndex].data[index];
14326
14327			if (helpers.isObject(value)) {
14328				label = me.getRightValue(value);
14329			}
14330			if (timeOpts.tooltipFormat) {
14331				return momentify(label, timeOpts).format(timeOpts.tooltipFormat);
14332			}
14333			if (typeof label === 'string') {
14334				return label;
14335			}
14336
14337			return momentify(label, timeOpts).format(me._labelFormat);
14338		},
14339
14340		/**
14341		 * Function to format an individual tick mark
14342		 * @private
14343		 */
14344		tickFormatFunction: function(tick, index, ticks, formatOverride) {
14345			var me = this;
14346			var options = me.options;
14347			var time = tick.valueOf();
14348			var formats = options.time.displayFormats;
14349			var minorFormat = formats[me._unit];
14350			var majorUnit = me._majorUnit;
14351			var majorFormat = formats[majorUnit];
14352			var majorTime = tick.clone().startOf(majorUnit).valueOf();
14353			var majorTickOpts = options.ticks.major;
14354			var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
14355			var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat);
14356			var tickOpts = major ? majorTickOpts : options.ticks.minor;
14357			var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
14358
14359			return formatter ? formatter(label, index, ticks) : label;
14360		},
14361
14362		convertTicksToLabels: function(ticks) {
14363			var labels = [];
14364			var i, ilen;
14365
14366			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
14367				labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
14368			}
14369
14370			return labels;
14371		},
14372
14373		/**
14374		 * @private
14375		 */
14376		getPixelForOffset: function(time) {
14377			var me = this;
14378			var size = me._horizontal ? me.width : me.height;
14379			var start = me._horizontal ? me.left : me.top;
14380			var pos = interpolate(me._table, 'time', time, 'pos');
14381
14382			return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
14383		},
14384
14385		getPixelForValue: function(value, index, datasetIndex) {
14386			var me = this;
14387			var time = null;
14388
14389			if (index !== undefined && datasetIndex !== undefined) {
14390				time = me._timestamps.datasets[datasetIndex][index];
14391			}
14392
14393			if (time === null) {
14394				time = parse(value, me);
14395			}
14396
14397			if (time !== null) {
14398				return me.getPixelForOffset(time);
14399			}
14400		},
14401
14402		getPixelForTick: function(index) {
14403			var ticks = this.getTicks();
14404			return index >= 0 && index < ticks.length ?
14405				this.getPixelForOffset(ticks[index].value) :
14406				null;
14407		},
14408
14409		getValueForPixel: function(pixel) {
14410			var me = this;
14411			var size = me._horizontal ? me.width : me.height;
14412			var start = me._horizontal ? me.left : me.top;
14413			var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
14414			var time = interpolate(me._table, 'pos', pos, 'time');
14415
14416			return moment(time);
14417		},
14418
14419		/**
14420		 * Crude approximation of what the label width might be
14421		 * @private
14422		 */
14423		getLabelWidth: function(label) {
14424			var me = this;
14425			var ticksOpts = me.options.ticks;
14426			var tickLabelWidth = me.ctx.measureText(label).width;
14427			var angle = helpers.toRadians(ticksOpts.maxRotation);
14428			var cosRotation = Math.cos(angle);
14429			var sinRotation = Math.sin(angle);
14430			var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
14431
14432			return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
14433		},
14434
14435		/**
14436		 * @private
14437		 */
14438		getLabelCapacity: function(exampleTime) {
14439			var me = this;
14440
14441			var formatOverride = me.options.time.displayFormats.millisecond;	// Pick the longest format for guestimation
14442
14443			var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride);
14444			var tickLabelWidth = me.getLabelWidth(exampleLabel);
14445			var innerWidth = me.isHorizontal() ? me.width : me.height;
14446
14447			var capacity = Math.floor(innerWidth / tickLabelWidth);
14448			return capacity > 0 ? capacity : 1;
14449		}
14450	});
14451
14452	scaleService.registerScaleType('time', TimeScale, defaultConfig);
14453};
14454
14455},{"1":1,"26":26,"33":33,"34":34,"46":46}]},{},[7])(7)
14456});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.