PageSourceSearch

https://cdn.koc.com.tr/cmscontainer/static/components/Chart.js?v=7be6cdce95a0d08b

js koc.com.tr collected 2026-09-24 18:59:18 UTC 460,792 bytes, 16,172 lines download raw bytes

1/*!
2 * Chart.js v2.9.4
3 * https://www.chartjs.org
4 * (c) 2020 Chart.js Contributors
5 * Released under the MIT License
6 */
7(function (global, factory) {
8	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) :
9	typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) :
10	(global = global || self, global.Chart = factory(global.moment));
11	}(this, (function (moment) { 'use strict';
12	
13	moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment;
14	
15	function createCommonjsModule(fn, module) {
16		return module = { exports: {} }, fn(module, module.exports), module.exports;
17	}
18	
19	function getCjsExportFromNamespace (n) {
20		return n && n['default'] || n;
21	}
22	
23	var colorName = {
24		"aliceblue": [240, 248, 255],
25		"antiquewhite": [250, 235, 215],
26		"aqua": [0, 255, 255],
27		"aquamarine": [127, 255, 212],
28		"azure": [240, 255, 255],
29		"beige": [245, 245, 220],
30		"bisque": [255, 228, 196],
31		"black": [0, 0, 0],
32		"blanchedalmond": [255, 235, 205],
33		"blue": [0, 0, 255],
34		"blueviolet": [138, 43, 226],
35		"brown": [165, 42, 42],
36		"burlywood": [222, 184, 135],
37		"cadetblue": [95, 158, 160],
38		"chartreuse": [127, 255, 0],
39		"chocolate": [210, 105, 30],
40		"coral": [255, 127, 80],
41		"cornflowerblue": [100, 149, 237],
42		"cornsilk": [255, 248, 220],
43		"crimson": [220, 20, 60],
44		"cyan": [0, 255, 255],
45		"darkblue": [0, 0, 139],
46		"darkcyan": [0, 139, 139],
47		"darkgoldenrod": [184, 134, 11],
48		"darkgray": [169, 169, 169],
49		"darkgreen": [0, 100, 0],
50		"darkgrey": [169, 169, 169],
51		"darkkhaki": [189, 183, 107],
52		"darkmagenta": [139, 0, 139],
53		"darkolivegreen": [85, 107, 47],
54		"darkorange": [255, 140, 0],
55		"darkorchid": [153, 50, 204],
56		"darkred": [139, 0, 0],
57		"darksalmon": [233, 150, 122],
58		"darkseagreen": [143, 188, 143],
59		"darkslateblue": [72, 61, 139],
60		"darkslategray": [47, 79, 79],
61		"darkslategrey": [47, 79, 79],
62		"darkturquoise": [0, 206, 209],
63		"darkviolet": [148, 0, 211],
64		"deeppink": [255, 20, 147],
65		"deepskyblue": [0, 191, 255],
66		"dimgray": [105, 105, 105],
67		"dimgrey": [105, 105, 105],
68		"dodgerblue": [30, 144, 255],
69		"firebrick": [178, 34, 34],
70		"floralwhite": [255, 250, 240],
71		"forestgreen": [34, 139, 34],
72		"fuchsia": [255, 0, 255],
73		"gainsboro": [220, 220, 220],
74		"ghostwhite": [248, 248, 255],
75		"gold": [255, 215, 0],
76		"goldenrod": [218, 165, 32],
77		"gray": [128, 128, 128],
78		"green": [0, 128, 0],
79		"greenyellow": [173, 255, 47],
80		"grey": [128, 128, 128],
81		"honeydew": [240, 255, 240],
82		"hotpink": [255, 105, 180],
83		"indianred": [205, 92, 92],
84		"indigo": [75, 0, 130],
85		"ivory": [255, 255, 240],
86		"khaki": [240, 230, 140],
87		"lavender": [230, 230, 250],
88		"lavenderblush": [255, 240, 245],
89		"lawngreen": [124, 252, 0],
90		"lemonchiffon": [255, 250, 205],
91		"lightblue": [173, 216, 230],
92		"lightcoral": [240, 128, 128],
93		"lightcyan": [224, 255, 255],
94		"lightgoldenrodyellow": [250, 250, 210],
95		"lightgray": [211, 211, 211],
96		"lightgreen": [144, 238, 144],
97		"lightgrey": [211, 211, 211],
98		"lightpink": [255, 182, 193],
99		"lightsalmon": [255, 160, 122],
100		"lightseagreen": [32, 178, 170],
101		"lightskyblue": [135, 206, 250],
102		"lightslategray": [119, 136, 153],
103		"lightslategrey": [119, 136, 153],
104		"lightsteelblue": [176, 196, 222],
105		"lightyellow": [255, 255, 224],
106		"lime": [0, 255, 0],
107		"limegreen": [50, 205, 50],
108		"linen": [250, 240, 230],
109		"magenta": [255, 0, 255],
110		"maroon": [128, 0, 0],
111		"mediumaquamarine": [102, 205, 170],
112		"mediumblue": [0, 0, 205],
113		"mediumorchid": [186, 85, 211],
114		"mediumpurple": [147, 112, 219],
115		"mediumseagreen": [60, 179, 113],
116		"mediumslateblue": [123, 104, 238],
117		"mediumspringgreen": [0, 250, 154],
118		"mediumturquoise": [72, 209, 204],
119		"mediumvioletred": [199, 21, 133],
120		"midnightblue": [25, 25, 112],
121		"mintcream": [245, 255, 250],
122		"mistyrose": [255, 228, 225],
123		"moccasin": [255, 228, 181],
124		"navajowhite": [255, 222, 173],
125		"navy": [0, 0, 128],
126		"oldlace": [253, 245, 230],
127		"olive": [128, 128, 0],
128		"olivedrab": [107, 142, 35],
129		"orange": [255, 165, 0],
130		"orangered": [255, 69, 0],
131		"orchid": [218, 112, 214],
132		"palegoldenrod": [238, 232, 170],
133		"palegreen": [152, 251, 152],
vendor: 456,313 bytes, lines 134-16172
134		"paleturquoise": [175, 238, 238],
135		"palevioletred": [219, 112, 147],
136		"papayawhip": [255, 239, 213],
137		"peachpuff": [255, 218, 185],
138		"peru": [205, 133, 63],
139		"pink": [255, 192, 203],
140		"plum": [221, 160, 221],
141		"powderblue": [176, 224, 230],
142		"purple": [128, 0, 128],
143		"rebeccapurple": [102, 51, 153],
144		"red": [255, 0, 0],
145		"rosybrown": [188, 143, 143],
146		"royalblue": [65, 105, 225],
147		"saddlebrown": [139, 69, 19],
148		"salmon": [250, 128, 114],
149		"sandybrown": [244, 164, 96],
150		"seagreen": [46, 139, 87],
151		"seashell": [255, 245, 238],
152		"sienna": [160, 82, 45],
153		"silver": [192, 192, 192],
154		"skyblue": [135, 206, 235],
155		"slateblue": [106, 90, 205],
156		"slategray": [112, 128, 144],
157		"slategrey": [112, 128, 144],
158		"snow": [255, 250, 250],
159		"springgreen": [0, 255, 127],
160		"steelblue": [70, 130, 180],
161		"tan": [210, 180, 140],
162		"teal": [0, 128, 128],
163		"thistle": [216, 191, 216],
164		"tomato": [255, 99, 71],
165		"turquoise": [64, 224, 208],
166		"violet": [238, 130, 238],
167		"wheat": [245, 222, 179],
168		"white": [255, 255, 255],
169		"whitesmoke": [245, 245, 245],
170		"yellow": [255, 255, 0],
171		"yellowgreen": [154, 205, 50]
172	};
173	
174	var conversions = createCommonjsModule(function (module) {
175	/* MIT license */
176	
177	
178	// NOTE: conversions should only return primitive values (i.e. arrays, or
179	//       values that give correct `typeof` results).
180	//       do not use box values types (i.e. Number(), String(), etc.)
181	
182	var reverseKeywords = {};
183	for (var key in colorName) {
184		if (colorName.hasOwnProperty(key)) {
185			reverseKeywords[colorName[key]] = key;
186		}
187	}
188	
189	var convert = module.exports = {
190		rgb: {channels: 3, labels: 'rgb'},
191		hsl: {channels: 3, labels: 'hsl'},
192		hsv: {channels: 3, labels: 'hsv'},
193		hwb: {channels: 3, labels: 'hwb'},
194		cmyk: {channels: 4, labels: 'cmyk'},
195		xyz: {channels: 3, labels: 'xyz'},
196		lab: {channels: 3, labels: 'lab'},
197		lch: {channels: 3, labels: 'lch'},
198		hex: {channels: 1, labels: ['hex']},
199		keyword: {channels: 1, labels: ['keyword']},
200		ansi16: {channels: 1, labels: ['ansi16']},
201		ansi256: {channels: 1, labels: ['ansi256']},
202		hcg: {channels: 3, labels: ['h', 'c', 'g']},
203		apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
204		gray: {channels: 1, labels: ['gray']}
205	};
206	
207	// hide .channels and .labels properties
208	for (var model in convert) {
209		if (convert.hasOwnProperty(model)) {
210			if (!('channels' in convert[model])) {
211				throw new Error('missing channels property: ' + model);
212			}
213	
214			if (!('labels' in convert[model])) {
215				throw new Error('missing channel labels property: ' + model);
216			}
217	
218			if (convert[model].labels.length !== convert[model].channels) {
219				throw new Error('channel and label counts mismatch: ' + model);
220			}
221	
222			var channels = convert[model].channels;
223			var labels = convert[model].labels;
224			delete convert[model].channels;
225			delete convert[model].labels;
226			Object.defineProperty(convert[model], 'channels', {value: channels});
227			Object.defineProperty(convert[model], 'labels', {value: labels});
228		}
229	}
230	
231	convert.rgb.hsl = function (rgb) {
232		var r = rgb[0] / 255;
233		var g = rgb[1] / 255;
234		var b = rgb[2] / 255;
235		var min = Math.min(r, g, b);
236		var max = Math.max(r, g, b);
237		var delta = max - min;
238		var h;
239		var s;
240		var l;
241	
242		if (max === min) {
243			h = 0;
244		} else if (r === max) {
245			h = (g - b) / delta;
246		} else if (g === max) {
247			h = 2 + (b - r) / delta;
248		} else if (b === max) {
249			h = 4 + (r - g) / delta;
250		}
251	
252		h = Math.min(h * 60, 360);
253	
254		if (h < 0) {
255			h += 360;
256		}
257	
258		l = (min + max) / 2;
259	
260		if (max === min) {
261			s = 0;
262		} else if (l <= 0.5) {
263			s = delta / (max + min);
264		} else {
265			s = delta / (2 - max - min);
266		}
267	
268		return [h, s * 100, l * 100];
269	};
270	
271	convert.rgb.hsv = function (rgb) {
272		var rdif;
273		var gdif;
274		var bdif;
275		var h;
276		var s;
277	
278		var r = rgb[0] / 255;
279		var g = rgb[1] / 255;
280		var b = rgb[2] / 255;
281		var v = Math.max(r, g, b);
282		var diff = v - Math.min(r, g, b);
283		var diffc = function (c) {
284			return (v - c) / 6 / diff + 1 / 2;
285		};
286	
287		if (diff === 0) {
288			h = s = 0;
289		} else {
290			s = diff / v;
291			rdif = diffc(r);
292			gdif = diffc(g);
293			bdif = diffc(b);
294	
295			if (r === v) {
296				h = bdif - gdif;
297			} else if (g === v) {
298				h = (1 / 3) + rdif - bdif;
299			} else if (b === v) {
300				h = (2 / 3) + gdif - rdif;
301			}
302			if (h < 0) {
303				h += 1;
304			} else if (h > 1) {
305				h -= 1;
306			}
307		}
308	
309		return [
310			h * 360,
311			s * 100,
312			v * 100
313		];
314	};
315	
316	convert.rgb.hwb = function (rgb) {
317		var r = rgb[0];
318		var g = rgb[1];
319		var b = rgb[2];
320		var h = convert.rgb.hsl(rgb)[0];
321		var w = 1 / 255 * Math.min(r, Math.min(g, b));
322	
323		b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
324	
325		return [h, w * 100, b * 100];
326	};
327	
328	convert.rgb.cmyk = function (rgb) {
329		var r = rgb[0] / 255;
330		var g = rgb[1] / 255;
331		var b = rgb[2] / 255;
332		var c;
333		var m;
334		var y;
335		var k;
336	
337		k = Math.min(1 - r, 1 - g, 1 - b);
338		c = (1 - r - k) / (1 - k) || 0;
339		m = (1 - g - k) / (1 - k) || 0;
340		y = (1 - b - k) / (1 - k) || 0;
341	
342		return [c * 100, m * 100, y * 100, k * 100];
343	};
344	
345	/**
346	 * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
347	 * */
348	function comparativeDistance(x, y) {
349		return (
350			Math.pow(x[0] - y[0], 2) +
351			Math.pow(x[1] - y[1], 2) +
352			Math.pow(x[2] - y[2], 2)
353		);
354	}
355	
356	convert.rgb.keyword = function (rgb) {
357		var reversed = reverseKeywords[rgb];
358		if (reversed) {
359			return reversed;
360		}
361	
362		var currentClosestDistance = Infinity;
363		var currentClosestKeyword;
364	
365		for (var keyword in colorName) {
366			if (colorName.hasOwnProperty(keyword)) {
367				var value = colorName[keyword];
368	
369				// Compute comparative distance
370				var distance = comparativeDistance(rgb, value);
371	
372				// Check if its less, if so set as closest
373				if (distance < currentClosestDistance) {
374					currentClosestDistance = distance;
375					currentClosestKeyword = keyword;
376				}
377			}
378		}
379	
380		return currentClosestKeyword;
381	};
382	
383	convert.keyword.rgb = function (keyword) {
384		return colorName[keyword];
385	};
386	
387	convert.rgb.xyz = function (rgb) {
388		var r = rgb[0] / 255;
389		var g = rgb[1] / 255;
390		var b = rgb[2] / 255;
391	
392		// assume sRGB
393		r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
394		g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
395		b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
396	
397		var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
398		var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
399		var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
400	
401		return [x * 100, y * 100, z * 100];
402	};
403	
404	convert.rgb.lab = function (rgb) {
405		var xyz = convert.rgb.xyz(rgb);
406		var x = xyz[0];
407		var y = xyz[1];
408		var z = xyz[2];
409		var l;
410		var a;
411		var b;
412	
413		x /= 95.047;
414		y /= 100;
415		z /= 108.883;
416	
417		x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
418		y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
419		z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
420	
421		l = (116 * y) - 16;
422		a = 500 * (x - y);
423		b = 200 * (y - z);
424	
425		return [l, a, b];
426	};
427	
428	convert.hsl.rgb = function (hsl) {
429		var h = hsl[0] / 360;
430		var s = hsl[1] / 100;
431		var l = hsl[2] / 100;
432		var t1;
433		var t2;
434		var t3;
435		var rgb;
436		var val;
437	
438		if (s === 0) {
439			val = l * 255;
440			return [val, val, val];
441		}
442	
443		if (l < 0.5) {
444			t2 = l * (1 + s);
445		} else {
446			t2 = l + s - l * s;
447		}
448	
449		t1 = 2 * l - t2;
450	
451		rgb = [0, 0, 0];
452		for (var i = 0; i < 3; i++) {
453			t3 = h + 1 / 3 * -(i - 1);
454			if (t3 < 0) {
455				t3++;
456			}
457			if (t3 > 1) {
458				t3--;
459			}
460	
461			if (6 * t3 < 1) {
462				val = t1 + (t2 - t1) * 6 * t3;
463			} else if (2 * t3 < 1) {
464				val = t2;
465			} else if (3 * t3 < 2) {
466				val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
467			} else {
468				val = t1;
469			}
470	
471			rgb[i] = val * 255;
472		}
473	
474		return rgb;
475	};
476	
477	convert.hsl.hsv = function (hsl) {
478		var h = hsl[0];
479		var s = hsl[1] / 100;
480		var l = hsl[2] / 100;
481		var smin = s;
482		var lmin = Math.max(l, 0.01);
483		var sv;
484		var v;
485	
486		l *= 2;
487		s *= (l <= 1) ? l : 2 - l;
488		smin *= lmin <= 1 ? lmin : 2 - lmin;
489		v = (l + s) / 2;
490		sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
491	
492		return [h, sv * 100, v * 100];
493	};
494	
495	convert.hsv.rgb = function (hsv) {
496		var h = hsv[0] / 60;
497		var s = hsv[1] / 100;
498		var v = hsv[2] / 100;
499		var hi = Math.floor(h) % 6;
500	
501		var f = h - Math.floor(h);
502		var p = 255 * v * (1 - s);
503		var q = 255 * v * (1 - (s * f));
504		var t = 255 * v * (1 - (s * (1 - f)));
505		v *= 255;
506	
507		switch (hi) {
508			case 0:
509				return [v, t, p];
510			case 1:
511				return [q, v, p];
512			case 2:
513				return [p, v, t];
514			case 3:
515				return [p, q, v];
516			case 4:
517				return [t, p, v];
518			case 5:
519				return [v, p, q];
520		}
521	};
522	
523	convert.hsv.hsl = function (hsv) {
524		var h = hsv[0];
525		var s = hsv[1] / 100;
526		var v = hsv[2] / 100;
527		var vmin = Math.max(v, 0.01);
528		var lmin;
529		var sl;
530		var l;
531	
532		l = (2 - s) * v;
533		lmin = (2 - s) * vmin;
534		sl = s * vmin;
535		sl /= (lmin <= 1) ? lmin : 2 - lmin;
536		sl = sl || 0;
537		l /= 2;
538	
539		return [h, sl * 100, l * 100];
540	};
541	
542	// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
543	convert.hwb.rgb = function (hwb) {
544		var h = hwb[0] / 360;
545		var wh = hwb[1] / 100;
546		var bl = hwb[2] / 100;
547		var ratio = wh + bl;
548		var i;
549		var v;
550		var f;
551		var n;
552	
553		// wh + bl cant be > 1
554		if (ratio > 1) {
555			wh /= ratio;
556			bl /= ratio;
557		}
558	
559		i = Math.floor(6 * h);
560		v = 1 - bl;
561		f = 6 * h - i;
562	
563		if ((i & 0x01) !== 0) {
564			f = 1 - f;
565		}
566	
567		n = wh + f * (v - wh); // linear interpolation
568	
569		var r;
570		var g;
571		var b;
572		switch (i) {
573			default:
574			case 6:
575			case 0: r = v; g = n; b = wh; break;
576			case 1: r = n; g = v; b = wh; break;
577			case 2: r = wh; g = v; b = n; break;
578			case 3: r = wh; g = n; b = v; break;
579			case 4: r = n; g = wh; b = v; break;
580			case 5: r = v; g = wh; b = n; break;
581		}
582	
583		return [r * 255, g * 255, b * 255];
584	};
585	
586	convert.cmyk.rgb = function (cmyk) {
587		var c = cmyk[0] / 100;
588		var m = cmyk[1] / 100;
589		var y = cmyk[2] / 100;
590		var k = cmyk[3] / 100;
591		var r;
592		var g;
593		var b;
594	
595		r = 1 - Math.min(1, c * (1 - k) + k);
596		g = 1 - Math.min(1, m * (1 - k) + k);
597		b = 1 - Math.min(1, y * (1 - k) + k);
598	
599		return [r * 255, g * 255, b * 255];
600	};
601	
602	convert.xyz.rgb = function (xyz) {
603		var x = xyz[0] / 100;
604		var y = xyz[1] / 100;
605		var z = xyz[2] / 100;
606		var r;
607		var g;
608		var b;
609	
610		r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
611		g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
612		b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
613	
614		// assume sRGB
615		r = r > 0.0031308
616			? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
617			: r * 12.92;
618	
619		g = g > 0.0031308
620			? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
621			: g * 12.92;
622	
623		b = b > 0.0031308
624			? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
625			: b * 12.92;
626	
627		r = Math.min(Math.max(0, r), 1);
628		g = Math.min(Math.max(0, g), 1);
629		b = Math.min(Math.max(0, b), 1);
630	
631		return [r * 255, g * 255, b * 255];
632	};
633	
634	convert.xyz.lab = function (xyz) {
635		var x = xyz[0];
636		var y = xyz[1];
637		var z = xyz[2];
638		var l;
639		var a;
640		var b;
641	
642		x /= 95.047;
643		y /= 100;
644		z /= 108.883;
645	
646		x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
647		y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
648		z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
649	
650		l = (116 * y) - 16;
651		a = 500 * (x - y);
652		b = 200 * (y - z);
653	
654		return [l, a, b];
655	};
656	
657	convert.lab.xyz = function (lab) {
658		var l = lab[0];
659		var a = lab[1];
660		var b = lab[2];
661		var x;
662		var y;
663		var z;
664	
665		y = (l + 16) / 116;
666		x = a / 500 + y;
667		z = y - b / 200;
668	
669		var y2 = Math.pow(y, 3);
670		var x2 = Math.pow(x, 3);
671		var z2 = Math.pow(z, 3);
672		y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
673		x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
674		z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
675	
676		x *= 95.047;
677		y *= 100;
678		z *= 108.883;
679	
680		return [x, y, z];
681	};
682	
683	convert.lab.lch = function (lab) {
684		var l = lab[0];
685		var a = lab[1];
686		var b = lab[2];
687		var hr;
688		var h;
689		var c;
690	
691		hr = Math.atan2(b, a);
692		h = hr * 360 / 2 / Math.PI;
693	
694		if (h < 0) {
695			h += 360;
696		}
697	
698		c = Math.sqrt(a * a + b * b);
699	
700		return [l, c, h];
701	};
702	
703	convert.lch.lab = function (lch) {
704		var l = lch[0];
705		var c = lch[1];
706		var h = lch[2];
707		var a;
708		var b;
709		var hr;
710	
711		hr = h / 360 * 2 * Math.PI;
712		a = c * Math.cos(hr);
713		b = c * Math.sin(hr);
714	
715		return [l, a, b];
716	};
717	
718	convert.rgb.ansi16 = function (args) {
719		var r = args[0];
720		var g = args[1];
721		var b = args[2];
722		var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization
723	
724		value = Math.round(value / 50);
725	
726		if (value === 0) {
727			return 30;
728		}
729	
730		var ansi = 30
731			+ ((Math.round(b / 255) << 2)
732			| (Math.round(g / 255) << 1)
733			| Math.round(r / 255));
734	
735		if (value === 2) {
736			ansi += 60;
737		}
738	
739		return ansi;
740	};
741	
742	convert.hsv.ansi16 = function (args) {
743		// optimization here; we already know the value and don't need to get
744		// it converted for us.
745		return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
746	};
747	
748	convert.rgb.ansi256 = function (args) {
749		var r = args[0];
750		var g = args[1];
751		var b = args[2];
752	
753		// we use the extended greyscale palette here, with the exception of
754		// black and white. normal palette only has 4 greyscale shades.
755		if (r === g && g === b) {
756			if (r < 8) {
757				return 16;
758			}
759	
760			if (r > 248) {
761				return 231;
762			}
763	
764			return Math.round(((r - 8) / 247) * 24) + 232;
765		}
766	
767		var ansi = 16
768			+ (36 * Math.round(r / 255 * 5))
769			+ (6 * Math.round(g / 255 * 5))
770			+ Math.round(b / 255 * 5);
771	
772		return ansi;
773	};
774	
775	convert.ansi16.rgb = function (args) {
776		var color = args % 10;
777	
778		// handle greyscale
779		if (color === 0 || color === 7) {
780			if (args > 50) {
781				color += 3.5;
782			}
783	
784			color = color / 10.5 * 255;
785	
786			return [color, color, color];
787		}
788	
789		var mult = (~~(args > 50) + 1) * 0.5;
790		var r = ((color & 1) * mult) * 255;
791		var g = (((color >> 1) & 1) * mult) * 255;
792		var b = (((color >> 2) & 1) * mult) * 255;
793	
794		return [r, g, b];
795	};
796	
797	convert.ansi256.rgb = function (args) {
798		// handle greyscale
799		if (args >= 232) {
800			var c = (args - 232) * 10 + 8;
801			return [c, c, c];
802		}
803	
804		args -= 16;
805	
806		var rem;
807		var r = Math.floor(args / 36) / 5 * 255;
808		var g = Math.floor((rem = args % 36) / 6) / 5 * 255;
809		var b = (rem % 6) / 5 * 255;
810	
811		return [r, g, b];
812	};
813	
814	convert.rgb.hex = function (args) {
815		var integer = ((Math.round(args[0]) & 0xFF) << 16)
816			+ ((Math.round(args[1]) & 0xFF) << 8)
817			+ (Math.round(args[2]) & 0xFF);
818	
819		var string = integer.toString(16).toUpperCase();
820		return '000000'.substring(string.length) + string;
821	};
822	
823	convert.hex.rgb = function (args) {
824		var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
825		if (!match) {
826			return [0, 0, 0];
827		}
828	
829		var colorString = match[0];
830	
831		if (match[0].length === 3) {
832			colorString = colorString.split('').map(function (char) {
833				return char + char;
834			}).join('');
835		}
836	
837		var integer = parseInt(colorString, 16);
838		var r = (integer >> 16) & 0xFF;
839		var g = (integer >> 8) & 0xFF;
840		var b = integer & 0xFF;
841	
842		return [r, g, b];
843	};
844	
845	convert.rgb.hcg = function (rgb) {
846		var r = rgb[0] / 255;
847		var g = rgb[1] / 255;
848		var b = rgb[2] / 255;
849		var max = Math.max(Math.max(r, g), b);
850		var min = Math.min(Math.min(r, g), b);
851		var chroma = (max - min);
852		var grayscale;
853		var hue;
854	
855		if (chroma < 1) {
856			grayscale = min / (1 - chroma);
857		} else {
858			grayscale = 0;
859		}
860	
861		if (chroma <= 0) {
862			hue = 0;
863		} else
864		if (max === r) {
865			hue = ((g - b) / chroma) % 6;
866		} else
867		if (max === g) {
868			hue = 2 + (b - r) / chroma;
869		} else {
870			hue = 4 + (r - g) / chroma + 4;
871		}
872	
873		hue /= 6;
874		hue %= 1;
875	
876		return [hue * 360, chroma * 100, grayscale * 100];
877	};
878	
879	convert.hsl.hcg = function (hsl) {
880		var s = hsl[1] / 100;
881		var l = hsl[2] / 100;
882		var c = 1;
883		var f = 0;
884	
885		if (l < 0.5) {
886			c = 2.0 * s * l;
887		} else {
888			c = 2.0 * s * (1.0 - l);
889		}
890	
891		if (c < 1.0) {
892			f = (l - 0.5 * c) / (1.0 - c);
893		}
894	
895		return [hsl[0], c * 100, f * 100];
896	};
897	
898	convert.hsv.hcg = function (hsv) {
899		var s = hsv[1] / 100;
900		var v = hsv[2] / 100;
901	
902		var c = s * v;
903		var f = 0;
904	
905		if (c < 1.0) {
906			f = (v - c) / (1 - c);
907		}
908	
909		return [hsv[0], c * 100, f * 100];
910	};
911	
912	convert.hcg.rgb = function (hcg) {
913		var h = hcg[0] / 360;
914		var c = hcg[1] / 100;
915		var g = hcg[2] / 100;
916	
917		if (c === 0.0) {
918			return [g * 255, g * 255, g * 255];
919		}
920	
921		var pure = [0, 0, 0];
922		var hi = (h % 1) * 6;
923		var v = hi % 1;
924		var w = 1 - v;
925		var mg = 0;
926	
927		switch (Math.floor(hi)) {
928			case 0:
929				pure[0] = 1; pure[1] = v; pure[2] = 0; break;
930			case 1:
931				pure[0] = w; pure[1] = 1; pure[2] = 0; break;
932			case 2:
933				pure[0] = 0; pure[1] = 1; pure[2] = v; break;
934			case 3:
935				pure[0] = 0; pure[1] = w; pure[2] = 1; break;
936			case 4:
937				pure[0] = v; pure[1] = 0; pure[2] = 1; break;
938			default:
939				pure[0] = 1; pure[1] = 0; pure[2] = w;
940		}
941	
942		mg = (1.0 - c) * g;
943	
944		return [
945			(c * pure[0] + mg) * 255,
946			(c * pure[1] + mg) * 255,
947			(c * pure[2] + mg) * 255
948		];
949	};
950	
951	convert.hcg.hsv = function (hcg) {
952		var c = hcg[1] / 100;
953		var g = hcg[2] / 100;
954	
955		var v = c + g * (1.0 - c);
956		var f = 0;
957	
958		if (v > 0.0) {
959			f = c / v;
960		}
961	
962		return [hcg[0], f * 100, v * 100];
963	};
964	
965	convert.hcg.hsl = function (hcg) {
966		var c = hcg[1] / 100;
967		var g = hcg[2] / 100;
968	
969		var l = g * (1.0 - c) + 0.5 * c;
970		var s = 0;
971	
972		if (l > 0.0 && l < 0.5) {
973			s = c / (2 * l);
974		} else
975		if (l >= 0.5 && l < 1.0) {
976			s = c / (2 * (1 - l));
977		}
978	
979		return [hcg[0], s * 100, l * 100];
980	};
981	
982	convert.hcg.hwb = function (hcg) {
983		var c = hcg[1] / 100;
984		var g = hcg[2] / 100;
985		var v = c + g * (1.0 - c);
986		return [hcg[0], (v - c) * 100, (1 - v) * 100];
987	};
988	
989	convert.hwb.hcg = function (hwb) {
990		var w = hwb[1] / 100;
991		var b = hwb[2] / 100;
992		var v = 1 - b;
993		var c = v - w;
994		var g = 0;
995	
996		if (c < 1) {
997			g = (v - c) / (1 - c);
998		}
999	
1000		return [hwb[0], c * 100, g * 100];
1001	};
1002	
1003	convert.apple.rgb = function (apple) {
1004		return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
1005	};
1006	
1007	convert.rgb.apple = function (rgb) {
1008		return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
1009	};
1010	
1011	convert.gray.rgb = function (args) {
1012		return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
1013	};
1014	
1015	convert.gray.hsl = convert.gray.hsv = function (args) {
1016		return [0, 0, args[0]];
1017	};
1018	
1019	convert.gray.hwb = function (gray) {
1020		return [0, 100, gray[0]];
1021	};
1022	
1023	convert.gray.cmyk = function (gray) {
1024		return [0, 0, 0, gray[0]];
1025	};
1026	
1027	convert.gray.lab = function (gray) {
1028		return [gray[0], 0, 0];
1029	};
1030	
1031	convert.gray.hex = function (gray) {
1032		var val = Math.round(gray[0] / 100 * 255) & 0xFF;
1033		var integer = (val << 16) + (val << 8) + val;
1034	
1035		var string = integer.toString(16).toUpperCase();
1036		return '000000'.substring(string.length) + string;
1037	};
1038	
1039	convert.rgb.gray = function (rgb) {
1040		var val = (rgb[0] + rgb[1] + rgb[2]) / 3;
1041		return [val / 255 * 100];
1042	};
1043	});
1044	var conversions_1 = conversions.rgb;
1045	var conversions_2 = conversions.hsl;
1046	var conversions_3 = conversions.hsv;
1047	var conversions_4 = conversions.hwb;
1048	var conversions_5 = conversions.cmyk;
1049	var conversions_6 = conversions.xyz;
1050	var conversions_7 = conversions.lab;
1051	var conversions_8 = conversions.lch;
1052	var conversions_9 = conversions.hex;
1053	var conversions_10 = conversions.keyword;
1054	var conversions_11 = conversions.ansi16;
1055	var conversions_12 = conversions.ansi256;
1056	var conversions_13 = conversions.hcg;
1057	var conversions_14 = conversions.apple;
1058	var conversions_15 = conversions.gray;
1059	
1060	/*
1061		this function routes a model to all other models.
1062	
1063		all functions that are routed have a property `.conversion` attached
1064		to the returned synthetic function. This property is an array
1065		of strings, each with the steps in between the 'from' and 'to'
1066		color models (inclusive).
1067	
1068		conversions that are not possible simply are not included.
1069	*/
1070	
1071	function buildGraph() {
1072		var graph = {};
1073		// https://jsperf.com/object-keys-vs-for-in-with-closure/3
1074		var models = Object.keys(conversions);
1075	
1076		for (var len = models.length, i = 0; i < len; i++) {
1077			graph[models[i]] = {
1078				// http://jsperf.com/1-vs-infinity
1079				// micro-opt, but this is simple.
1080				distance: -1,
1081				parent: null
1082			};
1083		}
1084	
1085		return graph;
1086	}
1087	
1088	// https://en.wikipedia.org/wiki/Breadth-first_search
1089	function deriveBFS(fromModel) {
1090		var graph = buildGraph();
1091		var queue = [fromModel]; // unshift -> queue -> pop
1092	
1093		graph[fromModel].distance = 0;
1094	
1095		while (queue.length) {
1096			var current = queue.pop();
1097			var adjacents = Object.keys(conversions[current]);
1098	
1099			for (var len = adjacents.length, i = 0; i < len; i++) {
1100				var adjacent = adjacents[i];
1101				var node = graph[adjacent];
1102	
1103				if (node.distance === -1) {
1104					node.distance = graph[current].distance + 1;
1105					node.parent = current;
1106					queue.unshift(adjacent);
1107				}
1108			}
1109		}
1110	
1111		return graph;
1112	}
1113	
1114	function link(from, to) {
1115		return function (args) {
1116			return to(from(args));
1117		};
1118	}
1119	
1120	function wrapConversion(toModel, graph) {
1121		var path = [graph[toModel].parent, toModel];
1122		var fn = conversions[graph[toModel].parent][toModel];
1123	
1124		var cur = graph[toModel].parent;
1125		while (graph[cur].parent) {
1126			path.unshift(graph[cur].parent);
1127			fn = link(conversions[graph[cur].parent][cur], fn);
1128			cur = graph[cur].parent;
1129		}
1130	
1131		fn.conversion = path;
1132		return fn;
1133	}
1134	
1135	var route = function (fromModel) {
1136		var graph = deriveBFS(fromModel);
1137		var conversion = {};
1138	
1139		var models = Object.keys(graph);
1140		for (var len = models.length, i = 0; i < len; i++) {
1141			var toModel = models[i];
1142			var node = graph[toModel];
1143	
1144			if (node.parent === null) {
1145				// no possible conversion, or this node is the source model.
1146				continue;
1147			}
1148	
1149			conversion[toModel] = wrapConversion(toModel, graph);
1150		}
1151	
1152		return conversion;
1153	};
1154	
1155	var convert = {};
1156	
1157	var models = Object.keys(conversions);
1158	
1159	function wrapRaw(fn) {
1160		var wrappedFn = function (args) {
1161			if (args === undefined || args === null) {
1162				return args;
1163			}
1164	
1165			if (arguments.length > 1) {
1166				args = Array.prototype.slice.call(arguments);
1167			}
1168	
1169			return fn(args);
1170		};
1171	
1172		// preserve .conversion property if there is one
1173		if ('conversion' in fn) {
1174			wrappedFn.conversion = fn.conversion;
1175		}
1176	
1177		return wrappedFn;
1178	}
1179	
1180	function wrapRounded(fn) {
1181		var wrappedFn = function (args) {
1182			if (args === undefined || args === null) {
1183				return args;
1184			}
1185	
1186			if (arguments.length > 1) {
1187				args = Array.prototype.slice.call(arguments);
1188			}
1189	
1190			var result = fn(args);
1191	
1192			// we're assuming the result is an array here.
1193			// see notice in conversions.js; don't use box types
1194			// in conversion functions.
1195			if (typeof result === 'object') {
1196				for (var len = result.length, i = 0; i < len; i++) {
1197					result[i] = Math.round(result[i]);
1198				}
1199			}
1200	
1201			return result;
1202		};
1203	
1204		// preserve .conversion property if there is one
1205		if ('conversion' in fn) {
1206			wrappedFn.conversion = fn.conversion;
1207		}
1208	
1209		return wrappedFn;
1210	}
1211	
1212	models.forEach(function (fromModel) {
1213		convert[fromModel] = {};
1214	
1215		Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
1216		Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
1217	
1218		var routes = route(fromModel);
1219		var routeModels = Object.keys(routes);
1220	
1221		routeModels.forEach(function (toModel) {
1222			var fn = routes[toModel];
1223	
1224			convert[fromModel][toModel] = wrapRounded(fn);
1225			convert[fromModel][toModel].raw = wrapRaw(fn);
1226		});
1227	});
1228	
1229	var colorConvert = convert;
1230	
1231	var colorName$1 = {
1232		"aliceblue": [240, 248, 255],
1233		"antiquewhite": [250, 235, 215],
1234		"aqua": [0, 255, 255],
1235		"aquamarine": [127, 255, 212],
1236		"azure": [240, 255, 255],
1237		"beige": [245, 245, 220],
1238		"bisque": [255, 228, 196],
1239		"black": [0, 0, 0],
1240		"blanchedalmond": [255, 235, 205],
1241		"blue": [0, 0, 255],
1242		"blueviolet": [138, 43, 226],
1243		"brown": [165, 42, 42],
1244		"burlywood": [222, 184, 135],
1245		"cadetblue": [95, 158, 160],
1246		"chartreuse": [127, 255, 0],
1247		"chocolate": [210, 105, 30],
1248		"coral": [255, 127, 80],
1249		"cornflowerblue": [100, 149, 237],
1250		"cornsilk": [255, 248, 220],
1251		"crimson": [220, 20, 60],
1252		"cyan": [0, 255, 255],
1253		"darkblue": [0, 0, 139],
1254		"darkcyan": [0, 139, 139],
1255		"darkgoldenrod": [184, 134, 11],
1256		"darkgray": [169, 169, 169],
1257		"darkgreen": [0, 100, 0],
1258		"darkgrey": [169, 169, 169],
1259		"darkkhaki": [189, 183, 107],
1260		"darkmagenta": [139, 0, 139],
1261		"darkolivegreen": [85, 107, 47],
1262		"darkorange": [255, 140, 0],
1263		"darkorchid": [153, 50, 204],
1264		"darkred": [139, 0, 0],
1265		"darksalmon": [233, 150, 122],
1266		"darkseagreen": [143, 188, 143],
1267		"darkslateblue": [72, 61, 139],
1268		"darkslategray": [47, 79, 79],
1269		"darkslategrey": [47, 79, 79],
1270		"darkturquoise": [0, 206, 209],
1271		"darkviolet": [148, 0, 211],
1272		"deeppink": [255, 20, 147],
1273		"deepskyblue": [0, 191, 255],
1274		"dimgray": [105, 105, 105],
1275		"dimgrey": [105, 105, 105],
1276		"dodgerblue": [30, 144, 255],
1277		"firebrick": [178, 34, 34],
1278		"floralwhite": [255, 250, 240],
1279		"forestgreen": [34, 139, 34],
1280		"fuchsia": [255, 0, 255],
1281		"gainsboro": [220, 220, 220],
1282		"ghostwhite": [248, 248, 255],
1283		"gold": [255, 215, 0],
1284		"goldenrod": [218, 165, 32],
1285		"gray": [128, 128, 128],
1286		"green": [0, 128, 0],
1287		"greenyellow": [173, 255, 47],
1288		"grey": [128, 128, 128],
1289		"honeydew": [240, 255, 240],
1290		"hotpink": [255, 105, 180],
1291		"indianred": [205, 92, 92],
1292		"indigo": [75, 0, 130],
1293		"ivory": [255, 255, 240],
1294		"khaki": [240, 230, 140],
1295		"lavender": [230, 230, 250],
1296		"lavenderblush": [255, 240, 245],
1297		"lawngreen": [124, 252, 0],
1298		"lemonchiffon": [255, 250, 205],
1299		"lightblue": [173, 216, 230],
1300		"lightcoral": [240, 128, 128],
1301		"lightcyan": [224, 255, 255],
1302		"lightgoldenrodyellow": [250, 250, 210],
1303		"lightgray": [211, 211, 211],
1304		"lightgreen": [144, 238, 144],
1305		"lightgrey": [211, 211, 211],
1306		"lightpink": [255, 182, 193],
1307		"lightsalmon": [255, 160, 122],
1308		"lightseagreen": [32, 178, 170],
1309		"lightskyblue": [135, 206, 250],
1310		"lightslategray": [119, 136, 153],
1311		"lightslategrey": [119, 136, 153],
1312		"lightsteelblue": [176, 196, 222],
1313		"lightyellow": [255, 255, 224],
1314		"lime": [0, 255, 0],
1315		"limegreen": [50, 205, 50],
1316		"linen": [250, 240, 230],
1317		"magenta": [255, 0, 255],
1318		"maroon": [128, 0, 0],
1319		"mediumaquamarine": [102, 205, 170],
1320		"mediumblue": [0, 0, 205],
1321		"mediumorchid": [186, 85, 211],
1322		"mediumpurple": [147, 112, 219],
1323		"mediumseagreen": [60, 179, 113],
1324		"mediumslateblue": [123, 104, 238],
1325		"mediumspringgreen": [0, 250, 154],
1326		"mediumturquoise": [72, 209, 204],
1327		"mediumvioletred": [199, 21, 133],
1328		"midnightblue": [25, 25, 112],
1329		"mintcream": [245, 255, 250],
1330		"mistyrose": [255, 228, 225],
1331		"moccasin": [255, 228, 181],
1332		"navajowhite": [255, 222, 173],
1333		"navy": [0, 0, 128],
1334		"oldlace": [253, 245, 230],
1335		"olive": [128, 128, 0],
1336		"olivedrab": [107, 142, 35],
1337		"orange": [255, 165, 0],
1338		"orangered": [255, 69, 0],
1339		"orchid": [218, 112, 214],
1340		"palegoldenrod": [238, 232, 170],
1341		"palegreen": [152, 251, 152],
1342		"paleturquoise": [175, 238, 238],
1343		"palevioletred": [219, 112, 147],
1344		"papayawhip": [255, 239, 213],
1345		"peachpuff": [255, 218, 185],
1346		"peru": [205, 133, 63],
1347		"pink": [255, 192, 203],
1348		"plum": [221, 160, 221],
1349		"powderblue": [176, 224, 230],
1350		"purple": [128, 0, 128],
1351		"rebeccapurple": [102, 51, 153],
1352		"red": [255, 0, 0],
1353		"rosybrown": [188, 143, 143],
1354		"royalblue": [65, 105, 225],
1355		"saddlebrown": [139, 69, 19],
1356		"salmon": [250, 128, 114],
1357		"sandybrown": [244, 164, 96],
1358		"seagreen": [46, 139, 87],
1359		"seashell": [255, 245, 238],
1360		"sienna": [160, 82, 45],
1361		"silver": [192, 192, 192],
1362		"skyblue": [135, 206, 235],
1363		"slateblue": [106, 90, 205],
1364		"slategray": [112, 128, 144],
1365		"slategrey": [112, 128, 144],
1366		"snow": [255, 250, 250],
1367		"springgreen": [0, 255, 127],
1368		"steelblue": [70, 130, 180],
1369		"tan": [210, 180, 140],
1370		"teal": [0, 128, 128],
1371		"thistle": [216, 191, 216],
1372		"tomato": [255, 99, 71],
1373		"turquoise": [64, 224, 208],
1374		"violet": [238, 130, 238],
1375		"wheat": [245, 222, 179],
1376		"white": [255, 255, 255],
1377		"whitesmoke": [245, 245, 245],
1378		"yellow": [255, 255, 0],
1379		"yellowgreen": [154, 205, 50]
1380	};
1381	
1382	/* MIT license */
1383	
1384	
1385	var colorString = {
1386		 getRgba: getRgba,
1387		 getHsla: getHsla,
1388		 getRgb: getRgb,
1389		 getHsl: getHsl,
1390		 getHwb: getHwb,
1391		 getAlpha: getAlpha,
1392	
1393		 hexString: hexString,
1394		 rgbString: rgbString,
1395		 rgbaString: rgbaString,
1396		 percentString: percentString,
1397		 percentaString: percentaString,
1398		 hslString: hslString,
1399		 hslaString: hslaString,
1400		 hwbString: hwbString,
1401		 keyword: keyword
1402	};
1403	
1404	function getRgba(string) {
1405		 if (!string) {
1406				return;
1407		 }
1408		 var abbr =  /^#([a-fA-F0-9]{3,4})$/i,
1409				 hex =  /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i,
1410				 rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
1411				 per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
1412				 keyword = /(\w+)/;
1413	
1414		 var rgb = [0, 0, 0],
1415				 a = 1,
1416				 match = string.match(abbr),
1417				 hexAlpha = "";
1418		 if (match) {
1419				match = match[1];
1420				hexAlpha = match[3];
1421				for (var i = 0; i < rgb.length; i++) {
1422					 rgb[i] = parseInt(match[i] + match[i], 16);
1423				}
1424				if (hexAlpha) {
1425					 a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
1426				}
1427		 }
1428		 else if (match = string.match(hex)) {
1429				hexAlpha = match[2];
1430				match = match[1];
1431				for (var i = 0; i < rgb.length; i++) {
1432					 rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
1433				}
1434				if (hexAlpha) {
1435					 a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
1436				}
1437		 }
1438		 else if (match = string.match(rgba)) {
1439				for (var i = 0; i < rgb.length; i++) {
1440					 rgb[i] = parseInt(match[i + 1]);
1441				}
1442				a = parseFloat(match[4]);
1443		 }
1444		 else if (match = string.match(per)) {
1445				for (var i = 0; i < rgb.length; i++) {
1446					 rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
1447				}
1448				a = parseFloat(match[4]);
1449		 }
1450		 else if (match = string.match(keyword)) {
1451				if (match[1] == "transparent") {
1452					 return [0, 0, 0, 0];
1453				}
1454				rgb = colorName$1[match[1]];
1455				if (!rgb) {
1456					 return;
1457				}
1458		 }
1459	
1460		 for (var i = 0; i < rgb.length; i++) {
1461				rgb[i] = scale(rgb[i], 0, 255);
1462		 }
1463		 if (!a && a != 0) {
1464				a = 1;
1465		 }
1466		 else {
1467				a = scale(a, 0, 1);
1468		 }
1469		 rgb[3] = a;
1470		 return rgb;
1471	}
1472	
1473	function getHsla(string) {
1474		 if (!string) {
1475				return;
1476		 }
1477		 var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
1478		 var match = string.match(hsl);
1479		 if (match) {
1480				var alpha = parseFloat(match[4]);
1481				var h = scale(parseInt(match[1]), 0, 360),
1482						s = scale(parseFloat(match[2]), 0, 100),
1483						l = scale(parseFloat(match[3]), 0, 100),
1484						a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
1485				return [h, s, l, a];
1486		 }
1487	}
1488	
1489	function getHwb(string) {
1490		 if (!string) {
1491				return;
1492		 }
1493		 var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
1494		 var match = string.match(hwb);
1495		 if (match) {
1496			var alpha = parseFloat(match[4]);
1497				var h = scale(parseInt(match[1]), 0, 360),
1498						w = scale(parseFloat(match[2]), 0, 100),
1499						b = scale(parseFloat(match[3]), 0, 100),
1500						a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
1501				return [h, w, b, a];
1502		 }
1503	}
1504	
1505	function getRgb(string) {
1506		 var rgba = getRgba(string);
1507		 return rgba && rgba.slice(0, 3);
1508	}
1509	
1510	function getHsl(string) {
1511		var hsla = getHsla(string);
1512		return hsla && hsla.slice(0, 3);
1513	}
1514	
1515	function getAlpha(string) {
1516		 var vals = getRgba(string);
1517		 if (vals) {
1518				return vals[3];
1519		 }
1520		 else if (vals = getHsla(string)) {
1521				return vals[3];
1522		 }
1523		 else if (vals = getHwb(string)) {
1524				return vals[3];
1525		 }
1526	}
1527	
1528	// generators
1529	function hexString(rgba, a) {
1530		 var a = (a !== undefined && rgba.length === 3) ? a : rgba[3];
1531		 return "#" + hexDouble(rgba[0]) 
1532								+ hexDouble(rgba[1])
1533								+ hexDouble(rgba[2])
1534								+ (
1535									 (a >= 0 && a < 1)
1536									 ? hexDouble(Math.round(a * 255))
1537									 : ""
1538								);
1539	}
1540	
1541	function rgbString(rgba, alpha) {
1542		 if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
1543				return rgbaString(rgba, alpha);
1544		 }
1545		 return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
1546	}
1547	
1548	function rgbaString(rgba, alpha) {
1549		 if (alpha === undefined) {
1550				alpha = (rgba[3] !== undefined ? rgba[3] : 1);
1551		 }
1552		 return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
1553						 + ", " + alpha + ")";
1554	}
1555	
1556	function percentString(rgba, alpha) {
1557		 if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
1558				return percentaString(rgba, alpha);
1559		 }
1560		 var r = Math.round(rgba[0]/255 * 100),
1561				 g = Math.round(rgba[1]/255 * 100),
1562				 b = Math.round(rgba[2]/255 * 100);
1563	
1564		 return "rgb(" + r + "%, " + g + "%, " + b + "%)";
1565	}
1566	
1567	function percentaString(rgba, alpha) {
1568		 var r = Math.round(rgba[0]/255 * 100),
1569				 g = Math.round(rgba[1]/255 * 100),
1570				 b = Math.round(rgba[2]/255 * 100);
1571		 return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
1572	}
1573	
1574	function hslString(hsla, alpha) {
1575		 if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
1576				return hslaString(hsla, alpha);
1577		 }
1578		 return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
1579	}
1580	
1581	function hslaString(hsla, alpha) {
1582		 if (alpha === undefined) {
1583				alpha = (hsla[3] !== undefined ? hsla[3] : 1);
1584		 }
1585		 return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
1586						 + alpha + ")";
1587	}
1588	
1589	// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
1590	// (hwb have alpha optional & 1 is default value)
1591	function hwbString(hwb, alpha) {
1592		 if (alpha === undefined) {
1593				alpha = (hwb[3] !== undefined ? hwb[3] : 1);
1594		 }
1595		 return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
1596						 + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
1597	}
1598	
1599	function keyword(rgb) {
1600		return reverseNames[rgb.slice(0, 3)];
1601	}
1602	
1603	// helpers
1604	function scale(num, min, max) {
1605		 return Math.min(Math.max(min, num), max);
1606	}
1607	
1608	function hexDouble(num) {
1609		var str = num.toString(16).toUpperCase();
1610		return (str.length < 2) ? "0" + str : str;
1611	}
1612	
1613	
1614	//create a list of reverse color names
1615	var reverseNames = {};
1616	for (var name in colorName$1) {
1617		 reverseNames[colorName$1[name]] = name;
1618	}
1619	
1620	/* MIT license */
1621	
1622	
1623	
1624	var Color = function (obj) {
1625		if (obj instanceof Color) {
1626			return obj;
1627		}
1628		if (!(this instanceof Color)) {
1629			return new Color(obj);
1630		}
1631	
1632		this.valid = false;
1633		this.values = {
1634			rgb: [0, 0, 0],
1635			hsl: [0, 0, 0],
1636			hsv: [0, 0, 0],
1637			hwb: [0, 0, 0],
1638			cmyk: [0, 0, 0, 0],
1639			alpha: 1
1640		};
1641	
1642		// parse Color() argument
1643		var vals;
1644		if (typeof obj === 'string') {
1645			vals = colorString.getRgba(obj);
1646			if (vals) {
1647				this.setValues('rgb', vals);
1648			} else if (vals = colorString.getHsla(obj)) {
1649				this.setValues('hsl', vals);
1650			} else if (vals = colorString.getHwb(obj)) {
1651				this.setValues('hwb', vals);
1652			}
1653		} else if (typeof obj === 'object') {
1654			vals = obj;
1655			if (vals.r !== undefined || vals.red !== undefined) {
1656				this.setValues('rgb', vals);
1657			} else if (vals.l !== undefined || vals.lightness !== undefined) {
1658				this.setValues('hsl', vals);
1659			} else if (vals.v !== undefined || vals.value !== undefined) {
1660				this.setValues('hsv', vals);
1661			} else if (vals.w !== undefined || vals.whiteness !== undefined) {
1662				this.setValues('hwb', vals);
1663			} else if (vals.c !== undefined || vals.cyan !== undefined) {
1664				this.setValues('cmyk', vals);
1665			}
1666		}
1667	};
1668	
1669	Color.prototype = {
1670		isValid: function () {
1671			return this.valid;
1672		},
1673		rgb: function () {
1674			return this.setSpace('rgb', arguments);
1675		},
1676		hsl: function () {
1677			return this.setSpace('hsl', arguments);
1678		},
1679		hsv: function () {
1680			return this.setSpace('hsv', arguments);
1681		},
1682		hwb: function () {
1683			return this.setSpace('hwb', arguments);
1684		},
1685		cmyk: function () {
1686			return this.setSpace('cmyk', arguments);
1687		},
1688	
1689		rgbArray: function () {
1690			return this.values.rgb;
1691		},
1692		hslArray: function () {
1693			return this.values.hsl;
1694		},
1695		hsvArray: function () {
1696			return this.values.hsv;
1697		},
1698		hwbArray: function () {
1699			var values = this.values;
1700			if (values.alpha !== 1) {
1701				return values.hwb.concat([values.alpha]);
1702			}
1703			return values.hwb;
1704		},
1705		cmykArray: function () {
1706			return this.values.cmyk;
1707		},
1708		rgbaArray: function () {
1709			var values = this.values;
1710			return values.rgb.concat([values.alpha]);
1711		},
1712		hslaArray: function () {
1713			var values = this.values;
1714			return values.hsl.concat([values.alpha]);
1715		},
1716		alpha: function (val) {
1717			if (val === undefined) {
1718				return this.values.alpha;
1719			}
1720			this.setValues('alpha', val);
1721			return this;
1722		},
1723	
1724		red: function (val) {
1725			return this.setChannel('rgb', 0, val);
1726		},
1727		green: function (val) {
1728			return this.setChannel('rgb', 1, val);
1729		},
1730		blue: function (val) {
1731			return this.setChannel('rgb', 2, val);
1732		},
1733		hue: function (val) {
1734			if (val) {
1735				val %= 360;
1736				val = val < 0 ? 360 + val : val;
1737			}
1738			return this.setChannel('hsl', 0, val);
1739		},
1740		saturation: function (val) {
1741			return this.setChannel('hsl', 1, val);
1742		},
1743		lightness: function (val) {
1744			return this.setChannel('hsl', 2, val);
1745		},
1746		saturationv: function (val) {
1747			return this.setChannel('hsv', 1, val);
1748		},
1749		whiteness: function (val) {
1750			return this.setChannel('hwb', 1, val);
1751		},
1752		blackness: function (val) {
1753			return this.setChannel('hwb', 2, val);
1754		},
1755		value: function (val) {
1756			return this.setChannel('hsv', 2, val);
1757		},
1758		cyan: function (val) {
1759			return this.setChannel('cmyk', 0, val);
1760		},
1761		magenta: function (val) {
1762			return this.setChannel('cmyk', 1, val);
1763		},
1764		yellow: function (val) {
1765			return this.setChannel('cmyk', 2, val);
1766		},
1767		black: function (val) {
1768			return this.setChannel('cmyk', 3, val);
1769		},
1770	
1771		hexString: function () {
1772			return colorString.hexString(this.values.rgb);
1773		},
1774		rgbString: function () {
1775			return colorString.rgbString(this.values.rgb, this.values.alpha);
1776		},
1777		rgbaString: function () {
1778			return colorString.rgbaString(this.values.rgb, this.values.alpha);
1779		},
1780		percentString: function () {
1781			return colorString.percentString(this.values.rgb, this.values.alpha);
1782		},
1783		hslString: function () {
1784			return colorString.hslString(this.values.hsl, this.values.alpha);
1785		},
1786		hslaString: function () {
1787			return colorString.hslaString(this.values.hsl, this.values.alpha);
1788		},
1789		hwbString: function () {
1790			return colorString.hwbString(this.values.hwb, this.values.alpha);
1791		},
1792		keyword: function () {
1793			return colorString.keyword(this.values.rgb, this.values.alpha);
1794		},
1795	
1796		rgbNumber: function () {
1797			var rgb = this.values.rgb;
1798			return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
1799		},
1800	
1801		luminosity: function () {
1802			// http://www.w3.org/TR/WCAG20/#relativeluminancedef
1803			var rgb = this.values.rgb;
1804			var lum = [];
1805			for (var i = 0; i < rgb.length; i++) {
1806				var chan = rgb[i] / 255;
1807				lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
1808			}
1809			return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
1810		},
1811	
1812		contrast: function (color2) {
1813			// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
1814			var lum1 = this.luminosity();
1815			var lum2 = color2.luminosity();
1816			if (lum1 > lum2) {
1817				return (lum1 + 0.05) / (lum2 + 0.05);
1818			}
1819			return (lum2 + 0.05) / (lum1 + 0.05);
1820		},
1821	
1822		level: function (color2) {
1823			var contrastRatio = this.contrast(color2);
1824			if (contrastRatio >= 7.1) {
1825				return 'AAA';
1826			}
1827	
1828			return (contrastRatio >= 4.5) ? 'AA' : '';
1829		},
1830	
1831		dark: function () {
1832			// YIQ equation from http://24ways.org/2010/calculating-color-contrast
1833			var rgb = this.values.rgb;
1834			var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
1835			return yiq < 128;
1836		},
1837	
1838		light: function () {
1839			return !this.dark();
1840		},
1841	
1842		negate: function () {
1843			var rgb = [];
1844			for (var i = 0; i < 3; i++) {
1845				rgb[i] = 255 - this.values.rgb[i];
1846			}
1847			this.setValues('rgb', rgb);
1848			return this;
1849		},
1850	
1851		lighten: function (ratio) {
1852			var hsl = this.values.hsl;
1853			hsl[2] += hsl[2] * ratio;
1854			this.setValues('hsl', hsl);
1855			return this;
1856		},
1857	
1858		darken: function (ratio) {
1859			var hsl = this.values.hsl;
1860			hsl[2] -= hsl[2] * ratio;
1861			this.setValues('hsl', hsl);
1862			return this;
1863		},
1864	
1865		saturate: function (ratio) {
1866			var hsl = this.values.hsl;
1867			hsl[1] += hsl[1] * ratio;
1868			this.setValues('hsl', hsl);
1869			return this;
1870		},
1871	
1872		desaturate: function (ratio) {
1873			var hsl = this.values.hsl;
1874			hsl[1] -= hsl[1] * ratio;
1875			this.setValues('hsl', hsl);
1876			return this;
1877		},
1878	
1879		whiten: function (ratio) {
1880			var hwb = this.values.hwb;
1881			hwb[1] += hwb[1] * ratio;
1882			this.setValues('hwb', hwb);
1883			return this;
1884		},
1885	
1886		blacken: function (ratio) {
1887			var hwb = this.values.hwb;
1888			hwb[2] += hwb[2] * ratio;
1889			this.setValues('hwb', hwb);
1890			return this;
1891		},
1892	
1893		greyscale: function () {
1894			var rgb = this.values.rgb;
1895			// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
1896			var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
1897			this.setValues('rgb', [val, val, val]);
1898			return this;
1899		},
1900	
1901		clearer: function (ratio) {
1902			var alpha = this.values.alpha;
1903			this.setValues('alpha', alpha - (alpha * ratio));
1904			return this;
1905		},
1906	
1907		opaquer: function (ratio) {
1908			var alpha = this.values.alpha;
1909			this.setValues('alpha', alpha + (alpha * ratio));
1910			return this;
1911		},
1912	
1913		rotate: function (degrees) {
1914			var hsl = this.values.hsl;
1915			var hue = (hsl[0] + degrees) % 360;
1916			hsl[0] = hue < 0 ? 360 + hue : hue;
1917			this.setValues('hsl', hsl);
1918			return this;
1919		},
1920	
1921		/**
1922		 * Ported from sass implementation in C
1923		 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
1924		 */
1925		mix: function (mixinColor, weight) {
1926			var color1 = this;
1927			var color2 = mixinColor;
1928			var p = weight === undefined ? 0.5 : weight;
1929	
1930			var w = 2 * p - 1;
1931			var a = color1.alpha() - color2.alpha();
1932	
1933			var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
1934			var w2 = 1 - w1;
1935	
1936			return this
1937				.rgb(
1938					w1 * color1.red() + w2 * color2.red(),
1939					w1 * color1.green() + w2 * color2.green(),
1940					w1 * color1.blue() + w2 * color2.blue()
1941				)
1942				.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
1943		},
1944	
1945		toJSON: function () {
1946			return this.rgb();
1947		},
1948	
1949		clone: function () {
1950			// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
1951			// making the final build way to big to embed in Chart.js. So let's do it manually,
1952			// assuming that values to clone are 1 dimension arrays containing only numbers,
1953			// except 'alpha' which is a number.
1954			var result = new Color();
1955			var source = this.values;
1956			var target = result.values;
1957			var value, type;
1958	
1959			for (var prop in source) {
1960				if (source.hasOwnProperty(prop)) {
1961					value = source[prop];
1962					type = ({}).toString.call(value);
1963					if (type === '[object Array]') {
1964						target[prop] = value.slice(0);
1965					} else if (type === '[object Number]') {
1966						target[prop] = value;
1967					} else {
1968						console.error('unexpected color value:', value);
1969					}
1970				}
1971			}
1972	
1973			return result;
1974		}
1975	};
1976	
1977	Color.prototype.spaces = {
1978		rgb: ['red', 'green', 'blue'],
1979		hsl: ['hue', 'saturation', 'lightness'],
1980		hsv: ['hue', 'saturation', 'value'],
1981		hwb: ['hue', 'whiteness', 'blackness'],
1982		cmyk: ['cyan', 'magenta', 'yellow', 'black']
1983	};
1984	
1985	Color.prototype.maxes = {
1986		rgb: [255, 255, 255],
1987		hsl: [360, 100, 100],
1988		hsv: [360, 100, 100],
1989		hwb: [360, 100, 100],
1990		cmyk: [100, 100, 100, 100]
1991	};
1992	
1993	Color.prototype.getValues = function (space) {
1994		var values = this.values;
1995		var vals = {};
1996	
1997		for (var i = 0; i < space.length; i++) {
1998			vals[space.charAt(i)] = values[space][i];
1999		}
2000	
2001		if (values.alpha !== 1) {
2002			vals.a = values.alpha;
2003		}
2004	
2005		// {r: 255, g: 255, b: 255, a: 0.4}
2006		return vals;
2007	};
2008	
2009	Color.prototype.setValues = function (space, vals) {
2010		var values = this.values;
2011		var spaces = this.spaces;
2012		var maxes = this.maxes;
2013		var alpha = 1;
2014		var i;
2015	
2016		this.valid = true;
2017	
2018		if (space === 'alpha') {
2019			alpha = vals;
2020		} else if (vals.length) {
2021			// [10, 10, 10]
2022			values[space] = vals.slice(0, space.length);
2023			alpha = vals[space.length];
2024		} else if (vals[space.charAt(0)] !== undefined) {
2025			// {r: 10, g: 10, b: 10}
2026			for (i = 0; i < space.length; i++) {
2027				values[space][i] = vals[space.charAt(i)];
2028			}
2029	
2030			alpha = vals.a;
2031		} else if (vals[spaces[space][0]] !== undefined) {
2032			// {red: 10, green: 10, blue: 10}
2033			var chans = spaces[space];
2034	
2035			for (i = 0; i < space.length; i++) {
2036				values[space][i] = vals[chans[i]];
2037			}
2038	
2039			alpha = vals.alpha;
2040		}
2041	
2042		values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
2043	
2044		if (space === 'alpha') {
2045			return false;
2046		}
2047	
2048		var capped;
2049	
2050		// cap values of the space prior converting all values
2051		for (i = 0; i < space.length; i++) {
2052			capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
2053			values[space][i] = Math.round(capped);
2054		}
2055	
2056		// convert to all the other color spaces
2057		for (var sname in spaces) {
2058			if (sname !== space) {
2059				values[sname] = colorConvert[space][sname](values[space]);
2060			}
2061		}
2062	
2063		return true;
2064	};
2065	
2066	Color.prototype.setSpace = function (space, args) {
2067		var vals = args[0];
2068	
2069		if (vals === undefined) {
2070			// color.rgb()
2071			return this.getValues(space);
2072		}
2073	
2074		// color.rgb(10, 10, 10)
2075		if (typeof vals === 'number') {
2076			vals = Array.prototype.slice.call(args);
2077		}
2078	
2079		this.setValues(space, vals);
2080		return this;
2081	};
2082	
2083	Color.prototype.setChannel = function (space, index, val) {
2084		var svalues = this.values[space];
2085		if (val === undefined) {
2086			// color.red()
2087			return svalues[index];
2088		} else if (val === svalues[index]) {
2089			// color.red(color.red())
2090			return this;
2091		}
2092	
2093		// color.red(100)
2094		svalues[index] = val;
2095		this.setValues(space, svalues);
2096	
2097		return this;
2098	};
2099	
2100	if (typeof window !== 'undefined') {
2101		window.Color = Color;
2102	}
2103	
2104	var chartjsColor = Color;
2105	
2106	function isValidKey(key) {
2107		return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;
2108	}
2109	
2110	/**
2111	 * @namespace Chart.helpers
2112	 */
2113	var helpers = {
2114		/**
2115		 * An empty function that can be used, for example, for optional callback.
2116		 */
2117		noop: function() {},
2118	
2119		/**
2120		 * Returns a unique id, sequentially generated from a global variable.
2121		 * @returns {number}
2122		 * @function
2123		 */
2124		uid: (function() {
2125			var id = 0;
2126			return function() {
2127				return id++;
2128			};
2129		}()),
2130	
2131		/**
2132		 * Returns true if `value` is neither null nor undefined, else returns false.
2133		 * @param {*} value - The value to test.
2134		 * @returns {boolean}
2135		 * @since 2.7.0
2136		 */
2137		isNullOrUndef: function(value) {
2138			return value === null || typeof value === 'undefined';
2139		},
2140	
2141		/**
2142		 * Returns true if `value` is an array (including typed arrays), else returns false.
2143		 * @param {*} value - The value to test.
2144		 * @returns {boolean}
2145		 * @function
2146		 */
2147		isArray: function(value) {
2148			if (Array.isArray && Array.isArray(value)) {
2149				return true;
2150			}
2151			var type = Object.prototype.toString.call(value);
2152			if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
2153				return true;
2154			}
2155			return false;
2156		},
2157	
2158		/**
2159		 * Returns true if `value` is an object (excluding null), else returns false.
2160		 * @param {*} value - The value to test.
2161		 * @returns {boolean}
2162		 * @since 2.7.0
2163		 */
2164		isObject: function(value) {
2165			return value !== null && Object.prototype.toString.call(value) === '[object Object]';
2166		},
2167	
2168		/**
2169		 * Returns true if `value` is a finite number, else returns false
2170		 * @param {*} value  - The value to test.
2171		 * @returns {boolean}
2172		 */
2173		isFinite: function(value) {
2174			return (typeof value === 'number' || value instanceof Number) && isFinite(value);
2175		},
2176	
2177		/**
2178		 * Returns `value` if defined, else returns `defaultValue`.
2179		 * @param {*} value - The value to return if defined.
2180		 * @param {*} defaultValue - The value to return if `value` is undefined.
2181		 * @returns {*}
2182		 */
2183		valueOrDefault: function(value, defaultValue) {
2184			return typeof value === 'undefined' ? defaultValue : value;
2185		},
2186	
2187		/**
2188		 * Returns value at the given `index` in array if defined, else returns `defaultValue`.
2189		 * @param {Array} value - The array to lookup for value at `index`.
2190		 * @param {number} index - The index in `value` to lookup for value.
2191		 * @param {*} defaultValue - The value to return if `value[index]` is undefined.
2192		 * @returns {*}
2193		 */
2194		valueAtIndexOrDefault: function(value, index, defaultValue) {
2195			return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
2196		},
2197	
2198		/**
2199		 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
2200		 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
2201		 * @param {function} fn - The function to call.
2202		 * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
2203		 * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
2204		 * @returns {*}
2205		 */
2206		callback: function(fn, args, thisArg) {
2207			if (fn && typeof fn.call === 'function') {
2208				return fn.apply(thisArg, args);
2209			}
2210		},
2211	
2212		/**
2213		 * Note(SB) for performance sake, this method should only be used when loopable type
2214		 * is unknown or in none intensive code (not called often and small loopable). Else
2215		 * it's preferable to use a regular for() loop and save extra function calls.
2216		 * @param {object|Array} loopable - The object or array to be iterated.
2217		 * @param {function} fn - The function to call for each item.
2218		 * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
2219		 * @param {boolean} [reverse] - If true, iterates backward on the loopable.
2220		 */
2221		each: function(loopable, fn, thisArg, reverse) {
2222			var i, len, keys;
2223			if (helpers.isArray(loopable)) {
2224				len = loopable.length;
2225				if (reverse) {
2226					for (i = len - 1; i >= 0; i--) {
2227						fn.call(thisArg, loopable[i], i);
2228					}
2229				} else {
2230					for (i = 0; i < len; i++) {
2231						fn.call(thisArg, loopable[i], i);
2232					}
2233				}
2234			} else if (helpers.isObject(loopable)) {
2235				keys = Object.keys(loopable);
2236				len = keys.length;
2237				for (i = 0; i < len; i++) {
2238					fn.call(thisArg, loopable[keys[i]], keys[i]);
2239				}
2240			}
2241		},
2242	
2243		/**
2244		 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
2245		 * @see https://stackoverflow.com/a/14853974
2246		 * @param {Array} a0 - The array to compare
2247		 * @param {Array} a1 - The array to compare
2248		 * @returns {boolean}
2249		 */
2250		arrayEquals: function(a0, a1) {
2251			var i, ilen, v0, v1;
2252	
2253			if (!a0 || !a1 || a0.length !== a1.length) {
2254				return false;
2255			}
2256	
2257			for (i = 0, ilen = a0.length; i < ilen; ++i) {
2258				v0 = a0[i];
2259				v1 = a1[i];
2260	
2261				if (v0 instanceof Array && v1 instanceof Array) {
2262					if (!helpers.arrayEquals(v0, v1)) {
2263						return false;
2264					}
2265				} else if (v0 !== v1) {
2266					// NOTE: two different object instances will never be equal: {x:20} != {x:20}
2267					return false;
2268				}
2269			}
2270	
2271			return true;
2272		},
2273	
2274		/**
2275		 * Returns a deep copy of `source` without keeping references on objects and arrays.
2276		 * @param {*} source - The value to clone.
2277		 * @returns {*}
2278		 */
2279		clone: function(source) {
2280			if (helpers.isArray(source)) {
2281				return source.map(helpers.clone);
2282			}
2283	
2284			if (helpers.isObject(source)) {
2285				var target = Object.create(source);
2286				var keys = Object.keys(source);
2287				var klen = keys.length;
2288				var k = 0;
2289	
2290				for (; k < klen; ++k) {
2291					target[keys[k]] = helpers.clone(source[keys[k]]);
2292				}
2293	
2294				return target;
2295			}
2296	
2297			return source;
2298		},
2299	
2300		/**
2301		 * The default merger when Chart.helpers.merge is called without merger option.
2302		 * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
2303		 * @private
2304		 */
2305		_merger: function(key, target, source, options) {
2306			if (!isValidKey(key)) {
2307				// We want to ensure we do not copy prototypes over
2308				// as this can pollute global namespaces
2309				return;
2310			}
2311	
2312			var tval = target[key];
2313			var sval = source[key];
2314	
2315			if (helpers.isObject(tval) && helpers.isObject(sval)) {
2316				helpers.merge(tval, sval, options);
2317			} else {
2318				target[key] = helpers.clone(sval);
2319			}
2320		},
2321	
2322		/**
2323		 * Merges source[key] in target[key] only if target[key] is undefined.
2324		 * @private
2325		 */
2326		_mergerIf: function(key, target, source) {
2327			if (!isValidKey(key)) {
2328				// We want to ensure we do not copy prototypes over
2329				// as this can pollute global namespaces
2330				return;
2331			}
2332	
2333			var tval = target[key];
2334			var sval = source[key];
2335	
2336			if (helpers.isObject(tval) && helpers.isObject(sval)) {
2337				helpers.mergeIf(tval, sval);
2338			} else if (!target.hasOwnProperty(key)) {
2339				target[key] = helpers.clone(sval);
2340			}
2341		},
2342	
2343		/**
2344		 * Recursively deep copies `source` properties into `target` with the given `options`.
2345		 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
2346		 * @param {object} target - The target object in which all sources are merged into.
2347		 * @param {object|object[]} source - Object(s) to merge into `target`.
2348		 * @param {object} [options] - Merging options:
2349		 * @param {function} [options.merger] - The merge method (key, target, source, options)
2350		 * @returns {object} The `target` object.
2351		 */
2352		merge: function(target, source, options) {
2353			var sources = helpers.isArray(source) ? source : [source];
2354			var ilen = sources.length;
2355			var merge, i, keys, klen, k;
2356	
2357			if (!helpers.isObject(target)) {
2358				return target;
2359			}
2360	
2361			options = options || {};
2362			merge = options.merger || helpers._merger;
2363	
2364			for (i = 0; i < ilen; ++i) {
2365				source = sources[i];
2366				if (!helpers.isObject(source)) {
2367					continue;
2368				}
2369	
2370				keys = Object.keys(source);
2371				for (k = 0, klen = keys.length; k < klen; ++k) {
2372					merge(keys[k], target, source, options);
2373				}
2374			}
2375	
2376			return target;
2377		},
2378	
2379		/**
2380		 * Recursively deep copies `source` properties into `target` *only* if not defined in target.
2381		 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
2382		 * @param {object} target - The target object in which all sources are merged into.
2383		 * @param {object|object[]} source - Object(s) to merge into `target`.
2384		 * @returns {object} The `target` object.
2385		 */
2386		mergeIf: function(target, source) {
2387			return helpers.merge(target, source, {merger: helpers._mergerIf});
2388		},
2389	
2390		/**
2391		 * Applies the contents of two or more objects together into the first object.
2392		 * @param {object} target - The target object in which all objects are merged into.
2393		 * @param {object} arg1 - Object containing additional properties to merge in target.
2394		 * @param {object} argN - Additional objects containing properties to merge in target.
2395		 * @returns {object} The `target` object.
2396		 */
2397		extend: Object.assign || function(target) {
2398			return helpers.merge(target, [].slice.call(arguments, 1), {
2399				merger: function(key, dst, src) {
2400					dst[key] = src[key];
2401				}
2402			});
2403		},
2404	
2405		/**
2406		 * Basic javascript inheritance based on the model created in Backbone.js
2407		 */
2408		inherits: function(extensions) {
2409			var me = this;
2410			var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
2411				return me.apply(this, arguments);
2412			};
2413	
2414			var Surrogate = function() {
2415				this.constructor = ChartElement;
2416			};
2417	
2418			Surrogate.prototype = me.prototype;
2419			ChartElement.prototype = new Surrogate();
2420			ChartElement.extend = helpers.inherits;
2421	
2422			if (extensions) {
2423				helpers.extend(ChartElement.prototype, extensions);
2424			}
2425	
2426			ChartElement.__super__ = me.prototype;
2427			return ChartElement;
2428		},
2429	
2430		_deprecated: function(scope, value, previous, current) {
2431			if (value !== undefined) {
2432				console.warn(scope + ': "' + previous +
2433					'" is deprecated. Please use "' + current + '" instead');
2434			}
2435		}
2436	};
2437	
2438	var helpers_core = helpers;
2439	
2440	// DEPRECATIONS
2441	
2442	/**
2443	 * Provided for backward compatibility, use Chart.helpers.callback instead.
2444	 * @function Chart.helpers.callCallback
2445	 * @deprecated since version 2.6.0
2446	 * @todo remove at version 3
2447	 * @private
2448	 */
2449	helpers.callCallback = helpers.callback;
2450	
2451	/**
2452	 * Provided for backward compatibility, use Array.prototype.indexOf instead.
2453	 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
2454	 * @function Chart.helpers.indexOf
2455	 * @deprecated since version 2.7.0
2456	 * @todo remove at version 3
2457	 * @private
2458	 */
2459	helpers.indexOf = function(array, item, fromIndex) {
2460		return Array.prototype.indexOf.call(array, item, fromIndex);
2461	};
2462	
2463	/**
2464	 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
2465	 * @function Chart.helpers.getValueOrDefault
2466	 * @deprecated since version 2.7.0
2467	 * @todo remove at version 3
2468	 * @private
2469	 */
2470	helpers.getValueOrDefault = helpers.valueOrDefault;
2471	
2472	/**
2473	 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
2474	 * @function Chart.helpers.getValueAtIndexOrDefault
2475	 * @deprecated since version 2.7.0
2476	 * @todo remove at version 3
2477	 * @private
2478	 */
2479	helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2480	
2481	/**
2482	 * Easing functions adapted from Robert Penner's easing equations.
2483	 * @namespace Chart.helpers.easingEffects
2484	 * @see http://www.robertpenner.com/easing/
2485	 */
2486	var effects = {
2487		linear: function(t) {
2488			return t;
2489		},
2490	
2491		easeInQuad: function(t) {
2492			return t * t;
2493		},
2494	
2495		easeOutQuad: function(t) {
2496			return -t * (t - 2);
2497		},
2498	
2499		easeInOutQuad: function(t) {
2500			if ((t /= 0.5) < 1) {
2501				return 0.5 * t * t;
2502			}
2503			return -0.5 * ((--t) * (t - 2) - 1);
2504		},
2505	
2506		easeInCubic: function(t) {
2507			return t * t * t;
2508		},
2509	
2510		easeOutCubic: function(t) {
2511			return (t = t - 1) * t * t + 1;
2512		},
2513	
2514		easeInOutCubic: function(t) {
2515			if ((t /= 0.5) < 1) {
2516				return 0.5 * t * t * t;
2517			}
2518			return 0.5 * ((t -= 2) * t * t + 2);
2519		},
2520	
2521		easeInQuart: function(t) {
2522			return t * t * t * t;
2523		},
2524	
2525		easeOutQuart: function(t) {
2526			return -((t = t - 1) * t * t * t - 1);
2527		},
2528	
2529		easeInOutQuart: function(t) {
2530			if ((t /= 0.5) < 1) {
2531				return 0.5 * t * t * t * t;
2532			}
2533			return -0.5 * ((t -= 2) * t * t * t - 2);
2534		},
2535	
2536		easeInQuint: function(t) {
2537			return t * t * t * t * t;
2538		},
2539	
2540		easeOutQuint: function(t) {
2541			return (t = t - 1) * t * t * t * t + 1;
2542		},
2543	
2544		easeInOutQuint: function(t) {
2545			if ((t /= 0.5) < 1) {
2546				return 0.5 * t * t * t * t * t;
2547			}
2548			return 0.5 * ((t -= 2) * t * t * t * t + 2);
2549		},
2550	
2551		easeInSine: function(t) {
2552			return -Math.cos(t * (Math.PI / 2)) + 1;
2553		},
2554	
2555		easeOutSine: function(t) {
2556			return Math.sin(t * (Math.PI / 2));
2557		},
2558	
2559		easeInOutSine: function(t) {
2560			return -0.5 * (Math.cos(Math.PI * t) - 1);
2561		},
2562	
2563		easeInExpo: function(t) {
2564			return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
2565		},
2566	
2567		easeOutExpo: function(t) {
2568			return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
2569		},
2570	
2571		easeInOutExpo: function(t) {
2572			if (t === 0) {
2573				return 0;
2574			}
2575			if (t === 1) {
2576				return 1;
2577			}
2578			if ((t /= 0.5) < 1) {
2579				return 0.5 * Math.pow(2, 10 * (t - 1));
2580			}
2581			return 0.5 * (-Math.pow(2, -10 * --t) + 2);
2582		},
2583	
2584		easeInCirc: function(t) {
2585			if (t >= 1) {
2586				return t;
2587			}
2588			return -(Math.sqrt(1 - t * t) - 1);
2589		},
2590	
2591		easeOutCirc: function(t) {
2592			return Math.sqrt(1 - (t = t - 1) * t);
2593		},
2594	
2595		easeInOutCirc: function(t) {
2596			if ((t /= 0.5) < 1) {
2597				return -0.5 * (Math.sqrt(1 - t * t) - 1);
2598			}
2599			return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
2600		},
2601	
2602		easeInElastic: function(t) {
2603			var s = 1.70158;
2604			var p = 0;
2605			var a = 1;
2606			if (t === 0) {
2607				return 0;
2608			}
2609			if (t === 1) {
2610				return 1;
2611			}
2612			if (!p) {
2613				p = 0.3;
2614			}
2615			if (a < 1) {
2616				a = 1;
2617				s = p / 4;
2618			} else {
2619				s = p / (2 * Math.PI) * Math.asin(1 / a);
2620			}
2621			return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
2622		},
2623	
2624		easeOutElastic: function(t) {
2625			var s = 1.70158;
2626			var p = 0;
2627			var a = 1;
2628			if (t === 0) {
2629				return 0;
2630			}
2631			if (t === 1) {
2632				return 1;
2633			}
2634			if (!p) {
2635				p = 0.3;
2636			}
2637			if (a < 1) {
2638				a = 1;
2639				s = p / 4;
2640			} else {
2641				s = p / (2 * Math.PI) * Math.asin(1 / a);
2642			}
2643			return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
2644		},
2645	
2646		easeInOutElastic: function(t) {
2647			var s = 1.70158;
2648			var p = 0;
2649			var a = 1;
2650			if (t === 0) {
2651				return 0;
2652			}
2653			if ((t /= 0.5) === 2) {
2654				return 1;
2655			}
2656			if (!p) {
2657				p = 0.45;
2658			}
2659			if (a < 1) {
2660				a = 1;
2661				s = p / 4;
2662			} else {
2663				s = p / (2 * Math.PI) * Math.asin(1 / a);
2664			}
2665			if (t < 1) {
2666				return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
2667			}
2668			return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
2669		},
2670		easeInBack: function(t) {
2671			var s = 1.70158;
2672			return t * t * ((s + 1) * t - s);
2673		},
2674	
2675		easeOutBack: function(t) {
2676			var s = 1.70158;
2677			return (t = t - 1) * t * ((s + 1) * t + s) + 1;
2678		},
2679	
2680		easeInOutBack: function(t) {
2681			var s = 1.70158;
2682			if ((t /= 0.5) < 1) {
2683				return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
2684			}
2685			return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
2686		},
2687	
2688		easeInBounce: function(t) {
2689			return 1 - effects.easeOutBounce(1 - t);
2690		},
2691	
2692		easeOutBounce: function(t) {
2693			if (t < (1 / 2.75)) {
2694				return 7.5625 * t * t;
2695			}
2696			if (t < (2 / 2.75)) {
2697				return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
2698			}
2699			if (t < (2.5 / 2.75)) {
2700				return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
2701			}
2702			return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
2703		},
2704	
2705		easeInOutBounce: function(t) {
2706			if (t < 0.5) {
2707				return effects.easeInBounce(t * 2) * 0.5;
2708			}
2709			return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
2710		}
2711	};
2712	
2713	var helpers_easing = {
2714		effects: effects
2715	};
2716	
2717	// DEPRECATIONS
2718	
2719	/**
2720	 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
2721	 * @function Chart.helpers.easingEffects
2722	 * @deprecated since version 2.7.0
2723	 * @todo remove at version 3
2724	 * @private
2725	 */
2726	helpers_core.easingEffects = effects;
2727	
2728	var PI = Math.PI;
2729	var RAD_PER_DEG = PI / 180;
2730	var DOUBLE_PI = PI * 2;
2731	var HALF_PI = PI / 2;
2732	var QUARTER_PI = PI / 4;
2733	var TWO_THIRDS_PI = PI * 2 / 3;
2734	
2735	/**
2736	 * @namespace Chart.helpers.canvas
2737	 */
2738	var exports$1 = {
2739		/**
2740		 * Clears the entire canvas associated to the given `chart`.
2741		 * @param {Chart} chart - The chart for which to clear the canvas.
2742		 */
2743		clear: function(chart) {
2744			chart.ctx.clearRect(0, 0, chart.width, chart.height);
2745		},
2746	
2747		/**
2748		 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
2749		 * given size (width, height) and the same `radius` for all corners.
2750		 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
2751		 * @param {number} x - The x axis of the coordinate for the rectangle starting point.
2752		 * @param {number} y - The y axis of the coordinate for the rectangle starting point.
2753		 * @param {number} width - The rectangle's width.
2754		 * @param {number} height - The rectangle's height.
2755		 * @param {number} radius - The rounded amount (in pixels) for the four corners.
2756		 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
2757		 */
2758		roundedRect: function(ctx, x, y, width, height, radius) {
2759			if (radius) {
2760				var r = Math.min(radius, height / 2, width / 2);
2761				var left = x + r;
2762				var top = y + r;
2763				var right = x + width - r;
2764				var bottom = y + height - r;
2765	
2766				ctx.moveTo(x, top);
2767				if (left < right && top < bottom) {
2768					ctx.arc(left, top, r, -PI, -HALF_PI);
2769					ctx.arc(right, top, r, -HALF_PI, 0);
2770					ctx.arc(right, bottom, r, 0, HALF_PI);
2771					ctx.arc(left, bottom, r, HALF_PI, PI);
2772				} else if (left < right) {
2773					ctx.moveTo(left, y);
2774					ctx.arc(right, top, r, -HALF_PI, HALF_PI);
2775					ctx.arc(left, top, r, HALF_PI, PI + HALF_PI);
2776				} else if (top < bottom) {
2777					ctx.arc(left, top, r, -PI, 0);
2778					ctx.arc(left, bottom, r, 0, PI);
2779				} else {
2780					ctx.arc(left, top, r, -PI, PI);
2781				}
2782				ctx.closePath();
2783				ctx.moveTo(x, y);
2784			} else {
2785				ctx.rect(x, y, width, height);
2786			}
2787		},
2788	
2789		drawPoint: function(ctx, style, radius, x, y, rotation) {
2790			var type, xOffset, yOffset, size, cornerRadius;
2791			var rad = (rotation || 0) * RAD_PER_DEG;
2792	
2793			if (style && typeof style === 'object') {
2794				type = style.toString();
2795				if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
2796					ctx.save();
2797					ctx.translate(x, y);
2798					ctx.rotate(rad);
2799					ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
2800					ctx.restore();
2801					return;
2802				}
2803			}
2804	
2805			if (isNaN(radius) || radius <= 0) {
2806				return;
2807			}
2808	
2809			ctx.beginPath();
2810	
2811			switch (style) {
2812			// Default includes circle
2813			default:
2814				ctx.arc(x, y, radius, 0, DOUBLE_PI);
2815				ctx.closePath();
2816				break;
2817			case 'triangle':
2818				ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
2819				rad += TWO_THIRDS_PI;
2820				ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
2821				rad += TWO_THIRDS_PI;
2822				ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
2823				ctx.closePath();
2824				break;
2825			case 'rectRounded':
2826				// NOTE: the rounded rect implementation changed to use `arc` instead of
2827				// `quadraticCurveTo` since it generates better results when rect is
2828				// almost a circle. 0.516 (instead of 0.5) produces results with visually
2829				// closer proportion to the previous impl and it is inscribed in the
2830				// circle with `radius`. For more details, see the following PRs:
2831				// https://github.com/chartjs/Chart.js/issues/5597
2832				// https://github.com/chartjs/Chart.js/issues/5858
2833				cornerRadius = radius * 0.516;
2834				size = radius - cornerRadius;
2835				xOffset = Math.cos(rad + QUARTER_PI) * size;
2836				yOffset = Math.sin(rad + QUARTER_PI) * size;
2837				ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
2838				ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
2839				ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
2840				ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
2841				ctx.closePath();
2842				break;
2843			case 'rect':
2844				if (!rotation) {
2845					size = Math.SQRT1_2 * radius;
2846					ctx.rect(x - size, y - size, 2 * size, 2 * size);
2847					break;
2848				}
2849				rad += QUARTER_PI;
2850				/* falls through */
2851			case 'rectRot':
2852				xOffset = Math.cos(rad) * radius;
2853				yOffset = Math.sin(rad) * radius;
2854				ctx.moveTo(x - xOffset, y - yOffset);
2855				ctx.lineTo(x + yOffset, y - xOffset);
2856				ctx.lineTo(x + xOffset, y + yOffset);
2857				ctx.lineTo(x - yOffset, y + xOffset);
2858				ctx.closePath();
2859				break;
2860			case 'crossRot':
2861				rad += QUARTER_PI;
2862				/* falls through */
2863			case 'cross':
2864				xOffset = Math.cos(rad) * radius;
2865				yOffset = Math.sin(rad) * radius;
2866				ctx.moveTo(x - xOffset, y - yOffset);
2867				ctx.lineTo(x + xOffset, y + yOffset);
2868				ctx.moveTo(x + yOffset, y - xOffset);
2869				ctx.lineTo(x - yOffset, y + xOffset);
2870				break;
2871			case 'star':
2872				xOffset = Math.cos(rad) * radius;
2873				yOffset = Math.sin(rad) * radius;
2874				ctx.moveTo(x - xOffset, y - yOffset);
2875				ctx.lineTo(x + xOffset, y + yOffset);
2876				ctx.moveTo(x + yOffset, y - xOffset);
2877				ctx.lineTo(x - yOffset, y + xOffset);
2878				rad += QUARTER_PI;
2879				xOffset = Math.cos(rad) * radius;
2880				yOffset = Math.sin(rad) * radius;
2881				ctx.moveTo(x - xOffset, y - yOffset);
2882				ctx.lineTo(x + xOffset, y + yOffset);
2883				ctx.moveTo(x + yOffset, y - xOffset);
2884				ctx.lineTo(x - yOffset, y + xOffset);
2885				break;
2886			case 'line':
2887				xOffset = Math.cos(rad) * radius;
2888				yOffset = Math.sin(rad) * radius;
2889				ctx.moveTo(x - xOffset, y - yOffset);
2890				ctx.lineTo(x + xOffset, y + yOffset);
2891				break;
2892			case 'dash':
2893				ctx.moveTo(x, y);
2894				ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
2895				break;
2896			}
2897	
2898			ctx.fill();
2899			ctx.stroke();
2900		},
2901	
2902		/**
2903		 * Returns true if the point is inside the rectangle
2904		 * @param {object} point - The point to test
2905		 * @param {object} area - The rectangle
2906		 * @returns {boolean}
2907		 * @private
2908		 */
2909		_isPointInArea: function(point, area) {
2910			var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
2911	
2912			return point.x > area.left - epsilon && point.x < area.right + epsilon &&
2913				point.y > area.top - epsilon && point.y < area.bottom + epsilon;
2914		},
2915	
2916		clipArea: function(ctx, area) {
2917			ctx.save();
2918			ctx.beginPath();
2919			ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
2920			ctx.clip();
2921		},
2922	
2923		unclipArea: function(ctx) {
2924			ctx.restore();
2925		},
2926	
2927		lineTo: function(ctx, previous, target, flip) {
2928			var stepped = target.steppedLine;
2929			if (stepped) {
2930				if (stepped === 'middle') {
2931					var midpoint = (previous.x + target.x) / 2.0;
2932					ctx.lineTo(midpoint, flip ? target.y : previous.y);
2933					ctx.lineTo(midpoint, flip ? previous.y : target.y);
2934				} else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {
2935					ctx.lineTo(previous.x, target.y);
2936				} else {
2937					ctx.lineTo(target.x, previous.y);
2938				}
2939				ctx.lineTo(target.x, target.y);
2940				return;
2941			}
2942	
2943			if (!target.tension) {
2944				ctx.lineTo(target.x, target.y);
2945				return;
2946			}
2947	
2948			ctx.bezierCurveTo(
2949				flip ? previous.controlPointPreviousX : previous.controlPointNextX,
2950				flip ? previous.controlPointPreviousY : previous.controlPointNextY,
2951				flip ? target.controlPointNextX : target.controlPointPreviousX,
2952				flip ? target.controlPointNextY : target.controlPointPreviousY,
2953				target.x,
2954				target.y);
2955		}
2956	};
2957	
2958	var helpers_canvas = exports$1;
2959	
2960	// DEPRECATIONS
2961	
2962	/**
2963	 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
2964	 * @namespace Chart.helpers.clear
2965	 * @deprecated since version 2.7.0
2966	 * @todo remove at version 3
2967	 * @private
2968	 */
2969	helpers_core.clear = exports$1.clear;
2970	
2971	/**
2972	 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
2973	 * @namespace Chart.helpers.drawRoundedRectangle
2974	 * @deprecated since version 2.7.0
2975	 * @todo remove at version 3
2976	 * @private
2977	 */
2978	helpers_core.drawRoundedRectangle = function(ctx) {
2979		ctx.beginPath();
2980		exports$1.roundedRect.apply(exports$1, arguments);
2981	};
2982	
2983	var defaults = {
2984		/**
2985		 * @private
2986		 */
2987		_set: function(scope, values) {
2988			return helpers_core.merge(this[scope] || (this[scope] = {}), values);
2989		}
2990	};
2991	
2992	// TODO(v3): remove 'global' from namespace.  all default are global and
2993	// there's inconsistency around which options are under 'global'
2994	defaults._set('global', {
2995		defaultColor: 'rgba(0,0,0,0.1)',
2996		defaultFontColor: '#666',
2997		defaultFontFamily: "sans-serif",
2998		defaultFontSize: 12,
2999		defaultFontStyle: 'normal',
3000		defaultLineHeight: 1.2,
3001		showLines: true
3002	});
3003	
3004	var core_defaults = defaults;
3005	
3006	var valueOrDefault = helpers_core.valueOrDefault;
3007	
3008	/**
3009	 * Converts the given font object into a CSS font string.
3010	 * @param {object} font - A font object.
3011	 * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
3012	 * @private
3013	 */
3014	function toFontString(font) {
3015		if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) {
3016			return null;
3017		}
3018	
3019		return (font.style ? font.style + ' ' : '')
3020			+ (font.weight ? font.weight + ' ' : '')
3021			+ font.size + 'px '
3022			+ font.family;
3023	}
3024	
3025	/**
3026	 * @alias Chart.helpers.options
3027	 * @namespace
3028	 */
3029	var helpers_options = {
3030		/**
3031		 * Converts the given line height `value` in pixels for a specific font `size`.
3032		 * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
3033		 * @param {number} size - The font size (in pixels) used to resolve relative `value`.
3034		 * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).
3035		 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
3036		 * @since 2.7.0
3037		 */
3038		toLineHeight: function(value, size) {
3039			var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
3040			if (!matches || matches[1] === 'normal') {
3041				return size * 1.2;
3042			}
3043	
3044			value = +matches[2];
3045	
3046			switch (matches[3]) {
3047			case 'px':
3048				return value;
3049			case '%':
3050				value /= 100;
3051				break;
3052			}
3053	
3054			return size * value;
3055		},
3056	
3057		/**
3058		 * Converts the given value into a padding object with pre-computed width/height.
3059		 * @param {number|object} value - If a number, set the value to all TRBL component,
3060		 *  else, if and object, use defined properties and sets undefined ones to 0.
3061		 * @returns {object} The padding values (top, right, bottom, left, width, height)
3062		 * @since 2.7.0
3063		 */
3064		toPadding: function(value) {
3065			var t, r, b, l;
3066	
3067			if (helpers_core.isObject(value)) {
3068				t = +value.top || 0;
3069				r = +value.right || 0;
3070				b = +value.bottom || 0;
3071				l = +value.left || 0;
3072			} else {
3073				t = r = b = l = +value || 0;
3074			}
3075	
3076			return {
3077				top: t,
3078				right: r,
3079				bottom: b,
3080				left: l,
3081				height: t + b,
3082				width: l + r
3083			};
3084		},
3085	
3086		/**
3087		 * Parses font options and returns the font object.
3088		 * @param {object} options - A object that contains font options to be parsed.
3089		 * @return {object} The font object.
3090		 * @todo Support font.* options and renamed to toFont().
3091		 * @private
3092		 */
3093		_parseFont: function(options) {
3094			var globalDefaults = core_defaults.global;
3095			var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
3096			var font = {
3097				family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),
3098				lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),
3099				size: size,
3100				style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),
3101				weight: null,
3102				string: ''
3103			};
3104	
3105			font.string = toFontString(font);
3106			return font;
3107		},
3108	
3109		/**
3110		 * Evaluates the given `inputs` sequentially and returns the first defined value.
3111		 * @param {Array} inputs - An array of values, falling back to the last value.
3112		 * @param {object} [context] - If defined and the current value is a function, the value
3113		 * is called with `context` as first argument and the result becomes the new input.
3114		 * @param {number} [index] - If defined and the current value is an array, the value
3115		 * at `index` become the new input.
3116		 * @param {object} [info] - object to return information about resolution in
3117		 * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable.
3118		 * @since 2.7.0
3119		 */
3120		resolve: function(inputs, context, index, info) {
3121			var cacheable = true;
3122			var i, ilen, value;
3123	
3124			for (i = 0, ilen = inputs.length; i < ilen; ++i) {
3125				value = inputs[i];
3126				if (value === undefined) {
3127					continue;
3128				}
3129				if (context !== undefined && typeof value === 'function') {
3130					value = value(context);
3131					cacheable = false;
3132				}
3133				if (index !== undefined && helpers_core.isArray(value)) {
3134					value = value[index];
3135					cacheable = false;
3136				}
3137				if (value !== undefined) {
3138					if (info && !cacheable) {
3139						info.cacheable = false;
3140					}
3141					return value;
3142				}
3143			}
3144		}
3145	};
3146	
3147	/**
3148	 * @alias Chart.helpers.math
3149	 * @namespace
3150	 */
3151	var exports$2 = {
3152		/**
3153		 * Returns an array of factors sorted from 1 to sqrt(value)
3154		 * @private
3155		 */
3156		_factorize: function(value) {
3157			var result = [];
3158			var sqrt = Math.sqrt(value);
3159			var i;
3160	
3161			for (i = 1; i < sqrt; i++) {
3162				if (value % i === 0) {
3163					result.push(i);
3164					result.push(value / i);
3165				}
3166			}
3167			if (sqrt === (sqrt | 0)) { // if value is a square number
3168				result.push(sqrt);
3169			}
3170	
3171			result.sort(function(a, b) {
3172				return a - b;
3173			}).pop();
3174			return result;
3175		},
3176	
3177		log10: Math.log10 || function(x) {
3178			var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
3179			// Check for whole powers of 10,
3180			// which due to floating point rounding error should be corrected.
3181			var powerOf10 = Math.round(exponent);
3182			var isPowerOf10 = x === Math.pow(10, powerOf10);
3183	
3184			return isPowerOf10 ? powerOf10 : exponent;
3185		}
3186	};
3187	
3188	var helpers_math = exports$2;
3189	
3190	// DEPRECATIONS
3191	
3192	/**
3193	 * Provided for backward compatibility, use Chart.helpers.math.log10 instead.
3194	 * @namespace Chart.helpers.log10
3195	 * @deprecated since version 2.9.0
3196	 * @todo remove at version 3
3197	 * @private
3198	 */
3199	helpers_core.log10 = exports$2.log10;
3200	
3201	var getRtlAdapter = function(rectX, width) {
3202		return {
3203			x: function(x) {
3204				return rectX + rectX + width - x;
3205			},
3206			setWidth: function(w) {
3207				width = w;
3208			},
3209			textAlign: function(align) {
3210				if (align === 'center') {
3211					return align;
3212				}
3213				return align === 'right' ? 'left' : 'right';
3214			},
3215			xPlus: function(x, value) {
3216				return x - value;
3217			},
3218			leftForLtr: function(x, itemWidth) {
3219				return x - itemWidth;
3220			},
3221		};
3222	};
3223	
3224	var getLtrAdapter = function() {
3225		return {
3226			x: function(x) {
3227				return x;
3228			},
3229			setWidth: function(w) { // eslint-disable-line no-unused-vars
3230			},
3231			textAlign: function(align) {
3232				return align;
3233			},
3234			xPlus: function(x, value) {
3235				return x + value;
3236			},
3237			leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars
3238				return x;
3239			},
3240		};
3241	};
3242	
3243	var getAdapter = function(rtl, rectX, width) {
3244		return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter();
3245	};
3246	
3247	var overrideTextDirection = function(ctx, direction) {
3248		var style, original;
3249		if (direction === 'ltr' || direction === 'rtl') {
3250			style = ctx.canvas.style;
3251			original = [
3252				style.getPropertyValue('direction'),
3253				style.getPropertyPriority('direction'),
3254			];
3255	
3256			style.setProperty('direction', direction, 'important');
3257			ctx.prevTextDirection = original;
3258		}
3259	};
3260	
3261	var restoreTextDirection = function(ctx) {
3262		var original = ctx.prevTextDirection;
3263		if (original !== undefined) {
3264			delete ctx.prevTextDirection;
3265			ctx.canvas.style.setProperty('direction', original[0], original[1]);
3266		}
3267	};
3268	
3269	var helpers_rtl = {
3270		getRtlAdapter: getAdapter,
3271		overrideTextDirection: overrideTextDirection,
3272		restoreTextDirection: restoreTextDirection,
3273	};
3274	
3275	var helpers$1 = helpers_core;
3276	var easing = helpers_easing;
3277	var canvas = helpers_canvas;
3278	var options = helpers_options;
3279	var math = helpers_math;
3280	var rtl = helpers_rtl;
3281	helpers$1.easing = easing;
3282	helpers$1.canvas = canvas;
3283	helpers$1.options = options;
3284	helpers$1.math = math;
3285	helpers$1.rtl = rtl;
3286	
3287	function interpolate(start, view, model, ease) {
3288		var keys = Object.keys(model);
3289		var i, ilen, key, actual, origin, target, type, c0, c1;
3290	
3291		for (i = 0, ilen = keys.length; i < ilen; ++i) {
3292			key = keys[i];
3293	
3294			target = model[key];
3295	
3296			// if a value is added to the model after pivot() has been called, the view
3297			// doesn't contain it, so let's initialize the view to the target value.
3298			if (!view.hasOwnProperty(key)) {
3299				view[key] = target;
3300			}
3301	
3302			actual = view[key];
3303	
3304			if (actual === target || key[0] === '_') {
3305				continue;
3306			}
3307	
3308			if (!start.hasOwnProperty(key)) {
3309				start[key] = actual;
3310			}
3311	
3312			origin = start[key];
3313	
3314			type = typeof target;
3315	
3316			if (type === typeof origin) {
3317				if (type === 'string') {
3318					c0 = chartjsColor(origin);
3319					if (c0.valid) {
3320						c1 = chartjsColor(target);
3321						if (c1.valid) {
3322							view[key] = c1.mix(c0, ease).rgbString();
3323							continue;
3324						}
3325					}
3326				} else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) {
3327					view[key] = origin + (target - origin) * ease;
3328					continue;
3329				}
3330			}
3331	
3332			view[key] = target;
3333		}
3334	}
3335	
3336	var Element = function(configuration) {
3337		helpers$1.extend(this, configuration);
3338		this.initialize.apply(this, arguments);
3339	};
3340	
3341	helpers$1.extend(Element.prototype, {
3342		_type: undefined,
3343	
3344		initialize: function() {
3345			this.hidden = false;
3346		},
3347	
3348		pivot: function() {
3349			var me = this;
3350			if (!me._view) {
3351				me._view = helpers$1.extend({}, me._model);
3352			}
3353			me._start = {};
3354			return me;
3355		},
3356	
3357		transition: function(ease) {
3358			var me = this;
3359			var model = me._model;
3360			var start = me._start;
3361			var view = me._view;
3362	
3363			// No animation -> No Transition
3364			if (!model || ease === 1) {
3365				me._view = helpers$1.extend({}, model);
3366				me._start = null;
3367				return me;
3368			}
3369	
3370			if (!view) {
3371				view = me._view = {};
3372			}
3373	
3374			if (!start) {
3375				start = me._start = {};
3376			}
3377	
3378			interpolate(start, view, model, ease);
3379	
3380			return me;
3381		},
3382	
3383		tooltipPosition: function() {
3384			return {
3385				x: this._model.x,
3386				y: this._model.y
3387			};
3388		},
3389	
3390		hasValue: function() {
3391			return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y);
3392		}
3393	});
3394	
3395	Element.extend = helpers$1.inherits;
3396	
3397	var core_element = Element;
3398	
3399	var exports$3 = core_element.extend({
3400		chart: null, // the animation associated chart instance
3401		currentStep: 0, // the current animation step
3402		numSteps: 60, // default number of steps
3403		easing: '', // the easing to use for this animation
3404		render: null, // render function used by the animation service
3405	
3406		onAnimationProgress: null, // user specified callback to fire on each step of the animation
3407		onAnimationComplete: null, // user specified callback to fire when the animation finishes
3408	});
3409	
3410	var core_animation = exports$3;
3411	
3412	// DEPRECATIONS
3413	
3414	/**
3415	 * Provided for backward compatibility, use Chart.Animation instead
3416	 * @prop Chart.Animation#animationObject
3417	 * @deprecated since version 2.6.0
3418	 * @todo remove at version 3
3419	 */
3420	Object.defineProperty(exports$3.prototype, 'animationObject', {
3421		get: function() {
3422			return this;
3423		}
3424	});
3425	
3426	/**
3427	 * Provided for backward compatibility, use Chart.Animation#chart instead
3428	 * @prop Chart.Animation#chartInstance
3429	 * @deprecated since version 2.6.0
3430	 * @todo remove at version 3
3431	 */
3432	Object.defineProperty(exports$3.prototype, 'chartInstance', {
3433		get: function() {
3434			return this.chart;
3435		},
3436		set: function(value) {
3437			this.chart = value;
3438		}
3439	});
3440	
3441	core_defaults._set('global', {
3442		animation: {
3443			duration: 1000,
3444			easing: 'easeOutQuart',
3445			onProgress: helpers$1.noop,
3446			onComplete: helpers$1.noop
3447		}
3448	});
3449	
3450	var core_animations = {
3451		animations: [],
3452		request: null,
3453	
3454		/**
3455		 * @param {Chart} chart - The chart to animate.
3456		 * @param {Chart.Animation} animation - The animation that we will animate.
3457		 * @param {number} duration - The animation duration in ms.
3458		 * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
3459		 */
3460		addAnimation: function(chart, animation, duration, lazy) {
3461			var animations = this.animations;
3462			var i, ilen;
3463	
3464			animation.chart = chart;
3465			animation.startTime = Date.now();
3466			animation.duration = duration;
3467	
3468			if (!lazy) {
3469				chart.animating = true;
3470			}
3471	
3472			for (i = 0, ilen = animations.length; i < ilen; ++i) {
3473				if (animations[i].chart === chart) {
3474					animations[i] = animation;
3475					return;
3476				}
3477			}
3478	
3479			animations.push(animation);
3480	
3481			// If there are no animations queued, manually kickstart a digest, for lack of a better word
3482			if (animations.length === 1) {
3483				this.requestAnimationFrame();
3484			}
3485		},
3486	
3487		cancelAnimation: function(chart) {
3488			var index = helpers$1.findIndex(this.animations, function(animation) {
3489				return animation.chart === chart;
3490			});
3491	
3492			if (index !== -1) {
3493				this.animations.splice(index, 1);
3494				chart.animating = false;
3495			}
3496		},
3497	
3498		requestAnimationFrame: function() {
3499			var me = this;
3500			if (me.request === null) {
3501				// Skip animation frame requests until the active one is executed.
3502				// This can happen when processing mouse events, e.g. 'mousemove'
3503				// and 'mouseout' events will trigger multiple renders.
3504				me.request = helpers$1.requestAnimFrame.call(window, function() {
3505					me.request = null;
3506					me.startDigest();
3507				});
3508			}
3509		},
3510	
3511		/**
3512		 * @private
3513		 */
3514		startDigest: function() {
3515			var me = this;
3516	
3517			me.advance();
3518	
3519			// Do we have more stuff to animate?
3520			if (me.animations.length > 0) {
3521				me.requestAnimationFrame();
3522			}
3523		},
3524	
3525		/**
3526		 * @private
3527		 */
3528		advance: function() {
3529			var animations = this.animations;
3530			var animation, chart, numSteps, nextStep;
3531			var i = 0;
3532	
3533			// 1 animation per chart, so we are looping charts here
3534			while (i < animations.length) {
3535				animation = animations[i];
3536				chart = animation.chart;
3537				numSteps = animation.numSteps;
3538	
3539				// Make sure that currentStep starts at 1
3540				// https://github.com/chartjs/Chart.js/issues/6104
3541				nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1;
3542				animation.currentStep = Math.min(nextStep, numSteps);
3543	
3544				helpers$1.callback(animation.render, [chart, animation], chart);
3545				helpers$1.callback(animation.onAnimationProgress, [animation], chart);
3546	
3547				if (animation.currentStep >= numSteps) {
3548					helpers$1.callback(animation.onAnimationComplete, [animation], chart);
3549					chart.animating = false;
3550					animations.splice(i, 1);
3551				} else {
3552					++i;
3553				}
3554			}
3555		}
3556	};
3557	
3558	var resolve = helpers$1.options.resolve;
3559	
3560	var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
3561	
3562	/**
3563	 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
3564	 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
3565	 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
3566	 */
3567	function listenArrayEvents(array, listener) {
3568		if (array._chartjs) {
3569			array._chartjs.listeners.push(listener);
3570			return;
3571		}
3572	
3573		Object.defineProperty(array, '_chartjs', {
3574			configurable: true,
3575			enumerable: false,
3576			value: {
3577				listeners: [listener]
3578			}
3579		});
3580	
3581		arrayEvents.forEach(function(key) {
3582			var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
3583			var base = array[key];
3584	
3585			Object.defineProperty(array, key, {
3586				configurable: true,
3587				enumerable: false,
3588				value: function() {
3589					var args = Array.prototype.slice.call(arguments);
3590					var res = base.apply(this, args);
3591	
3592					helpers$1.each(array._chartjs.listeners, function(object) {
3593						if (typeof object[method] === 'function') {
3594							object[method].apply(object, args);
3595						}
3596					});
3597	
3598					return res;
3599				}
3600			});
3601		});
3602	}
3603	
3604	/**
3605	 * Removes the given array event listener and cleanup extra attached properties (such as
3606	 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
3607	 */
3608	function unlistenArrayEvents(array, listener) {
3609		var stub = array._chartjs;
3610		if (!stub) {
3611			return;
3612		}
3613	
3614		var listeners = stub.listeners;
3615		var index = listeners.indexOf(listener);
3616		if (index !== -1) {
3617			listeners.splice(index, 1);
3618		}
3619	
3620		if (listeners.length > 0) {
3621			return;
3622		}
3623	
3624		arrayEvents.forEach(function(key) {
3625			delete array[key];
3626		});
3627	
3628		delete array._chartjs;
3629	}
3630	
3631	// Base class for all dataset controllers (line, bar, etc)
3632	var DatasetController = function(chart, datasetIndex) {
3633		this.initialize(chart, datasetIndex);
3634	};
3635	
3636	helpers$1.extend(DatasetController.prototype, {
3637	
3638		/**
3639		 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
3640		 * @type {Chart.core.element}
3641		 */
3642		datasetElementType: null,
3643	
3644		/**
3645		 * Element type used to generate a meta data (e.g. Chart.element.Point).
3646		 * @type {Chart.core.element}
3647		 */
3648		dataElementType: null,
3649	
3650		/**
3651		 * Dataset element option keys to be resolved in _resolveDatasetElementOptions.
3652		 * A derived controller may override this to resolve controller-specific options.
3653		 * The keys defined here are for backward compatibility for legend styles.
3654		 * @private
3655		 */
3656		_datasetElementOptions: [
3657			'backgroundColor',
3658			'borderCapStyle',
3659			'borderColor',
3660			'borderDash',
3661			'borderDashOffset',
3662			'borderJoinStyle',
3663			'borderWidth'
3664		],
3665	
3666		/**
3667		 * Data element option keys to be resolved in _resolveDataElementOptions.
3668		 * A derived controller may override this to resolve controller-specific options.
3669		 * The keys defined here are for backward compatibility for legend styles.
3670		 * @private
3671		 */
3672		_dataElementOptions: [
3673			'backgroundColor',
3674			'borderColor',
3675			'borderWidth',
3676			'pointStyle'
3677		],
3678	
3679		initialize: function(chart, datasetIndex) {
3680			var me = this;
3681			me.chart = chart;
3682			me.index = datasetIndex;
3683			me.linkScales();
3684			me.addElements();
3685			me._type = me.getMeta().type;
3686		},
3687	
3688		updateIndex: function(datasetIndex) {
3689			this.index = datasetIndex;
3690		},
3691	
3692		linkScales: function() {
3693			var me = this;
3694			var meta = me.getMeta();
3695			var chart = me.chart;
3696			var scales = chart.scales;
3697			var dataset = me.getDataset();
3698			var scalesOpts = chart.options.scales;
3699	
3700			if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) {
3701				meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id;
3702			}
3703			if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) {
3704				meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id;
3705			}
3706		},
3707	
3708		getDataset: function() {
3709			return this.chart.data.datasets[this.index];
3710		},
3711	
3712		getMeta: function() {
3713			return this.chart.getDatasetMeta(this.index);
3714		},
3715	
3716		getScaleForId: function(scaleID) {
3717			return this.chart.scales[scaleID];
3718		},
3719	
3720		/**
3721		 * @private
3722		 */
3723		_getValueScaleId: function() {
3724			return this.getMeta().yAxisID;
3725		},
3726	
3727		/**
3728		 * @private
3729		 */
3730		_getIndexScaleId: function() {
3731			return this.getMeta().xAxisID;
3732		},
3733	
3734		/**
3735		 * @private
3736		 */
3737		_getValueScale: function() {
3738			return this.getScaleForId(this._getValueScaleId());
3739		},
3740	
3741		/**
3742		 * @private
3743		 */
3744		_getIndexScale: function() {
3745			return this.getScaleForId(this._getIndexScaleId());
3746		},
3747	
3748		reset: function() {
3749			this._update(true);
3750		},
3751	
3752		/**
3753		 * @private
3754		 */
3755		destroy: function() {
3756			if (this._data) {
3757				unlistenArrayEvents(this._data, this);
3758			}
3759		},
3760	
3761		createMetaDataset: function() {
3762			var me = this;
3763			var type = me.datasetElementType;
3764			return type && new type({
3765				_chart: me.chart,
3766				_datasetIndex: me.index
3767			});
3768		},
3769	
3770		createMetaData: function(index) {
3771			var me = this;
3772			var type = me.dataElementType;
3773			return type && new type({
3774				_chart: me.chart,
3775				_datasetIndex: me.index,
3776				_index: index
3777			});
3778		},
3779	
3780		addElements: function() {
3781			var me = this;
3782			var meta = me.getMeta();
3783			var data = me.getDataset().data || [];
3784			var metaData = meta.data;
3785			var i, ilen;
3786	
3787			for (i = 0, ilen = data.length; i < ilen; ++i) {
3788				metaData[i] = metaData[i] || me.createMetaData(i);
3789			}
3790	
3791			meta.dataset = meta.dataset || me.createMetaDataset();
3792		},
3793	
3794		addElementAndReset: function(index) {
3795			var element = this.createMetaData(index);
3796			this.getMeta().data.splice(index, 0, element);
3797			this.updateElement(element, index, true);
3798		},
3799	
3800		buildOrUpdateElements: function() {
3801			var me = this;
3802			var dataset = me.getDataset();
3803			var data = dataset.data || (dataset.data = []);
3804	
3805			// In order to correctly handle data addition/deletion animation (an thus simulate
3806			// real-time charts), we need to monitor these data modifications and synchronize
3807			// the internal meta data accordingly.
3808			if (me._data !== data) {
3809				if (me._data) {
3810					// This case happens when the user replaced the data array instance.
3811					unlistenArrayEvents(me._data, me);
3812				}
3813	
3814				if (data && Object.isExtensible(data)) {
3815					listenArrayEvents(data, me);
3816				}
3817				me._data = data;
3818			}
3819	
3820			// Re-sync meta data in case the user replaced the data array or if we missed
3821			// any updates and so make sure that we handle number of datapoints changing.
3822			me.resyncElements();
3823		},
3824	
3825		/**
3826		 * Returns the merged user-supplied and default dataset-level options
3827		 * @private
3828		 */
3829		_configure: function() {
3830			var me = this;
3831			me._config = helpers$1.merge(Object.create(null), [
3832				me.chart.options.datasets[me._type],
3833				me.getDataset(),
3834			], {
3835				merger: function(key, target, source) {
3836					if (key !== '_meta' && key !== 'data') {
3837						helpers$1._merger(key, target, source);
3838					}
3839				}
3840			});
3841		},
3842	
3843		_update: function(reset) {
3844			var me = this;
3845			me._configure();
3846			me._cachedDataOpts = null;
3847			me.update(reset);
3848		},
3849	
3850		update: helpers$1.noop,
3851	
3852		transition: function(easingValue) {
3853			var meta = this.getMeta();
3854			var elements = meta.data || [];
3855			var ilen = elements.length;
3856			var i = 0;
3857	
3858			for (; i < ilen; ++i) {
3859				elements[i].transition(easingValue);
3860			}
3861	
3862			if (meta.dataset) {
3863				meta.dataset.transition(easingValue);
3864			}
3865		},
3866	
3867		draw: function() {
3868			var meta = this.getMeta();
3869			var elements = meta.data || [];
3870			var ilen = elements.length;
3871			var i = 0;
3872	
3873			if (meta.dataset) {
3874				meta.dataset.draw();
3875			}
3876	
3877			for (; i < ilen; ++i) {
3878				elements[i].draw();
3879			}
3880		},
3881	
3882		/**
3883		 * Returns a set of predefined style properties that should be used to represent the dataset
3884		 * or the data if the index is specified
3885		 * @param {number} index - data index
3886		 * @return {IStyleInterface} style object
3887		 */
3888		getStyle: function(index) {
3889			var me = this;
3890			var meta = me.getMeta();
3891			var dataset = meta.dataset;
3892			var style;
3893	
3894			me._configure();
3895			if (dataset && index === undefined) {
3896				style = me._resolveDatasetElementOptions(dataset || {});
3897			} else {
3898				index = index || 0;
3899				style = me._resolveDataElementOptions(meta.data[index] || {}, index);
3900			}
3901	
3902			if (style.fill === false || style.fill === null) {
3903				style.backgroundColor = style.borderColor;
3904			}
3905	
3906			return style;
3907		},
3908	
3909		/**
3910		 * @private
3911		 */
3912		_resolveDatasetElementOptions: function(element, hover) {
3913			var me = this;
3914			var chart = me.chart;
3915			var datasetOpts = me._config;
3916			var custom = element.custom || {};
3917			var options = chart.options.elements[me.datasetElementType.prototype._type] || {};
3918			var elementOptions = me._datasetElementOptions;
3919			var values = {};
3920			var i, ilen, key, readKey;
3921	
3922			// Scriptable options
3923			var context = {
3924				chart: chart,
3925				dataset: me.getDataset(),
3926				datasetIndex: me.index,
3927				hover: hover
3928			};
3929	
3930			for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
3931				key = elementOptions[i];
3932				readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
3933				values[key] = resolve([
3934					custom[readKey],
3935					datasetOpts[readKey],
3936					options[readKey]
3937				], context);
3938			}
3939	
3940			return values;
3941		},
3942	
3943		/**
3944		 * @private
3945		 */
3946		_resolveDataElementOptions: function(element, index) {
3947			var me = this;
3948			var custom = element && element.custom;
3949			var cached = me._cachedDataOpts;
3950			if (cached && !custom) {
3951				return cached;
3952			}
3953			var chart = me.chart;
3954			var datasetOpts = me._config;
3955			var options = chart.options.elements[me.dataElementType.prototype._type] || {};
3956			var elementOptions = me._dataElementOptions;
3957			var values = {};
3958	
3959			// Scriptable options
3960			var context = {
3961				chart: chart,
3962				dataIndex: index,
3963				dataset: me.getDataset(),
3964				datasetIndex: me.index
3965			};
3966	
3967			// `resolve` sets cacheable to `false` if any option is indexed or scripted
3968			var info = {cacheable: !custom};
3969	
3970			var keys, i, ilen, key;
3971	
3972			custom = custom || {};
3973	
3974			if (helpers$1.isArray(elementOptions)) {
3975				for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
3976					key = elementOptions[i];
3977					values[key] = resolve([
3978						custom[key],
3979						datasetOpts[key],
3980						options[key]
3981					], context, index, info);
3982				}
3983			} else {
3984				keys = Object.keys(elementOptions);
3985				for (i = 0, ilen = keys.length; i < ilen; ++i) {
3986					key = keys[i];
3987					values[key] = resolve([
3988						custom[key],
3989						datasetOpts[elementOptions[key]],
3990						datasetOpts[key],
3991						options[key]
3992					], context, index, info);
3993				}
3994			}
3995	
3996			if (info.cacheable) {
3997				me._cachedDataOpts = Object.freeze(values);
3998			}
3999	
4000			return values;
4001		},
4002	
4003		removeHoverStyle: function(element) {
4004			helpers$1.merge(element._model, element.$previousStyle || {});
4005			delete element.$previousStyle;
4006		},
4007	
4008		setHoverStyle: function(element) {
4009			var dataset = this.chart.data.datasets[element._datasetIndex];
4010			var index = element._index;
4011			var custom = element.custom || {};
4012			var model = element._model;
4013			var getHoverColor = helpers$1.getHoverColor;
4014	
4015			element.$previousStyle = {
4016				backgroundColor: model.backgroundColor,
4017				borderColor: model.borderColor,
4018				borderWidth: model.borderWidth
4019			};
4020	
4021			model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index);
4022			model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index);
4023			model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index);
4024		},
4025	
4026		/**
4027		 * @private
4028		 */
4029		_removeDatasetHoverStyle: function() {
4030			var element = this.getMeta().dataset;
4031	
4032			if (element) {
4033				this.removeHoverStyle(element);
4034			}
4035		},
4036	
4037		/**
4038		 * @private
4039		 */
4040		_setDatasetHoverStyle: function() {
4041			var element = this.getMeta().dataset;
4042			var prev = {};
4043			var i, ilen, key, keys, hoverOptions, model;
4044	
4045			if (!element) {
4046				return;
4047			}
4048	
4049			model = element._model;
4050			hoverOptions = this._resolveDatasetElementOptions(element, true);
4051	
4052			keys = Object.keys(hoverOptions);
4053			for (i = 0, ilen = keys.length; i < ilen; ++i) {
4054				key = keys[i];
4055				prev[key] = model[key];
4056				model[key] = hoverOptions[key];
4057			}
4058	
4059			element.$previousStyle = prev;
4060		},
4061	
4062		/**
4063		 * @private
4064		 */
4065		resyncElements: function() {
4066			var me = this;
4067			var meta = me.getMeta();
4068			var data = me.getDataset().data;
4069			var numMeta = meta.data.length;
4070			var numData = data.length;
4071	
4072			if (numData < numMeta) {
4073				meta.data.splice(numData, numMeta - numData);
4074			} else if (numData > numMeta) {
4075				me.insertElements(numMeta, numData - numMeta);
4076			}
4077		},
4078	
4079		/**
4080		 * @private
4081		 */
4082		insertElements: function(start, count) {
4083			for (var i = 0; i < count; ++i) {
4084				this.addElementAndReset(start + i);
4085			}
4086		},
4087	
4088		/**
4089		 * @private
4090		 */
4091		onDataPush: function() {
4092			var count = arguments.length;
4093			this.insertElements(this.getDataset().data.length - count, count);
4094		},
4095	
4096		/**
4097		 * @private
4098		 */
4099		onDataPop: function() {
4100			this.getMeta().data.pop();
4101		},
4102	
4103		/**
4104		 * @private
4105		 */
4106		onDataShift: function() {
4107			this.getMeta().data.shift();
4108		},
4109	
4110		/**
4111		 * @private
4112		 */
4113		onDataSplice: function(start, count) {
4114			this.getMeta().data.splice(start, count);
4115			this.insertElements(start, arguments.length - 2);
4116		},
4117	
4118		/**
4119		 * @private
4120		 */
4121		onDataUnshift: function() {
4122			this.insertElements(0, arguments.length);
4123		}
4124	});
4125	
4126	DatasetController.extend = helpers$1.inherits;
4127	
4128	var core_datasetController = DatasetController;
4129	
4130	var TAU = Math.PI * 2;
4131	
4132	core_defaults._set('global', {
4133		elements: {
4134			arc: {
4135				backgroundColor: core_defaults.global.defaultColor,
4136				borderColor: '#fff',
4137				borderWidth: 2,
4138				borderAlign: 'center'
4139			}
4140		}
4141	});
4142	
4143	function clipArc(ctx, arc) {
4144		var startAngle = arc.startAngle;
4145		var endAngle = arc.endAngle;
4146		var pixelMargin = arc.pixelMargin;
4147		var angleMargin = pixelMargin / arc.outerRadius;
4148		var x = arc.x;
4149		var y = arc.y;
4150	
4151		// Draw an inner border by cliping the arc and drawing a double-width border
4152		// Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
4153		ctx.beginPath();
4154		ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin);
4155		if (arc.innerRadius > pixelMargin) {
4156			angleMargin = pixelMargin / arc.innerRadius;
4157			ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true);
4158		} else {
4159			ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2);
4160		}
4161		ctx.closePath();
4162		ctx.clip();
4163	}
4164	
4165	function drawFullCircleBorders(ctx, vm, arc, inner) {
4166		var endAngle = arc.endAngle;
4167		var i;
4168	
4169		if (inner) {
4170			arc.endAngle = arc.startAngle + TAU;
4171			clipArc(ctx, arc);
4172			arc.endAngle = endAngle;
4173			if (arc.endAngle === arc.startAngle && arc.fullCircles) {
4174				arc.endAngle += TAU;
4175				arc.fullCircles--;
4176			}
4177		}
4178	
4179		ctx.beginPath();
4180		ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true);
4181		for (i = 0; i < arc.fullCircles; ++i) {
4182			ctx.stroke();
4183		}
4184	
4185		ctx.beginPath();
4186		ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU);
4187		for (i = 0; i < arc.fullCircles; ++i) {
4188			ctx.stroke();
4189		}
4190	}
4191	
4192	function drawBorder(ctx, vm, arc) {
4193		var inner = vm.borderAlign === 'inner';
4194	
4195		if (inner) {
4196			ctx.lineWidth = vm.borderWidth * 2;
4197			ctx.lineJoin = 'round';
4198		} else {
4199			ctx.lineWidth = vm.borderWidth;
4200			ctx.lineJoin = 'bevel';
4201		}
4202	
4203		if (arc.fullCircles) {
4204			drawFullCircleBorders(ctx, vm, arc, inner);
4205		}
4206	
4207		if (inner) {
4208			clipArc(ctx, arc);
4209		}
4210	
4211		ctx.beginPath();
4212		ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle);
4213		ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
4214		ctx.closePath();
4215		ctx.stroke();
4216	}
4217	
4218	var element_arc = core_element.extend({
4219		_type: 'arc',
4220	
4221		inLabelRange: function(mouseX) {
4222			var vm = this._view;
4223	
4224			if (vm) {
4225				return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
4226			}
4227			return false;
4228		},
4229	
4230		inRange: function(chartX, chartY) {
4231			var vm = this._view;
4232	
4233			if (vm) {
4234				var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY});
4235				var angle = pointRelativePosition.angle;
4236				var distance = pointRelativePosition.distance;
4237	
4238				// Sanitise angle range
4239				var startAngle = vm.startAngle;
4240				var endAngle = vm.endAngle;
4241				while (endAngle < startAngle) {
4242					endAngle += TAU;
4243				}
4244				while (angle > endAngle) {
4245					angle -= TAU;
4246				}
4247				while (angle < startAngle) {
4248					angle += TAU;
4249				}
4250	
4251				// Check if within the range of the open/close angle
4252				var betweenAngles = (angle >= startAngle && angle <= endAngle);
4253				var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
4254	
4255				return (betweenAngles && withinRadius);
4256			}
4257			return false;
4258		},
4259	
4260		getCenterPoint: function() {
4261			var vm = this._view;
4262			var halfAngle = (vm.startAngle + vm.endAngle) / 2;
4263			var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
4264			return {
4265				x: vm.x + Math.cos(halfAngle) * halfRadius,
4266				y: vm.y + Math.sin(halfAngle) * halfRadius
4267			};
4268		},
4269	
4270		getArea: function() {
4271			var vm = this._view;
4272			return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
4273		},
4274	
4275		tooltipPosition: function() {
4276			var vm = this._view;
4277			var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
4278			var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
4279	
4280			return {
4281				x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
4282				y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
4283			};
4284		},
4285	
4286		draw: function() {
4287			var ctx = this._chart.ctx;
4288			var vm = this._view;
4289			var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
4290			var arc = {
4291				x: vm.x,
4292				y: vm.y,
4293				innerRadius: vm.innerRadius,
4294				outerRadius: Math.max(vm.outerRadius - pixelMargin, 0),
4295				pixelMargin: pixelMargin,
4296				startAngle: vm.startAngle,
4297				endAngle: vm.endAngle,
4298				fullCircles: Math.floor(vm.circumference / TAU)
4299			};
4300			var i;
4301	
4302			ctx.save();
4303	
4304			ctx.fillStyle = vm.backgroundColor;
4305			ctx.strokeStyle = vm.borderColor;
4306	
4307			if (arc.fullCircles) {
4308				arc.endAngle = arc.startAngle + TAU;
4309				ctx.beginPath();
4310				ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
4311				ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
4312				ctx.closePath();
4313				for (i = 0; i < arc.fullCircles; ++i) {
4314					ctx.fill();
4315				}
4316				arc.endAngle = arc.startAngle + vm.circumference % TAU;
4317			}
4318	
4319			ctx.beginPath();
4320			ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
4321			ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
4322			ctx.closePath();
4323			ctx.fill();
4324	
4325			if (vm.borderWidth) {
4326				drawBorder(ctx, vm, arc);
4327			}
4328	
4329			ctx.restore();
4330		}
4331	});
4332	
4333	var valueOrDefault$1 = helpers$1.valueOrDefault;
4334	
4335	var defaultColor = core_defaults.global.defaultColor;
4336	
4337	core_defaults._set('global', {
4338		elements: {
4339			line: {
4340				tension: 0.4,
4341				backgroundColor: defaultColor,
4342				borderWidth: 3,
4343				borderColor: defaultColor,
4344				borderCapStyle: 'butt',
4345				borderDash: [],
4346				borderDashOffset: 0.0,
4347				borderJoinStyle: 'miter',
4348				capBezierPoints: true,
4349				fill: true, // do we fill in the area between the line and its base axis
4350			}
4351		}
4352	});
4353	
4354	var element_line = core_element.extend({
4355		_type: 'line',
4356	
4357		draw: function() {
4358			var me = this;
4359			var vm = me._view;
4360			var ctx = me._chart.ctx;
4361			var spanGaps = vm.spanGaps;
4362			var points = me._children.slice(); // clone array
4363			var globalDefaults = core_defaults.global;
4364			var globalOptionLineElements = globalDefaults.elements.line;
4365			var lastDrawnIndex = -1;
4366			var closePath = me._loop;
4367			var index, previous, currentVM;
4368	
4369			if (!points.length) {
4370				return;
4371			}
4372	
4373			if (me._loop) {
4374				for (index = 0; index < points.length; ++index) {
4375					previous = helpers$1.previousItem(points, index);
4376					// If the line has an open path, shift the point array
4377					if (!points[index]._view.skip && previous._view.skip) {
4378						points = points.slice(index).concat(points.slice(0, index));
4379						closePath = spanGaps;
4380						break;
4381					}
4382				}
4383				// If the line has a close path, add the first point again
4384				if (closePath) {
4385					points.push(points[0]);
4386				}
4387			}
4388	
4389			ctx.save();
4390	
4391			// Stroke Line Options
4392			ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
4393	
4394			// IE 9 and 10 do not support line dash
4395			if (ctx.setLineDash) {
4396				ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
4397			}
4398	
4399			ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset);
4400			ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
4401			ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth);
4402			ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
4403	
4404			// Stroke Line
4405			ctx.beginPath();
4406	
4407			// First point moves to it's starting position no matter what
4408			currentVM = points[0]._view;
4409			if (!currentVM.skip) {
4410				ctx.moveTo(currentVM.x, currentVM.y);
4411				lastDrawnIndex = 0;
4412			}
4413	
4414			for (index = 1; index < points.length; ++index) {
4415				currentVM = points[index]._view;
4416				previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex];
4417	
4418				if (!currentVM.skip) {
4419					if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
4420						// There was a gap and this is the first point after the gap
4421						ctx.moveTo(currentVM.x, currentVM.y);
4422					} else {
4423						// Line to next point
4424						helpers$1.canvas.lineTo(ctx, previous._view, currentVM);
4425					}
4426					lastDrawnIndex = index;
4427				}
4428			}
4429	
4430			if (closePath) {
4431				ctx.closePath();
4432			}
4433	
4434			ctx.stroke();
4435			ctx.restore();
4436		}
4437	});
4438	
4439	var valueOrDefault$2 = helpers$1.valueOrDefault;
4440	
4441	var defaultColor$1 = core_defaults.global.defaultColor;
4442	
4443	core_defaults._set('global', {
4444		elements: {
4445			point: {
4446				radius: 3,
4447				pointStyle: 'circle',
4448				backgroundColor: defaultColor$1,
4449				borderColor: defaultColor$1,
4450				borderWidth: 1,
4451				// Hover
4452				hitRadius: 1,
4453				hoverRadius: 4,
4454				hoverBorderWidth: 1
4455			}
4456		}
4457	});
4458	
4459	function xRange(mouseX) {
4460		var vm = this._view;
4461		return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
4462	}
4463	
4464	function yRange(mouseY) {
4465		var vm = this._view;
4466		return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
4467	}
4468	
4469	var element_point = core_element.extend({
4470		_type: 'point',
4471	
4472		inRange: function(mouseX, mouseY) {
4473			var vm = this._view;
4474			return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
4475		},
4476	
4477		inLabelRange: xRange,
4478		inXRange: xRange,
4479		inYRange: yRange,
4480	
4481		getCenterPoint: function() {
4482			var vm = this._view;
4483			return {
4484				x: vm.x,
4485				y: vm.y
4486			};
4487		},
4488	
4489		getArea: function() {
4490			return Math.PI * Math.pow(this._view.radius, 2);
4491		},
4492	
4493		tooltipPosition: function() {
4494			var vm = this._view;
4495			return {
4496				x: vm.x,
4497				y: vm.y,
4498				padding: vm.radius + vm.borderWidth
4499			};
4500		},
4501	
4502		draw: function(chartArea) {
4503			var vm = this._view;
4504			var ctx = this._chart.ctx;
4505			var pointStyle = vm.pointStyle;
4506			var rotation = vm.rotation;
4507			var radius = vm.radius;
4508			var x = vm.x;
4509			var y = vm.y;
4510			var globalDefaults = core_defaults.global;
4511			var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow
4512	
4513			if (vm.skip) {
4514				return;
4515			}
4516	
4517			// Clipping for Points.
4518			if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) {
4519				ctx.strokeStyle = vm.borderColor || defaultColor;
4520				ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth);
4521				ctx.fillStyle = vm.backgroundColor || defaultColor;
4522				helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
4523			}
4524		}
4525	});
4526	
4527	var defaultColor$2 = core_defaults.global.defaultColor;
4528	
4529	core_defaults._set('global', {
4530		elements: {
4531			rectangle: {
4532				backgroundColor: defaultColor$2,
4533				borderColor: defaultColor$2,
4534				borderSkipped: 'bottom',
4535				borderWidth: 0
4536			}
4537		}
4538	});
4539	
4540	function isVertical(vm) {
4541		return vm && vm.width !== undefined;
4542	}
4543	
4544	/**
4545	 * Helper function to get the bounds of the bar regardless of the orientation
4546	 * @param bar {Chart.Element.Rectangle} the bar
4547	 * @return {Bounds} bounds of the bar
4548	 * @private
4549	 */
4550	function getBarBounds(vm) {
4551		var x1, x2, y1, y2, half;
4552	
4553		if (isVertical(vm)) {
4554			half = vm.width / 2;
4555			x1 = vm.x - half;
4556			x2 = vm.x + half;
4557			y1 = Math.min(vm.y, vm.base);
4558			y2 = Math.max(vm.y, vm.base);
4559		} else {
4560			half = vm.height / 2;
4561			x1 = Math.min(vm.x, vm.base);
4562			x2 = Math.max(vm.x, vm.base);
4563			y1 = vm.y - half;
4564			y2 = vm.y + half;
4565		}
4566	
4567		return {
4568			left: x1,
4569			top: y1,
4570			right: x2,
4571			bottom: y2
4572		};
4573	}
4574	
4575	function swap(orig, v1, v2) {
4576		return orig === v1 ? v2 : orig === v2 ? v1 : orig;
4577	}
4578	
4579	function parseBorderSkipped(vm) {
4580		var edge = vm.borderSkipped;
4581		var res = {};
4582	
4583		if (!edge) {
4584			return res;
4585		}
4586	
4587		if (vm.horizontal) {
4588			if (vm.base > vm.x) {
4589				edge = swap(edge, 'left', 'right');
4590			}
4591		} else if (vm.base < vm.y) {
4592			edge = swap(edge, 'bottom', 'top');
4593		}
4594	
4595		res[edge] = true;
4596		return res;
4597	}
4598	
4599	function parseBorderWidth(vm, maxW, maxH) {
4600		var value = vm.borderWidth;
4601		var skip = parseBorderSkipped(vm);
4602		var t, r, b, l;
4603	
4604		if (helpers$1.isObject(value)) {
4605			t = +value.top || 0;
4606			r = +value.right || 0;
4607			b = +value.bottom || 0;
4608			l = +value.left || 0;
4609		} else {
4610			t = r = b = l = +value || 0;
4611		}
4612	
4613		return {
4614			t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t,
4615			r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r,
4616			b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b,
4617			l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l
4618		};
4619	}
4620	
4621	function boundingRects(vm) {
4622		var bounds = getBarBounds(vm);
4623		var width = bounds.right - bounds.left;
4624		var height = bounds.bottom - bounds.top;
4625		var border = parseBorderWidth(vm, width / 2, height / 2);
4626	
4627		return {
4628			outer: {
4629				x: bounds.left,
4630				y: bounds.top,
4631				w: width,
4632				h: height
4633			},
4634			inner: {
4635				x: bounds.left + border.l,
4636				y: bounds.top + border.t,
4637				w: width - border.l - border.r,
4638				h: height - border.t - border.b
4639			}
4640		};
4641	}
4642	
4643	function inRange(vm, x, y) {
4644		var skipX = x === null;
4645		var skipY = y === null;
4646		var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm);
4647	
4648		return bounds
4649			&& (skipX || x >= bounds.left && x <= bounds.right)
4650			&& (skipY || y >= bounds.top && y <= bounds.bottom);
4651	}
4652	
4653	var element_rectangle = core_element.extend({
4654		_type: 'rectangle',
4655	
4656		draw: function() {
4657			var ctx = this._chart.ctx;
4658			var vm = this._view;
4659			var rects = boundingRects(vm);
4660			var outer = rects.outer;
4661			var inner = rects.inner;
4662	
4663			ctx.fillStyle = vm.backgroundColor;
4664			ctx.fillRect(outer.x, outer.y, outer.w, outer.h);
4665	
4666			if (outer.w === inner.w && outer.h === inner.h) {
4667				return;
4668			}
4669	
4670			ctx.save();
4671			ctx.beginPath();
4672			ctx.rect(outer.x, outer.y, outer.w, outer.h);
4673			ctx.clip();
4674			ctx.fillStyle = vm.borderColor;
4675			ctx.rect(inner.x, inner.y, inner.w, inner.h);
4676			ctx.fill('evenodd');
4677			ctx.restore();
4678		},
4679	
4680		height: function() {
4681			var vm = this._view;
4682			return vm.base - vm.y;
4683		},
4684	
4685		inRange: function(mouseX, mouseY) {
4686			return inRange(this._view, mouseX, mouseY);
4687		},
4688	
4689		inLabelRange: function(mouseX, mouseY) {
4690			var vm = this._view;
4691			return isVertical(vm)
4692				? inRange(vm, mouseX, null)
4693				: inRange(vm, null, mouseY);
4694		},
4695	
4696		inXRange: function(mouseX) {
4697			return inRange(this._view, mouseX, null);
4698		},
4699	
4700		inYRange: function(mouseY) {
4701			return inRange(this._view, null, mouseY);
4702		},
4703	
4704		getCenterPoint: function() {
4705			var vm = this._view;
4706			var x, y;
4707			if (isVertical(vm)) {
4708				x = vm.x;
4709				y = (vm.y + vm.base) / 2;
4710			} else {
4711				x = (vm.x + vm.base) / 2;
4712				y = vm.y;
4713			}
4714	
4715			return {x: x, y: y};
4716		},
4717	
4718		getArea: function() {
4719			var vm = this._view;
4720	
4721			return isVertical(vm)
4722				? vm.width * Math.abs(vm.y - vm.base)
4723				: vm.height * Math.abs(vm.x - vm.base);
4724		},
4725	
4726		tooltipPosition: function() {
4727			var vm = this._view;
4728			return {
4729				x: vm.x,
4730				y: vm.y
4731			};
4732		}
4733	});
4734	
4735	var elements = {};
4736	var Arc = element_arc;
4737	var Line = element_line;
4738	var Point = element_point;
4739	var Rectangle = element_rectangle;
4740	elements.Arc = Arc;
4741	elements.Line = Line;
4742	elements.Point = Point;
4743	elements.Rectangle = Rectangle;
4744	
4745	var deprecated = helpers$1._deprecated;
4746	var valueOrDefault$3 = helpers$1.valueOrDefault;
4747	
4748	core_defaults._set('bar', {
4749		hover: {
4750			mode: 'label'
4751		},
4752	
4753		scales: {
4754			xAxes: [{
4755				type: 'category',
4756				offset: true,
4757				gridLines: {
4758					offsetGridLines: true
4759				}
4760			}],
4761	
4762			yAxes: [{
4763				type: 'linear'
4764			}]
4765		}
4766	});
4767	
4768	core_defaults._set('global', {
4769		datasets: {
4770			bar: {
4771				categoryPercentage: 0.8,
4772				barPercentage: 0.9
4773			}
4774		}
4775	});
4776	
4777	/**
4778	 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
4779	 * @private
4780	 */
4781	function computeMinSampleSize(scale, pixels) {
4782		var min = scale._length;
4783		var prev, curr, i, ilen;
4784	
4785		for (i = 1, ilen = pixels.length; i < ilen; ++i) {
4786			min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
4787		}
4788	
4789		for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) {
4790			curr = scale.getPixelForTick(i);
4791			min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
4792			prev = curr;
4793		}
4794	
4795		return min;
4796	}
4797	
4798	/**
4799	 * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
4800	 * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
4801	 * mode currently always generates bars equally sized (until we introduce scriptable options?).
4802	 * @private
4803	 */
4804	function computeFitCategoryTraits(index, ruler, options) {
4805		var thickness = options.barThickness;
4806		var count = ruler.stackCount;
4807		var curr = ruler.pixels[index];
4808		var min = helpers$1.isNullOrUndef(thickness)
4809			? computeMinSampleSize(ruler.scale, ruler.pixels)
4810			: -1;
4811		var size, ratio;
4812	
4813		if (helpers$1.isNullOrUndef(thickness)) {
4814			size = min * options.categoryPercentage;
4815			ratio = options.barPercentage;
4816		} else {
4817			// When bar thickness is enforced, category and bar percentages are ignored.
4818			// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
4819			// and deprecate barPercentage since this value is ignored when thickness is absolute.
4820			size = thickness * count;
4821			ratio = 1;
4822		}
4823	
4824		return {
4825			chunk: size / count,
4826			ratio: ratio,
4827			start: curr - (size / 2)
4828		};
4829	}
4830	
4831	/**
4832	 * Computes an "optimal" category that globally arranges bars side by side (no gap when
4833	 * percentage options are 1), based on the previous and following categories. This mode
4834	 * generates bars with different widths when data are not evenly spaced.
4835	 * @private
4836	 */
4837	function computeFlexCategoryTraits(index, ruler, options) {
4838		var pixels = ruler.pixels;
4839		var curr = pixels[index];
4840		var prev = index > 0 ? pixels[index - 1] : null;
4841		var next = index < pixels.length - 1 ? pixels[index + 1] : null;
4842		var percent = options.categoryPercentage;
4843		var start, size;
4844	
4845		if (prev === null) {
4846			// first data: its size is double based on the next point or,
4847			// if it's also the last data, we use the scale size.
4848			prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
4849		}
4850	
4851		if (next === null) {
4852			// last data: its size is also double based on the previous point.
4853			next = curr + curr - prev;
4854		}
4855	
4856		start = curr - (curr - Math.min(prev, next)) / 2 * percent;
4857		size = Math.abs(next - prev) / 2 * percent;
4858	
4859		return {
4860			chunk: size / ruler.stackCount,
4861			ratio: options.barPercentage,
4862			start: start
4863		};
4864	}
4865	
4866	var controller_bar = core_datasetController.extend({
4867	
4868		dataElementType: elements.Rectangle,
4869	
4870		/**
4871		 * @private
4872		 */
4873		_dataElementOptions: [
4874			'backgroundColor',
4875			'borderColor',
4876			'borderSkipped',
4877			'borderWidth',
4878			'barPercentage',
4879			'barThickness',
4880			'categoryPercentage',
4881			'maxBarThickness',
4882			'minBarLength'
4883		],
4884	
4885		initialize: function() {
4886			var me = this;
4887			var meta, scaleOpts;
4888	
4889			core_datasetController.prototype.initialize.apply(me, arguments);
4890	
4891			meta = me.getMeta();
4892			meta.stack = me.getDataset().stack;
4893			meta.bar = true;
4894	
4895			scaleOpts = me._getIndexScale().options;
4896			deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage');
4897			deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness');
4898			deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage');
4899			deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength');
4900			deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness');
4901		},
4902	
4903		update: function(reset) {
4904			var me = this;
4905			var rects = me.getMeta().data;
4906			var i, ilen;
4907	
4908			me._ruler = me.getRuler();
4909	
4910			for (i = 0, ilen = rects.length; i < ilen; ++i) {
4911				me.updateElement(rects[i], i, reset);
4912			}
4913		},
4914	
4915		updateElement: function(rectangle, index, reset) {
4916			var me = this;
4917			var meta = me.getMeta();
4918			var dataset = me.getDataset();
4919			var options = me._resolveDataElementOptions(rectangle, index);
4920	
4921			rectangle._xScale = me.getScaleForId(meta.xAxisID);
4922			rectangle._yScale = me.getScaleForId(meta.yAxisID);
4923			rectangle._datasetIndex = me.index;
4924			rectangle._index = index;
4925			rectangle._model = {
4926				backgroundColor: options.backgroundColor,
4927				borderColor: options.borderColor,
4928				borderSkipped: options.borderSkipped,
4929				borderWidth: options.borderWidth,
4930				datasetLabel: dataset.label,
4931				label: me.chart.data.labels[index]
4932			};
4933	
4934			if (helpers$1.isArray(dataset.data[index])) {
4935				rectangle._model.borderSkipped = null;
4936			}
4937	
4938			me._updateElementGeometry(rectangle, index, reset, options);
4939	
4940			rectangle.pivot();
4941		},
4942	
4943		/**
4944		 * @private
4945		 */
4946		_updateElementGeometry: function(rectangle, index, reset, options) {
4947			var me = this;
4948			var model = rectangle._model;
4949			var vscale = me._getValueScale();
4950			var base = vscale.getBasePixel();
4951			var horizontal = vscale.isHorizontal();
4952			var ruler = me._ruler || me.getRuler();
4953			var vpixels = me.calculateBarValuePixels(me.index, index, options);
4954			var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options);
4955	
4956			model.horizontal = horizontal;
4957			model.base = reset ? base : vpixels.base;
4958			model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
4959			model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
4960			model.height = horizontal ? ipixels.size : undefined;
4961			model.width = horizontal ? undefined : ipixels.size;
4962		},
4963	
4964		/**
4965		 * Returns the stacks based on groups and bar visibility.
4966		 * @param {number} [last] - The dataset index
4967		 * @returns {string[]} The list of stack IDs
4968		 * @private
4969		 */
4970		_getStacks: function(last) {
4971			var me = this;
4972			var scale = me._getIndexScale();
4973			var metasets = scale._getMatchingVisibleMetas(me._type);
4974			var stacked = scale.options.stacked;
4975			var ilen = metasets.length;
4976			var stacks = [];
4977			var i, meta;
4978	
4979			for (i = 0; i < ilen; ++i) {
4980				meta = metasets[i];
4981				// stacked   | meta.stack
4982				//           | found | not found | undefined
4983				// false     |   x   |     x     |     x
4984				// true      |       |     x     |
4985				// undefined |       |     x     |     x
4986				if (stacked === false || stacks.indexOf(meta.stack) === -1 ||
4987					(stacked === undefined && meta.stack === undefined)) {
4988					stacks.push(meta.stack);
4989				}
4990				if (meta.index === last) {
4991					break;
4992				}
4993			}
4994	
4995			return stacks;
4996		},
4997	
4998		/**
4999		 * Returns the effective number of stacks based on groups and bar visibility.
5000		 * @private
5001		 */
5002		getStackCount: function() {
5003			return this._getStacks().length;
5004		},
5005	
5006		/**
5007		 * Returns the stack index for the given dataset based on groups and bar visibility.
5008		 * @param {number} [datasetIndex] - The dataset index
5009		 * @param {string} [name] - The stack name to find
5010		 * @returns {number} The stack index
5011		 * @private
5012		 */
5013		getStackIndex: function(datasetIndex, name) {
5014			var stacks = this._getStacks(datasetIndex);
5015			var index = (name !== undefined)
5016				? stacks.indexOf(name)
5017				: -1; // indexOf returns -1 if element is not present
5018	
5019			return (index === -1)
5020				? stacks.length - 1
5021				: index;
5022		},
5023	
5024		/**
5025		 * @private
5026		 */
5027		getRuler: function() {
5028			var me = this;
5029			var scale = me._getIndexScale();
5030			var pixels = [];
5031			var i, ilen;
5032	
5033			for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
5034				pixels.push(scale.getPixelForValue(null, i, me.index));
5035			}
5036	
5037			return {
5038				pixels: pixels,
5039				start: scale._startPixel,
5040				end: scale._endPixel,
5041				stackCount: me.getStackCount(),
5042				scale: scale
5043			};
5044		},
5045	
5046		/**
5047		 * Note: pixel values are not clamped to the scale area.
5048		 * @private
5049		 */
5050		calculateBarValuePixels: function(datasetIndex, index, options) {
5051			var me = this;
5052			var chart = me.chart;
5053			var scale = me._getValueScale();
5054			var isHorizontal = scale.isHorizontal();
5055			var datasets = chart.data.datasets;
5056			var metasets = scale._getMatchingVisibleMetas(me._type);
5057			var value = scale._parseValue(datasets[datasetIndex].data[index]);
5058			var minBarLength = options.minBarLength;
5059			var stacked = scale.options.stacked;
5060			var stack = me.getMeta().stack;
5061			var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;
5062			var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;
5063			var ilen = metasets.length;
5064			var i, imeta, ivalue, base, head, size, stackLength;
5065	
5066			if (stacked || (stacked === undefined && stack !== undefined)) {
5067				for (i = 0; i < ilen; ++i) {
5068					imeta = metasets[i];
5069	
5070					if (imeta.index === datasetIndex) {
5071						break;
5072					}
5073	
5074					if (imeta.stack === stack) {
5075						stackLength = scale._parseValue(datasets[imeta.index].data[index]);
5076						ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;
5077	
5078						if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {
5079							start += ivalue;
5080						}
5081					}
5082				}
5083			}
5084	
5085			base = scale.getPixelForValue(start);
5086			head = scale.getPixelForValue(start + length);
5087			size = head - base;
5088	
5089			if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
5090				size = minBarLength;
5091				if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) {
5092					head = base - minBarLength;
5093				} else {
5094					head = base + minBarLength;
5095				}
5096			}
5097	
5098			return {
5099				size: size,
5100				base: base,
5101				head: head,
5102				center: head + size / 2
5103			};
5104		},
5105	
5106		/**
5107		 * @private
5108		 */
5109		calculateBarIndexPixels: function(datasetIndex, index, ruler, options) {
5110			var me = this;
5111			var range = options.barThickness === 'flex'
5112				? computeFlexCategoryTraits(index, ruler, options)
5113				: computeFitCategoryTraits(index, ruler, options);
5114	
5115			var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
5116			var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
5117			var size = Math.min(
5118				valueOrDefault$3(options.maxBarThickness, Infinity),
5119				range.chunk * range.ratio);
5120	
5121			return {
5122				base: center - size / 2,
5123				head: center + size / 2,
5124				center: center,
5125				size: size
5126			};
5127		},
5128	
5129		draw: function() {
5130			var me = this;
5131			var chart = me.chart;
5132			var scale = me._getValueScale();
5133			var rects = me.getMeta().data;
5134			var dataset = me.getDataset();
5135			var ilen = rects.length;
5136			var i = 0;
5137	
5138			helpers$1.canvas.clipArea(chart.ctx, chart.chartArea);
5139	
5140			for (; i < ilen; ++i) {
5141				var val = scale._parseValue(dataset.data[i]);
5142				if (!isNaN(val.min) && !isNaN(val.max)) {
5143					rects[i].draw();
5144				}
5145			}
5146	
5147			helpers$1.canvas.unclipArea(chart.ctx);
5148		},
5149	
5150		/**
5151		 * @private
5152		 */
5153		_resolveDataElementOptions: function() {
5154			var me = this;
5155			var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments));
5156			var indexOpts = me._getIndexScale().options;
5157			var valueOpts = me._getValueScale().options;
5158	
5159			values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage);
5160			values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness);
5161			values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage);
5162			values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness);
5163			values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength);
5164	
5165			return values;
5166		}
5167	
5168	});
5169	
5170	var valueOrDefault$4 = helpers$1.valueOrDefault;
5171	var resolve$1 = helpers$1.options.resolve;
5172	
5173	core_defaults._set('bubble', {
5174		hover: {
5175			mode: 'single'
5176		},
5177	
5178		scales: {
5179			xAxes: [{
5180				type: 'linear', // bubble should probably use a linear scale by default
5181				position: 'bottom',
5182				id: 'x-axis-0' // need an ID so datasets can reference the scale
5183			}],
5184			yAxes: [{
5185				type: 'linear',
5186				position: 'left',
5187				id: 'y-axis-0'
5188			}]
5189		},
5190	
5191		tooltips: {
5192			callbacks: {
5193				title: function() {
5194					// Title doesn't make sense for scatter since we format the data as a point
5195					return '';
5196				},
5197				label: function(item, data) {
5198					var datasetLabel = data.datasets[item.datasetIndex].label || '';
5199					var dataPoint = data.datasets[item.datasetIndex].data[item.index];
5200					return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
5201				}
5202			}
5203		}
5204	});
5205	
5206	var controller_bubble = core_datasetController.extend({
5207		/**
5208		 * @protected
5209		 */
5210		dataElementType: elements.Point,
5211	
5212		/**
5213		 * @private
5214		 */
5215		_dataElementOptions: [
5216			'backgroundColor',
5217			'borderColor',
5218			'borderWidth',
5219			'hoverBackgroundColor',
5220			'hoverBorderColor',
5221			'hoverBorderWidth',
5222			'hoverRadius',
5223			'hitRadius',
5224			'pointStyle',
5225			'rotation'
5226		],
5227	
5228		/**
5229		 * @protected
5230		 */
5231		update: function(reset) {
5232			var me = this;
5233			var meta = me.getMeta();
5234			var points = meta.data;
5235	
5236			// Update Points
5237			helpers$1.each(points, function(point, index) {
5238				me.updateElement(point, index, reset);
5239			});
5240		},
5241	
5242		/**
5243		 * @protected
5244		 */
5245		updateElement: function(point, index, reset) {
5246			var me = this;
5247			var meta = me.getMeta();
5248			var custom = point.custom || {};
5249			var xScale = me.getScaleForId(meta.xAxisID);
5250			var yScale = me.getScaleForId(meta.yAxisID);
5251			var options = me._resolveDataElementOptions(point, index);
5252			var data = me.getDataset().data[index];
5253			var dsIndex = me.index;
5254	
5255			var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
5256			var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
5257	
5258			point._xScale = xScale;
5259			point._yScale = yScale;
5260			point._options = options;
5261			point._datasetIndex = dsIndex;
5262			point._index = index;
5263			point._model = {
5264				backgroundColor: options.backgroundColor,
5265				borderColor: options.borderColor,
5266				borderWidth: options.borderWidth,
5267				hitRadius: options.hitRadius,
5268				pointStyle: options.pointStyle,
5269				rotation: options.rotation,
5270				radius: reset ? 0 : options.radius,
5271				skip: custom.skip || isNaN(x) || isNaN(y),
5272				x: x,
5273				y: y,
5274			};
5275	
5276			point.pivot();
5277		},
5278	
5279		/**
5280		 * @protected
5281		 */
5282		setHoverStyle: function(point) {
5283			var model = point._model;
5284			var options = point._options;
5285			var getHoverColor = helpers$1.getHoverColor;
5286	
5287			point.$previousStyle = {
5288				backgroundColor: model.backgroundColor,
5289				borderColor: model.borderColor,
5290				borderWidth: model.borderWidth,
5291				radius: model.radius
5292			};
5293	
5294			model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
5295			model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor));
5296			model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth);
5297			model.radius = options.radius + options.hoverRadius;
5298		},
5299	
5300		/**
5301		 * @private
5302		 */
5303		_resolveDataElementOptions: function(point, index) {
5304			var me = this;
5305			var chart = me.chart;
5306			var dataset = me.getDataset();
5307			var custom = point.custom || {};
5308			var data = dataset.data[index] || {};
5309			var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments);
5310	
5311			// Scriptable options
5312			var context = {
5313				chart: chart,
5314				dataIndex: index,
5315				dataset: dataset,
5316				datasetIndex: me.index
5317			};
5318	
5319			// In case values were cached (and thus frozen), we need to clone the values
5320			if (me._cachedDataOpts === values) {
5321				values = helpers$1.extend({}, values);
5322			}
5323	
5324			// Custom radius resolution
5325			values.radius = resolve$1([
5326				custom.radius,
5327				data.r,
5328				me._config.radius,
5329				chart.options.elements.point.radius
5330			], context, index);
5331	
5332			return values;
5333		}
5334	});
5335	
5336	var valueOrDefault$5 = helpers$1.valueOrDefault;
5337	
5338	var PI$1 = Math.PI;
5339	var DOUBLE_PI$1 = PI$1 * 2;
5340	var HALF_PI$1 = PI$1 / 2;
5341	
5342	core_defaults._set('doughnut', {
5343		animation: {
5344			// Boolean - Whether we animate the rotation of the Doughnut
5345			animateRotate: true,
5346			// Boolean - Whether we animate scaling the Doughnut from the centre
5347			animateScale: false
5348		},
5349		hover: {
5350			mode: 'single'
5351		},
5352		legendCallback: function(chart) {
5353			var list = document.createElement('ul');
5354			var data = chart.data;
5355			var datasets = data.datasets;
5356			var labels = data.labels;
5357			var i, ilen, listItem, listItemSpan;
5358	
5359			list.setAttribute('class', chart.id + '-legend');
5360			if (datasets.length) {
5361				for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
5362					listItem = list.appendChild(document.createElement('li'));
5363					listItemSpan = listItem.appendChild(document.createElement('span'));
5364					listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
5365					if (labels[i]) {
5366						listItem.appendChild(document.createTextNode(labels[i]));
5367					}
5368				}
5369			}
5370	
5371			return list.outerHTML;
5372		},
5373		legend: {
5374			labels: {
5375				generateLabels: function(chart) {
5376					var data = chart.data;
5377					if (data.labels.length && data.datasets.length) {
5378						return data.labels.map(function(label, i) {
5379							var meta = chart.getDatasetMeta(0);
5380							var style = meta.controller.getStyle(i);
5381	
5382							return {
5383								text: label,
5384								fillStyle: style.backgroundColor,
5385								strokeStyle: style.borderColor,
5386								lineWidth: style.borderWidth,
5387								hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
5388	
5389								// Extra data used for toggling the correct item
5390								index: i
5391							};
5392						});
5393					}
5394					return [];
5395				}
5396			},
5397	
5398			onClick: function(e, legendItem) {
5399				var index = legendItem.index;
5400				var chart = this.chart;
5401				var i, ilen, meta;
5402	
5403				for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
5404					meta = chart.getDatasetMeta(i);
5405					// toggle visibility of index if exists
5406					if (meta.data[index]) {
5407						meta.data[index].hidden = !meta.data[index].hidden;
5408					}
5409				}
5410	
5411				chart.update();
5412			}
5413		},
5414	
5415		// The percentage of the chart that we cut out of the middle.
5416		cutoutPercentage: 50,
5417	
5418		// The rotation of the chart, where the first data arc begins.
5419		rotation: -HALF_PI$1,
5420	
5421		// The total circumference of the chart.
5422		circumference: DOUBLE_PI$1,
5423	
5424		// Need to override these to give a nice default
5425		tooltips: {
5426			callbacks: {
5427				title: function() {
5428					return '';
5429				},
5430				label: function(tooltipItem, data) {
5431					var dataLabel = data.labels[tooltipItem.index];
5432					var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
5433	
5434					if (helpers$1.isArray(dataLabel)) {
5435						// show value on first line of multiline label
5436						// need to clone because we are changing the value
5437						dataLabel = dataLabel.slice();
5438						dataLabel[0] += value;
5439					} else {
5440						dataLabel += value;
5441					}
5442	
5443					return dataLabel;
5444				}
5445			}
5446		}
5447	});
5448	
5449	var controller_doughnut = core_datasetController.extend({
5450	
5451		dataElementType: elements.Arc,
5452	
5453		linkScales: helpers$1.noop,
5454	
5455		/**
5456		 * @private
5457		 */
5458		_dataElementOptions: [
5459			'backgroundColor',
5460			'borderColor',
5461			'borderWidth',
5462			'borderAlign',
5463			'hoverBackgroundColor',
5464			'hoverBorderColor',
5465			'hoverBorderWidth',
5466		],
5467	
5468		// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
5469		getRingIndex: function(datasetIndex) {
5470			var ringIndex = 0;
5471	
5472			for (var j = 0; j < datasetIndex; ++j) {
5473				if (this.chart.isDatasetVisible(j)) {
5474					++ringIndex;
5475				}
5476			}
5477	
5478			return ringIndex;
5479		},
5480	
5481		update: function(reset) {
5482			var me = this;
5483			var chart = me.chart;
5484			var chartArea = chart.chartArea;
5485			var opts = chart.options;
5486			var ratioX = 1;
5487			var ratioY = 1;
5488			var offsetX = 0;
5489			var offsetY = 0;
5490			var meta = me.getMeta();
5491			var arcs = meta.data;
5492			var cutout = opts.cutoutPercentage / 100 || 0;
5493			var circumference = opts.circumference;
5494			var chartWeight = me._getRingWeight(me.index);
5495			var maxWidth, maxHeight, i, ilen;
5496	
5497			// If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
5498			if (circumference < DOUBLE_PI$1) {
5499				var startAngle = opts.rotation % DOUBLE_PI$1;
5500				startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0;
5501				var endAngle = startAngle + circumference;
5502				var startX = Math.cos(startAngle);
5503				var startY = Math.sin(startAngle);
5504				var endX = Math.cos(endAngle);
5505				var endY = Math.sin(endAngle);
5506				var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1;
5507				var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1;
5508				var contains180 = startAngle === -PI$1 || endAngle >= PI$1;
5509				var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1;
5510				var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
5511				var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
5512				var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
5513				var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
5514				ratioX = (maxX - minX) / 2;
5515				ratioY = (maxY - minY) / 2;
5516				offsetX = -(maxX + minX) / 2;
5517				offsetY = -(maxY + minY) / 2;
5518			}
5519	
5520			for (i = 0, ilen = arcs.length; i < ilen; ++i) {
5521				arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
5522			}
5523	
5524			chart.borderWidth = me.getMaxBorderWidth();
5525			maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
5526			maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
5527			chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
5528			chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
5529			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
5530			chart.offsetX = offsetX * chart.outerRadius;
5531			chart.offsetY = offsetY * chart.outerRadius;
5532	
5533			meta.total = me.calculateTotal();
5534	
5535			me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
5536			me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);
5537	
5538			for (i = 0, ilen = arcs.length; i < ilen; ++i) {
5539				me.updateElement(arcs[i], i, reset);
5540			}
5541		},
5542	
5543		updateElement: function(arc, index, reset) {
5544			var me = this;
5545			var chart = me.chart;
5546			var chartArea = chart.chartArea;
5547			var opts = chart.options;
5548			var animationOpts = opts.animation;
5549			var centerX = (chartArea.left + chartArea.right) / 2;
5550			var centerY = (chartArea.top + chartArea.bottom) / 2;
5551			var startAngle = opts.rotation; // non reset case handled later
5552			var endAngle = opts.rotation; // non reset case handled later
5553			var dataset = me.getDataset();
5554			var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1);
5555			var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
5556			var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
5557			var options = arc._options || {};
5558	
5559			helpers$1.extend(arc, {
5560				// Utility
5561				_datasetIndex: me.index,
5562				_index: index,
5563	
5564				// Desired view properties
5565				_model: {
5566					backgroundColor: options.backgroundColor,
5567					borderColor: options.borderColor,
5568					borderWidth: options.borderWidth,
5569					borderAlign: options.borderAlign,
5570					x: centerX + chart.offsetX,
5571					y: centerY + chart.offsetY,
5572					startAngle: startAngle,
5573					endAngle: endAngle,
5574					circumference: circumference,
5575					outerRadius: outerRadius,
5576					innerRadius: innerRadius,
5577					label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
5578				}
5579			});
5580	
5581			var model = arc._model;
5582	
5583			// Set correct angles if not resetting
5584			if (!reset || !animationOpts.animateRotate) {
5585				if (index === 0) {
5586					model.startAngle = opts.rotation;
5587				} else {
5588					model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
5589				}
5590	
5591				model.endAngle = model.startAngle + model.circumference;
5592			}
5593	
5594			arc.pivot();
5595		},
5596	
5597		calculateTotal: function() {
5598			var dataset = this.getDataset();
5599			var meta = this.getMeta();
5600			var total = 0;
5601			var value;
5602	
5603			helpers$1.each(meta.data, function(element, index) {
5604				value = dataset.data[index];
5605				if (!isNaN(value) && !element.hidden) {
5606					total += Math.abs(value);
5607				}
5608			});
5609	
5610			/* if (total === 0) {
5611				total = NaN;
5612			}*/
5613	
5614			return total;
5615		},
5616	
5617		calculateCircumference: function(value) {
5618			var total = this.getMeta().total;
5619			if (total > 0 && !isNaN(value)) {
5620				return DOUBLE_PI$1 * (Math.abs(value) / total);
5621			}
5622			return 0;
5623		},
5624	
5625		// gets the max border or hover width to properly scale pie charts
5626		getMaxBorderWidth: function(arcs) {
5627			var me = this;
5628			var max = 0;
5629			var chart = me.chart;
5630			var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
5631	
5632			if (!arcs) {
5633				// Find the outmost visible dataset
5634				for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
5635					if (chart.isDatasetVisible(i)) {
5636						meta = chart.getDatasetMeta(i);
5637						arcs = meta.data;
5638						if (i !== me.index) {
5639							controller = meta.controller;
5640						}
5641						break;
5642					}
5643				}
5644			}
5645	
5646			if (!arcs) {
5647				return 0;
5648			}
5649	
5650			for (i = 0, ilen = arcs.length; i < ilen; ++i) {
5651				arc = arcs[i];
5652				if (controller) {
5653					controller._configure();
5654					options = controller._resolveDataElementOptions(arc, i);
5655				} else {
5656					options = arc._options;
5657				}
5658				if (options.borderAlign !== 'inner') {
5659					borderWidth = options.borderWidth;
5660					hoverWidth = options.hoverBorderWidth;
5661	
5662					max = borderWidth > max ? borderWidth : max;
5663					max = hoverWidth > max ? hoverWidth : max;
5664				}
5665			}
5666			return max;
5667		},
5668	
5669		/**
5670		 * @protected
5671		 */
5672		setHoverStyle: function(arc) {
5673			var model = arc._model;
5674			var options = arc._options;
5675			var getHoverColor = helpers$1.getHoverColor;
5676	
5677			arc.$previousStyle = {
5678				backgroundColor: model.backgroundColor,
5679				borderColor: model.borderColor,
5680				borderWidth: model.borderWidth,
5681			};
5682	
5683			model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
5684			model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor));
5685			model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth);
5686		},
5687	
5688		/**
5689		 * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
5690		 * @private
5691		 */
5692		_getRingWeightOffset: function(datasetIndex) {
5693			var ringWeightOffset = 0;
5694	
5695			for (var i = 0; i < datasetIndex; ++i) {
5696				if (this.chart.isDatasetVisible(i)) {
5697					ringWeightOffset += this._getRingWeight(i);
5698				}
5699			}
5700	
5701			return ringWeightOffset;
5702		},
5703	
5704		/**
5705		 * @private
5706		 */
5707		_getRingWeight: function(dataSetIndex) {
5708			return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0);
5709		},
5710	
5711		/**
5712		 * Returns the sum of all visibile data set weights.  This value can be 0.
5713		 * @private
5714		 */
5715		_getVisibleDatasetWeightTotal: function() {
5716			return this._getRingWeightOffset(this.chart.data.datasets.length);
5717		}
5718	});
5719	
5720	core_defaults._set('horizontalBar', {
5721		hover: {
5722			mode: 'index',
5723			axis: 'y'
5724		},
5725	
5726		scales: {
5727			xAxes: [{
5728				type: 'linear',
5729				position: 'bottom'
5730			}],
5731	
5732			yAxes: [{
5733				type: 'category',
5734				position: 'left',
5735				offset: true,
5736				gridLines: {
5737					offsetGridLines: true
5738				}
5739			}]
5740		},
5741	
5742		elements: {
5743			rectangle: {
5744				borderSkipped: 'left'
5745			}
5746		},
5747	
5748		tooltips: {
5749			mode: 'index',
5750			axis: 'y'
5751		}
5752	});
5753	
5754	core_defaults._set('global', {
5755		datasets: {
5756			horizontalBar: {
5757				categoryPercentage: 0.8,
5758				barPercentage: 0.9
5759			}
5760		}
5761	});
5762	
5763	var controller_horizontalBar = controller_bar.extend({
5764		/**
5765		 * @private
5766		 */
5767		_getValueScaleId: function() {
5768			return this.getMeta().xAxisID;
5769		},
5770	
5771		/**
5772		 * @private
5773		 */
5774		_getIndexScaleId: function() {
5775			return this.getMeta().yAxisID;
5776		}
5777	});
5778	
5779	var valueOrDefault$6 = helpers$1.valueOrDefault;
5780	var resolve$2 = helpers$1.options.resolve;
5781	var isPointInArea = helpers$1.canvas._isPointInArea;
5782	
5783	core_defaults._set('line', {
5784		showLines: true,
5785		spanGaps: false,
5786	
5787		hover: {
5788			mode: 'label'
5789		},
5790	
5791		scales: {
5792			xAxes: [{
5793				type: 'category',
5794				id: 'x-axis-0'
5795			}],
5796			yAxes: [{
5797				type: 'linear',
5798				id: 'y-axis-0'
5799			}]
5800		}
5801	});
5802	
5803	function scaleClip(scale, halfBorderWidth) {
5804		var tickOpts = scale && scale.options.ticks || {};
5805		var reverse = tickOpts.reverse;
5806		var min = tickOpts.min === undefined ? halfBorderWidth : 0;
5807		var max = tickOpts.max === undefined ? halfBorderWidth : 0;
5808		return {
5809			start: reverse ? max : min,
5810			end: reverse ? min : max
5811		};
5812	}
5813	
5814	function defaultClip(xScale, yScale, borderWidth) {
5815		var halfBorderWidth = borderWidth / 2;
5816		var x = scaleClip(xScale, halfBorderWidth);
5817		var y = scaleClip(yScale, halfBorderWidth);
5818	
5819		return {
5820			top: y.end,
5821			right: x.end,
5822			bottom: y.start,
5823			left: x.start
5824		};
5825	}
5826	
5827	function toClip(value) {
5828		var t, r, b, l;
5829	
5830		if (helpers$1.isObject(value)) {
5831			t = value.top;
5832			r = value.right;
5833			b = value.bottom;
5834			l = value.left;
5835		} else {
5836			t = r = b = l = value;
5837		}
5838	
5839		return {
5840			top: t,
5841			right: r,
5842			bottom: b,
5843			left: l
5844		};
5845	}
5846	
5847	
5848	var controller_line = core_datasetController.extend({
5849	
5850		datasetElementType: elements.Line,
5851	
5852		dataElementType: elements.Point,
5853	
5854		/**
5855		 * @private
5856		 */
5857		_datasetElementOptions: [
5858			'backgroundColor',
5859			'borderCapStyle',
5860			'borderColor',
5861			'borderDash',
5862			'borderDashOffset',
5863			'borderJoinStyle',
5864			'borderWidth',
5865			'cubicInterpolationMode',
5866			'fill'
5867		],
5868	
5869		/**
5870		 * @private
5871		 */
5872		_dataElementOptions: {
5873			backgroundColor: 'pointBackgroundColor',
5874			borderColor: 'pointBorderColor',
5875			borderWidth: 'pointBorderWidth',
5876			hitRadius: 'pointHitRadius',
5877			hoverBackgroundColor: 'pointHoverBackgroundColor',
5878			hoverBorderColor: 'pointHoverBorderColor',
5879			hoverBorderWidth: 'pointHoverBorderWidth',
5880			hoverRadius: 'pointHoverRadius',
5881			pointStyle: 'pointStyle',
5882			radius: 'pointRadius',
5883			rotation: 'pointRotation'
5884		},
5885	
5886		update: function(reset) {
5887			var me = this;
5888			var meta = me.getMeta();
5889			var line = meta.dataset;
5890			var points = meta.data || [];
5891			var options = me.chart.options;
5892			var config = me._config;
5893			var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines);
5894			var i, ilen;
5895	
5896			me._xScale = me.getScaleForId(meta.xAxisID);
5897			me._yScale = me.getScaleForId(meta.yAxisID);
5898	
5899			// Update Line
5900			if (showLine) {
5901				// Compatibility: If the properties are defined with only the old name, use those values
5902				if (config.tension !== undefined && config.lineTension === undefined) {
5903					config.lineTension = config.tension;
5904				}
5905	
5906				// Utility
5907				line._scale = me._yScale;
5908				line._datasetIndex = me.index;
5909				// Data
5910				line._children = points;
5911				// Model
5912				line._model = me._resolveDatasetElementOptions(line);
5913	
5914				line.pivot();
5915			}
5916	
5917			// Update Points
5918			for (i = 0, ilen = points.length; i < ilen; ++i) {
5919				me.updateElement(points[i], i, reset);
5920			}
5921	
5922			if (showLine && line._model.tension !== 0) {
5923				me.updateBezierControlPoints();
5924			}
5925	
5926			// Now pivot the point for animation
5927			for (i = 0, ilen = points.length; i < ilen; ++i) {
5928				points[i].pivot();
5929			}
5930		},
5931	
5932		updateElement: function(point, index, reset) {
5933			var me = this;
5934			var meta = me.getMeta();
5935			var custom = point.custom || {};
5936			var dataset = me.getDataset();
5937			var datasetIndex = me.index;
5938			var value = dataset.data[index];
5939			var xScale = me._xScale;
5940			var yScale = me._yScale;
5941			var lineModel = meta.dataset._model;
5942			var x, y;
5943	
5944			var options = me._resolveDataElementOptions(point, index);
5945	
5946			x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
5947			y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
5948	
5949			// Utility
5950			point._xScale = xScale;
5951			point._yScale = yScale;
5952			point._options = options;
5953			point._datasetIndex = datasetIndex;
5954			point._index = index;
5955	
5956			// Desired view properties
5957			point._model = {
5958				x: x,
5959				y: y,
5960				skip: custom.skip || isNaN(x) || isNaN(y),
5961				// Appearance
5962				radius: options.radius,
5963				pointStyle: options.pointStyle,
5964				rotation: options.rotation,
5965				backgroundColor: options.backgroundColor,
5966				borderColor: options.borderColor,
5967				borderWidth: options.borderWidth,
5968				tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0),
5969				steppedLine: lineModel ? lineModel.steppedLine : false,
5970				// Tooltip
5971				hitRadius: options.hitRadius
5972			};
5973		},
5974	
5975		/**
5976		 * @private
5977		 */
5978		_resolveDatasetElementOptions: function(element) {
5979			var me = this;
5980			var config = me._config;
5981			var custom = element.custom || {};
5982			var options = me.chart.options;
5983			var lineOptions = options.elements.line;
5984			var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
5985	
5986			// The default behavior of lines is to break at null values, according
5987			// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
5988			// This option gives lines the ability to span gaps
5989			values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps);
5990			values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension);
5991			values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]);
5992			values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth)));
5993	
5994			return values;
5995		},
5996	
5997		calculatePointY: function(value, index, datasetIndex) {
5998			var me = this;
5999			var chart = me.chart;
6000			var yScale = me._yScale;
6001			var sumPos = 0;
6002			var sumNeg = 0;
6003			var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen;
6004	
6005			if (yScale.options.stacked) {
6006				rightValue = +yScale.getRightValue(value);
6007				metasets = chart._getSortedVisibleDatasetMetas();
6008				ilen = metasets.length;
6009	
6010				for (i = 0; i < ilen; ++i) {
6011					dsMeta = metasets[i];
6012					if (dsMeta.index === datasetIndex) {
6013						break;
6014					}
6015	
6016					ds = chart.data.datasets[dsMeta.index];
6017					if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) {
6018						stackedRightValue = +yScale.getRightValue(ds.data[index]);
6019						if (stackedRightValue < 0) {
6020							sumNeg += stackedRightValue || 0;
6021						} else {
6022							sumPos += stackedRightValue || 0;
6023						}
6024					}
6025				}
6026	
6027				if (rightValue < 0) {
6028					return yScale.getPixelForValue(sumNeg + rightValue);
6029				}
6030				return yScale.getPixelForValue(sumPos + rightValue);
6031			}
6032			return yScale.getPixelForValue(value);
6033		},
6034	
6035		updateBezierControlPoints: function() {
6036			var me = this;
6037			var chart = me.chart;
6038			var meta = me.getMeta();
6039			var lineModel = meta.dataset._model;
6040			var area = chart.chartArea;
6041			var points = meta.data || [];
6042			var i, ilen, model, controlPoints;
6043	
6044			// Only consider points that are drawn in case the spanGaps option is used
6045			if (lineModel.spanGaps) {
6046				points = points.filter(function(pt) {
6047					return !pt._model.skip;
6048				});
6049			}
6050	
6051			function capControlPoint(pt, min, max) {
6052				return Math.max(Math.min(pt, max), min);
6053			}
6054	
6055			if (lineModel.cubicInterpolationMode === 'monotone') {
6056				helpers$1.splineCurveMonotone(points);
6057			} else {
6058				for (i = 0, ilen = points.length; i < ilen; ++i) {
6059					model = points[i]._model;
6060					controlPoints = helpers$1.splineCurve(
6061						helpers$1.previousItem(points, i)._model,
6062						model,
6063						helpers$1.nextItem(points, i)._model,
6064						lineModel.tension
6065					);
6066					model.controlPointPreviousX = controlPoints.previous.x;
6067					model.controlPointPreviousY = controlPoints.previous.y;
6068					model.controlPointNextX = controlPoints.next.x;
6069					model.controlPointNextY = controlPoints.next.y;
6070				}
6071			}
6072	
6073			if (chart.options.elements.line.capBezierPoints) {
6074				for (i = 0, ilen = points.length; i < ilen; ++i) {
6075					model = points[i]._model;
6076					if (isPointInArea(model, area)) {
6077						if (i > 0 && isPointInArea(points[i - 1]._model, area)) {
6078							model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
6079							model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
6080						}
6081						if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {
6082							model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
6083							model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
6084						}
6085					}
6086				}
6087			}
6088		},
6089	
6090		draw: function() {
6091			var me = this;
6092			var chart = me.chart;
6093			var meta = me.getMeta();
6094			var points = meta.data || [];
6095			var area = chart.chartArea;
6096			var canvas = chart.canvas;
6097			var i = 0;
6098			var ilen = points.length;
6099			var clip;
6100	
6101			if (me._showLine) {
6102				clip = meta.dataset._model.clip;
6103	
6104				helpers$1.canvas.clipArea(chart.ctx, {
6105					left: clip.left === false ? 0 : area.left - clip.left,
6106					right: clip.right === false ? canvas.width : area.right + clip.right,
6107					top: clip.top === false ? 0 : area.top - clip.top,
6108					bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom
6109				});
6110	
6111				meta.dataset.draw();
6112	
6113				helpers$1.canvas.unclipArea(chart.ctx);
6114			}
6115	
6116			// Draw the points
6117			for (; i < ilen; ++i) {
6118				points[i].draw(area);
6119			}
6120		},
6121	
6122		/**
6123		 * @protected
6124		 */
6125		setHoverStyle: function(point) {
6126			var model = point._model;
6127			var options = point._options;
6128			var getHoverColor = helpers$1.getHoverColor;
6129	
6130			point.$previousStyle = {
6131				backgroundColor: model.backgroundColor,
6132				borderColor: model.borderColor,
6133				borderWidth: model.borderWidth,
6134				radius: model.radius
6135			};
6136	
6137			model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
6138			model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));
6139			model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);
6140			model.radius = valueOrDefault$6(options.hoverRadius, options.radius);
6141		},
6142	});
6143	
6144	var resolve$3 = helpers$1.options.resolve;
6145	
6146	core_defaults._set('polarArea', {
6147		scale: {
6148			type: 'radialLinear',
6149			angleLines: {
6150				display: false
6151			},
6152			gridLines: {
6153				circular: true
6154			},
6155			pointLabels: {
6156				display: false
6157			},
6158			ticks: {
6159				beginAtZero: true
6160			}
6161		},
6162	
6163		// Boolean - Whether to animate the rotation of the chart
6164		animation: {
6165			animateRotate: true,
6166			animateScale: true
6167		},
6168	
6169		startAngle: -0.5 * Math.PI,
6170		legendCallback: function(chart) {
6171			var list = document.createElement('ul');
6172			var data = chart.data;
6173			var datasets = data.datasets;
6174			var labels = data.labels;
6175			var i, ilen, listItem, listItemSpan;
6176	
6177			list.setAttribute('class', chart.id + '-legend');
6178			if (datasets.length) {
6179				for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
6180					listItem = list.appendChild(document.createElement('li'));
6181					listItemSpan = listItem.appendChild(document.createElement('span'));
6182					listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
6183					if (labels[i]) {
6184						listItem.appendChild(document.createTextNode(labels[i]));
6185					}
6186				}
6187			}
6188	
6189			return list.outerHTML;
6190		},
6191		legend: {
6192			labels: {
6193				generateLabels: function(chart) {
6194					var data = chart.data;
6195					if (data.labels.length && data.datasets.length) {
6196						return data.labels.map(function(label, i) {
6197							var meta = chart.getDatasetMeta(0);
6198							var style = meta.controller.getStyle(i);
6199	
6200							return {
6201								text: label,
6202								fillStyle: style.backgroundColor,
6203								strokeStyle: style.borderColor,
6204								lineWidth: style.borderWidth,
6205								hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
6206	
6207								// Extra data used for toggling the correct item
6208								index: i
6209							};
6210						});
6211					}
6212					return [];
6213				}
6214			},
6215	
6216			onClick: function(e, legendItem) {
6217				var index = legendItem.index;
6218				var chart = this.chart;
6219				var i, ilen, meta;
6220	
6221				for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
6222					meta = chart.getDatasetMeta(i);
6223					meta.data[index].hidden = !meta.data[index].hidden;
6224				}
6225	
6226				chart.update();
6227			}
6228		},
6229	
6230		// Need to override these to give a nice default
6231		tooltips: {
6232			callbacks: {
6233				title: function() {
6234					return '';
6235				},
6236				label: function(item, data) {
6237					return data.labels[item.index] + ': ' + item.yLabel;
6238				}
6239			}
6240		}
6241	});
6242	
6243	var controller_polarArea = core_datasetController.extend({
6244	
6245		dataElementType: elements.Arc,
6246	
6247		linkScales: helpers$1.noop,
6248	
6249		/**
6250		 * @private
6251		 */
6252		_dataElementOptions: [
6253			'backgroundColor',
6254			'borderColor',
6255			'borderWidth',
6256			'borderAlign',
6257			'hoverBackgroundColor',
6258			'hoverBorderColor',
6259			'hoverBorderWidth',
6260		],
6261	
6262		/**
6263		 * @private
6264		 */
6265		_getIndexScaleId: function() {
6266			return this.chart.scale.id;
6267		},
6268	
6269		/**
6270		 * @private
6271		 */
6272		_getValueScaleId: function() {
6273			return this.chart.scale.id;
6274		},
6275	
6276		update: function(reset) {
6277			var me = this;
6278			var dataset = me.getDataset();
6279			var meta = me.getMeta();
6280			var start = me.chart.options.startAngle || 0;
6281			var starts = me._starts = [];
6282			var angles = me._angles = [];
6283			var arcs = meta.data;
6284			var i, ilen, angle;
6285	
6286			me._updateRadius();
6287	
6288			meta.count = me.countVisibleElements();
6289	
6290			for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
6291				starts[i] = start;
6292				angle = me._computeAngle(i);
6293				angles[i] = angle;
6294				start += angle;
6295			}
6296	
6297			for (i = 0, ilen = arcs.length; i < ilen; ++i) {
6298				arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
6299				me.updateElement(arcs[i], i, reset);
6300			}
6301		},
6302	
6303		/**
6304		 * @private
6305		 */
6306		_updateRadius: function() {
6307			var me = this;
6308			var chart = me.chart;
6309			var chartArea = chart.chartArea;
6310			var opts = chart.options;
6311			var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
6312	
6313			chart.outerRadius = Math.max(minSize / 2, 0);
6314			chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
6315			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
6316	
6317			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
6318			me.innerRadius = me.outerRadius - chart.radiusLength;
6319		},
6320	
6321		updateElement: function(arc, index, reset) {
6322			var me = this;
6323			var chart = me.chart;
6324			var dataset = me.getDataset();
6325			var opts = chart.options;
6326			var animationOpts = opts.animation;
6327			var scale = chart.scale;
6328			var labels = chart.data.labels;
6329	
6330			var centerX = scale.xCenter;
6331			var centerY = scale.yCenter;
6332	
6333			// var negHalfPI = -0.5 * Math.PI;
6334			var datasetStartAngle = opts.startAngle;
6335			var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
6336			var startAngle = me._starts[index];
6337			var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
6338	
6339			var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
6340			var options = arc._options || {};
6341	
6342			helpers$1.extend(arc, {
6343				// Utility
6344				_datasetIndex: me.index,
6345				_index: index,
6346				_scale: scale,
6347	
6348				// Desired view properties
6349				_model: {
6350					backgroundColor: options.backgroundColor,
6351					borderColor: options.borderColor,
6352					borderWidth: options.borderWidth,
6353					borderAlign: options.borderAlign,
6354					x: centerX,
6355					y: centerY,
6356					innerRadius: 0,
6357					outerRadius: reset ? resetRadius : distance,
6358					startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
6359					endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
6360					label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index])
6361				}
6362			});
6363	
6364			arc.pivot();
6365		},
6366	
6367		countVisibleElements: function() {
6368			var dataset = this.getDataset();
6369			var meta = this.getMeta();
6370			var count = 0;
6371	
6372			helpers$1.each(meta.data, function(element, index) {
6373				if (!isNaN(dataset.data[index]) && !element.hidden) {
6374					count++;
6375				}
6376			});
6377	
6378			return count;
6379		},
6380	
6381		/**
6382		 * @protected
6383		 */
6384		setHoverStyle: function(arc) {
6385			var model = arc._model;
6386			var options = arc._options;
6387			var getHoverColor = helpers$1.getHoverColor;
6388			var valueOrDefault = helpers$1.valueOrDefault;
6389	
6390			arc.$previousStyle = {
6391				backgroundColor: model.backgroundColor,
6392				borderColor: model.borderColor,
6393				borderWidth: model.borderWidth,
6394			};
6395	
6396			model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
6397			model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
6398			model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
6399		},
6400	
6401		/**
6402		 * @private
6403		 */
6404		_computeAngle: function(index) {
6405			var me = this;
6406			var count = this.getMeta().count;
6407			var dataset = me.getDataset();
6408			var meta = me.getMeta();
6409	
6410			if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
6411				return 0;
6412			}
6413	
6414			// Scriptable options
6415			var context = {
6416				chart: me.chart,
6417				dataIndex: index,
6418				dataset: dataset,
6419				datasetIndex: me.index
6420			};
6421	
6422			return resolve$3([
6423				me.chart.options.elements.arc.angle,
6424				(2 * Math.PI) / count
6425			], context, index);
6426		}
6427	});
6428	
6429	core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut));
6430	core_defaults._set('pie', {
6431		cutoutPercentage: 0
6432	});
6433	
6434	// Pie charts are Doughnut chart with different defaults
6435	var controller_pie = controller_doughnut;
6436	
6437	var valueOrDefault$7 = helpers$1.valueOrDefault;
6438	
6439	core_defaults._set('radar', {
6440		spanGaps: false,
6441		scale: {
6442			type: 'radialLinear'
6443		},
6444		elements: {
6445			line: {
6446				fill: 'start',
6447				tension: 0 // no bezier in radar
6448			}
6449		}
6450	});
6451	
6452	var controller_radar = core_datasetController.extend({
6453		datasetElementType: elements.Line,
6454	
6455		dataElementType: elements.Point,
6456	
6457		linkScales: helpers$1.noop,
6458	
6459		/**
6460		 * @private
6461		 */
6462		_datasetElementOptions: [
6463			'backgroundColor',
6464			'borderWidth',
6465			'borderColor',
6466			'borderCapStyle',
6467			'borderDash',
6468			'borderDashOffset',
6469			'borderJoinStyle',
6470			'fill'
6471		],
6472	
6473		/**
6474		 * @private
6475		 */
6476		_dataElementOptions: {
6477			backgroundColor: 'pointBackgroundColor',
6478			borderColor: 'pointBorderColor',
6479			borderWidth: 'pointBorderWidth',
6480			hitRadius: 'pointHitRadius',
6481			hoverBackgroundColor: 'pointHoverBackgroundColor',
6482			hoverBorderColor: 'pointHoverBorderColor',
6483			hoverBorderWidth: 'pointHoverBorderWidth',
6484			hoverRadius: 'pointHoverRadius',
6485			pointStyle: 'pointStyle',
6486			radius: 'pointRadius',
6487			rotation: 'pointRotation'
6488		},
6489	
6490		/**
6491		 * @private
6492		 */
6493		_getIndexScaleId: function() {
6494			return this.chart.scale.id;
6495		},
6496	
6497		/**
6498		 * @private
6499		 */
6500		_getValueScaleId: function() {
6501			return this.chart.scale.id;
6502		},
6503	
6504		update: function(reset) {
6505			var me = this;
6506			var meta = me.getMeta();
6507			var line = meta.dataset;
6508			var points = meta.data || [];
6509			var scale = me.chart.scale;
6510			var config = me._config;
6511			var i, ilen;
6512	
6513			// Compatibility: If the properties are defined with only the old name, use those values
6514			if (config.tension !== undefined && config.lineTension === undefined) {
6515				config.lineTension = config.tension;
6516			}
6517	
6518			// Utility
6519			line._scale = scale;
6520			line._datasetIndex = me.index;
6521			// Data
6522			line._children = points;
6523			line._loop = true;
6524			// Model
6525			line._model = me._resolveDatasetElementOptions(line);
6526	
6527			line.pivot();
6528	
6529			// Update Points
6530			for (i = 0, ilen = points.length; i < ilen; ++i) {
6531				me.updateElement(points[i], i, reset);
6532			}
6533	
6534			// Update bezier control points
6535			me.updateBezierControlPoints();
6536	
6537			// Now pivot the point for animation
6538			for (i = 0, ilen = points.length; i < ilen; ++i) {
6539				points[i].pivot();
6540			}
6541		},
6542	
6543		updateElement: function(point, index, reset) {
6544			var me = this;
6545			var custom = point.custom || {};
6546			var dataset = me.getDataset();
6547			var scale = me.chart.scale;
6548			var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
6549			var options = me._resolveDataElementOptions(point, index);
6550			var lineModel = me.getMeta().dataset._model;
6551			var x = reset ? scale.xCenter : pointPosition.x;
6552			var y = reset ? scale.yCenter : pointPosition.y;
6553	
6554			// Utility
6555			point._scale = scale;
6556			point._options = options;
6557			point._datasetIndex = me.index;
6558			point._index = index;
6559	
6560			// Desired view properties
6561			point._model = {
6562				x: x, // value not used in dataset scale, but we want a consistent API between scales
6563				y: y,
6564				skip: custom.skip || isNaN(x) || isNaN(y),
6565				// Appearance
6566				radius: options.radius,
6567				pointStyle: options.pointStyle,
6568				rotation: options.rotation,
6569				backgroundColor: options.backgroundColor,
6570				borderColor: options.borderColor,
6571				borderWidth: options.borderWidth,
6572				tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0),
6573	
6574				// Tooltip
6575				hitRadius: options.hitRadius
6576			};
6577		},
6578	
6579		/**
6580		 * @private
6581		 */
6582		_resolveDatasetElementOptions: function() {
6583			var me = this;
6584			var config = me._config;
6585			var options = me.chart.options;
6586			var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
6587	
6588			values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps);
6589			values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension);
6590	
6591			return values;
6592		},
6593	
6594		updateBezierControlPoints: function() {
6595			var me = this;
6596			var meta = me.getMeta();
6597			var area = me.chart.chartArea;
6598			var points = meta.data || [];
6599			var i, ilen, model, controlPoints;
6600	
6601			// Only consider points that are drawn in case the spanGaps option is used
6602			if (meta.dataset._model.spanGaps) {
6603				points = points.filter(function(pt) {
6604					return !pt._model.skip;
6605				});
6606			}
6607	
6608			function capControlPoint(pt, min, max) {
6609				return Math.max(Math.min(pt, max), min);
6610			}
6611	
6612			for (i = 0, ilen = points.length; i < ilen; ++i) {
6613				model = points[i]._model;
6614				controlPoints = helpers$1.splineCurve(
6615					helpers$1.previousItem(points, i, true)._model,
6616					model,
6617					helpers$1.nextItem(points, i, true)._model,
6618					model.tension
6619				);
6620	
6621				// Prevent the bezier going outside of the bounds of the graph
6622				model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
6623				model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
6624				model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
6625				model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
6626			}
6627		},
6628	
6629		setHoverStyle: function(point) {
6630			var model = point._model;
6631			var options = point._options;
6632			var getHoverColor = helpers$1.getHoverColor;
6633	
6634			point.$previousStyle = {
6635				backgroundColor: model.backgroundColor,
6636				borderColor: model.borderColor,
6637				borderWidth: model.borderWidth,
6638				radius: model.radius
6639			};
6640	
6641			model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
6642			model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor));
6643			model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth);
6644			model.radius = valueOrDefault$7(options.hoverRadius, options.radius);
6645		}
6646	});
6647	
6648	core_defaults._set('scatter', {
6649		hover: {
6650			mode: 'single'
6651		},
6652	
6653		scales: {
6654			xAxes: [{
6655				id: 'x-axis-1',    // need an ID so datasets can reference the scale
6656				type: 'linear',    // scatter should not use a category axis
6657				position: 'bottom'
6658			}],
6659			yAxes: [{
6660				id: 'y-axis-1',
6661				type: 'linear',
6662				position: 'left'
6663			}]
6664		},
6665	
6666		tooltips: {
6667			callbacks: {
6668				title: function() {
6669					return '';     // doesn't make sense for scatter since data are formatted as a point
6670				},
6671				label: function(item) {
6672					return '(' + item.xLabel + ', ' + item.yLabel + ')';
6673				}
6674			}
6675		}
6676	});
6677	
6678	core_defaults._set('global', {
6679		datasets: {
6680			scatter: {
6681				showLine: false
6682			}
6683		}
6684	});
6685	
6686	// Scatter charts use line controllers
6687	var controller_scatter = controller_line;
6688	
6689	// NOTE export a map in which the key represents the controller type, not
6690	// the class, and so must be CamelCase in order to be correctly retrieved
6691	// by the controller in core.controller.js (`controllers[meta.type]`).
6692	
6693	var controllers = {
6694		bar: controller_bar,
6695		bubble: controller_bubble,
6696		doughnut: controller_doughnut,
6697		horizontalBar: controller_horizontalBar,
6698		line: controller_line,
6699		polarArea: controller_polarArea,
6700		pie: controller_pie,
6701		radar: controller_radar,
6702		scatter: controller_scatter
6703	};
6704	
6705	/**
6706	 * Helper function to get relative position for an event
6707	 * @param {Event|IEvent} event - The event to get the position for
6708	 * @param {Chart} chart - The chart
6709	 * @returns {object} the event position
6710	 */
6711	function getRelativePosition(e, chart) {
6712		if (e.native) {
6713			return {
6714				x: e.x,
6715				y: e.y
6716			};
6717		}
6718	
6719		return helpers$1.getRelativePosition(e, chart);
6720	}
6721	
6722	/**
6723	 * Helper function to traverse all of the visible elements in the chart
6724	 * @param {Chart} chart - the chart
6725	 * @param {function} handler - the callback to execute for each visible item
6726	 */
6727	function parseVisibleItems(chart, handler) {
6728		var metasets = chart._getSortedVisibleDatasetMetas();
6729		var metadata, i, j, ilen, jlen, element;
6730	
6731		for (i = 0, ilen = metasets.length; i < ilen; ++i) {
6732			metadata = metasets[i].data;
6733			for (j = 0, jlen = metadata.length; j < jlen; ++j) {
6734				element = metadata[j];
6735				if (!element._view.skip) {
6736					handler(element);
6737				}
6738			}
6739		}
6740	}
6741	
6742	/**
6743	 * Helper function to get the items that intersect the event position
6744	 * @param {ChartElement[]} items - elements to filter
6745	 * @param {object} position - the point to be nearest to
6746	 * @return {ChartElement[]} the nearest items
6747	 */
6748	function getIntersectItems(chart, position) {
6749		var elements = [];
6750	
6751		parseVisibleItems(chart, function(element) {
6752			if (element.inRange(position.x, position.y)) {
6753				elements.push(element);
6754			}
6755		});
6756	
6757		return elements;
6758	}
6759	
6760	/**
6761	 * Helper function to get the items nearest to the event position considering all visible items in teh chart
6762	 * @param {Chart} chart - the chart to look at elements from
6763	 * @param {object} position - the point to be nearest to
6764	 * @param {boolean} intersect - if true, only consider items that intersect the position
6765	 * @param {function} distanceMetric - function to provide the distance between points
6766	 * @return {ChartElement[]} the nearest items
6767	 */
6768	function getNearestItems(chart, position, intersect, distanceMetric) {
6769		var minDistance = Number.POSITIVE_INFINITY;
6770		var nearestItems = [];
6771	
6772		parseVisibleItems(chart, function(element) {
6773			if (intersect && !element.inRange(position.x, position.y)) {
6774				return;
6775			}
6776	
6777			var center = element.getCenterPoint();
6778			var distance = distanceMetric(position, center);
6779			if (distance < minDistance) {
6780				nearestItems = [element];
6781				minDistance = distance;
6782			} else if (distance === minDistance) {
6783				// Can have multiple items at the same distance in which case we sort by size
6784				nearestItems.push(element);
6785			}
6786		});
6787	
6788		return nearestItems;
6789	}
6790	
6791	/**
6792	 * Get a distance metric function for two points based on the
6793	 * axis mode setting
6794	 * @param {string} axis - the axis mode. x|y|xy
6795	 */
6796	function getDistanceMetricForAxis(axis) {
6797		var useX = axis.indexOf('x') !== -1;
6798		var useY = axis.indexOf('y') !== -1;
6799	
6800		return function(pt1, pt2) {
6801			var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
6802			var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
6803			return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
6804		};
6805	}
6806	
6807	function indexMode(chart, e, options) {
6808		var position = getRelativePosition(e, chart);
6809		// Default axis for index mode is 'x' to match old behaviour
6810		options.axis = options.axis || 'x';
6811		var distanceMetric = getDistanceMetricForAxis(options.axis);
6812		var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6813		var elements = [];
6814	
6815		if (!items.length) {
6816			return [];
6817		}
6818	
6819		chart._getSortedVisibleDatasetMetas().forEach(function(meta) {
6820			var element = meta.data[items[0]._index];
6821	
6822			// don't count items that are skipped (null data)
6823			if (element && !element._view.skip) {
6824				elements.push(element);
6825			}
6826		});
6827	
6828		return elements;
6829	}
6830	
6831	/**
6832	 * @interface IInteractionOptions
6833	 */
6834	/**
6835	 * If true, only consider items that intersect the point
6836	 * @name IInterfaceOptions#boolean
6837	 * @type Boolean
6838	 */
6839	
6840	/**
6841	 * Contains interaction related functions
6842	 * @namespace Chart.Interaction
6843	 */
6844	var core_interaction = {
6845		// Helper function for different modes
6846		modes: {
6847			single: function(chart, e) {
6848				var position = getRelativePosition(e, chart);
6849				var elements = [];
6850	
6851				parseVisibleItems(chart, function(element) {
6852					if (element.inRange(position.x, position.y)) {
6853						elements.push(element);
6854						return elements;
6855					}
6856				});
6857	
6858				return elements.slice(0, 1);
6859			},
6860	
6861			/**
6862			 * @function Chart.Interaction.modes.label
6863			 * @deprecated since version 2.4.0
6864			 * @todo remove at version 3
6865			 * @private
6866			 */
6867			label: indexMode,
6868	
6869			/**
6870			 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
6871			 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
6872			 * @function Chart.Interaction.modes.index
6873			 * @since v2.4.0
6874			 * @param {Chart} chart - the chart we are returning items from
6875			 * @param {Event} e - the event we are find things at
6876			 * @param {IInteractionOptions} options - options to use during interaction
6877			 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6878			 */
6879			index: indexMode,
6880	
6881			/**
6882			 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
6883			 * If the options.intersect is false, we find the nearest item and return the items in that dataset
6884			 * @function Chart.Interaction.modes.dataset
6885			 * @param {Chart} chart - the chart we are returning items from
6886			 * @param {Event} e - the event we are find things at
6887			 * @param {IInteractionOptions} options - options to use during interaction
6888			 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6889			 */
6890			dataset: function(chart, e, options) {
6891				var position = getRelativePosition(e, chart);
6892				options.axis = options.axis || 'xy';
6893				var distanceMetric = getDistanceMetricForAxis(options.axis);
6894				var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6895	
6896				if (items.length > 0) {
6897					items = chart.getDatasetMeta(items[0]._datasetIndex).data;
6898				}
6899	
6900				return items;
6901			},
6902	
6903			/**
6904			 * @function Chart.Interaction.modes.x-axis
6905			 * @deprecated since version 2.4.0. Use index mode and intersect == true
6906			 * @todo remove at version 3
6907			 * @private
6908			 */
6909			'x-axis': function(chart, e) {
6910				return indexMode(chart, e, {intersect: false});
6911			},
6912	
6913			/**
6914			 * Point mode returns all elements that hit test based on the event position
6915			 * of the event
6916			 * @function Chart.Interaction.modes.intersect
6917			 * @param {Chart} chart - the chart we are returning items from
6918			 * @param {Event} e - the event we are find things at
6919			 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6920			 */
6921			point: function(chart, e) {
6922				var position = getRelativePosition(e, chart);
6923				return getIntersectItems(chart, position);
6924			},
6925	
6926			/**
6927			 * nearest mode returns the element closest to the point
6928			 * @function Chart.Interaction.modes.intersect
6929			 * @param {Chart} chart - the chart we are returning items from
6930			 * @param {Event} e - the event we are find things at
6931			 * @param {IInteractionOptions} options - options to use
6932			 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6933			 */
6934			nearest: function(chart, e, options) {
6935				var position = getRelativePosition(e, chart);
6936				options.axis = options.axis || 'xy';
6937				var distanceMetric = getDistanceMetricForAxis(options.axis);
6938				return getNearestItems(chart, position, options.intersect, distanceMetric);
6939			},
6940	
6941			/**
6942			 * x mode returns the elements that hit-test at the current x coordinate
6943			 * @function Chart.Interaction.modes.x
6944			 * @param {Chart} chart - the chart we are returning items from
6945			 * @param {Event} e - the event we are find things at
6946			 * @param {IInteractionOptions} options - options to use
6947			 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6948			 */
6949			x: function(chart, e, options) {
6950				var position = getRelativePosition(e, chart);
6951				var items = [];
6952				var intersectsItem = false;
6953	
6954				parseVisibleItems(chart, function(element) {
6955					if (element.inXRange(position.x)) {
6956						items.push(element);
6957					}
6958	
6959					if (element.inRange(position.x, position.y)) {
6960						intersectsItem = true;
6961					}
6962				});
6963	
6964				// If we want to trigger on an intersect and we don't have any items
6965				// that intersect the position, return nothing
6966				if (options.intersect && !intersectsItem) {
6967					items = [];
6968				}
6969				return items;
6970			},
6971	
6972			/**
6973			 * y mode returns the elements that hit-test at the current y coordinate
6974			 * @function Chart.Interaction.modes.y
6975			 * @param {Chart} chart - the chart we are returning items from
6976			 * @param {Event} e - the event we are find things at
6977			 * @param {IInteractionOptions} options - options to use
6978			 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6979			 */
6980			y: function(chart, e, options) {
6981				var position = getRelativePosition(e, chart);
6982				var items = [];
6983				var intersectsItem = false;
6984	
6985				parseVisibleItems(chart, function(element) {
6986					if (element.inYRange(position.y)) {
6987						items.push(element);
6988					}
6989	
6990					if (element.inRange(position.x, position.y)) {
6991						intersectsItem = true;
6992					}
6993				});
6994	
6995				// If we want to trigger on an intersect and we don't have any items
6996				// that intersect the position, return nothing
6997				if (options.intersect && !intersectsItem) {
6998					items = [];
6999				}
7000				return items;
7001			}
7002		}
7003	};
7004	
7005	var extend = helpers$1.extend;
7006	
7007	function filterByPosition(array, position) {
7008		return helpers$1.where(array, function(v) {
7009			return v.pos === position;
7010		});
7011	}
7012	
7013	function sortByWeight(array, reverse) {
7014		return array.sort(function(a, b) {
7015			var v0 = reverse ? b : a;
7016			var v1 = reverse ? a : b;
7017			return v0.weight === v1.weight ?
7018				v0.index - v1.index :
7019				v0.weight - v1.weight;
7020		});
7021	}
7022	
7023	function wrapBoxes(boxes) {
7024		var layoutBoxes = [];
7025		var i, ilen, box;
7026	
7027		for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {
7028			box = boxes[i];
7029			layoutBoxes.push({
7030				index: i,
7031				box: box,
7032				pos: box.position,
7033				horizontal: box.isHorizontal(),
7034				weight: box.weight
7035			});
7036		}
7037		return layoutBoxes;
7038	}
7039	
7040	function setLayoutDims(layouts, params) {
7041		var i, ilen, layout;
7042		for (i = 0, ilen = layouts.length; i < ilen; ++i) {
7043			layout = layouts[i];
7044			// store width used instead of chartArea.w in fitBoxes
7045			layout.width = layout.horizontal
7046				? layout.box.fullWidth && params.availableWidth
7047				: params.vBoxMaxWidth;
7048			// store height used instead of chartArea.h in fitBoxes
7049			layout.height = layout.horizontal && params.hBoxMaxHeight;
7050		}
7051	}
7052	
7053	function buildLayoutBoxes(boxes) {
7054		var layoutBoxes = wrapBoxes(boxes);
7055		var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
7056		var right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
7057		var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
7058		var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
7059	
7060		return {
7061			leftAndTop: left.concat(top),
7062			rightAndBottom: right.concat(bottom),
7063			chartArea: filterByPosition(layoutBoxes, 'chartArea'),
7064			vertical: left.concat(right),
7065			horizontal: top.concat(bottom)
7066		};
7067	}
7068	
7069	function getCombinedMax(maxPadding, chartArea, a, b) {
7070		return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
7071	}
7072	
7073	function updateDims(chartArea, params, layout) {
7074		var box = layout.box;
7075		var maxPadding = chartArea.maxPadding;
7076		var newWidth, newHeight;
7077	
7078		if (layout.size) {
7079			// this layout was already counted for, lets first reduce old size
7080			chartArea[layout.pos] -= layout.size;
7081		}
7082		layout.size = layout.horizontal ? box.height : box.width;
7083		chartArea[layout.pos] += layout.size;
7084	
7085		if (box.getPadding) {
7086			var boxPadding = box.getPadding();
7087			maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
7088			maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
7089			maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
7090			maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
7091		}
7092	
7093		newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right');
7094		newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom');
7095	
7096		if (newWidth !== chartArea.w || newHeight !== chartArea.h) {
7097			chartArea.w = newWidth;
7098			chartArea.h = newHeight;
7099	
7100			// return true if chart area changed in layout's direction
7101			var sizes = layout.horizontal ? [newWidth, chartArea.w] : [newHeight, chartArea.h];
7102			return sizes[0] !== sizes[1] && (!isNaN(sizes[0]) || !isNaN(sizes[1]));
7103		}
7104	}
7105	
7106	function handleMaxPadding(chartArea) {
7107		var maxPadding = chartArea.maxPadding;
7108	
7109		function updatePos(pos) {
7110			var change = Math.max(maxPadding[pos] - chartArea[pos], 0);
7111			chartArea[pos] += change;
7112			return change;
7113		}
7114		chartArea.y += updatePos('top');
7115		chartArea.x += updatePos('left');
7116		updatePos('right');
7117		updatePos('bottom');
7118	}
7119	
7120	function getMargins(horizontal, chartArea) {
7121		var maxPadding = chartArea.maxPadding;
7122	
7123		function marginForPositions(positions) {
7124			var margin = {left: 0, top: 0, right: 0, bottom: 0};
7125			positions.forEach(function(pos) {
7126				margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
7127			});
7128			return margin;
7129		}
7130	
7131		return horizontal
7132			? marginForPositions(['left', 'right'])
7133			: marginForPositions(['top', 'bottom']);
7134	}
7135	
7136	function fitBoxes(boxes, chartArea, params) {
7137		var refitBoxes = [];
7138		var i, ilen, layout, box, refit, changed;
7139	
7140		for (i = 0, ilen = boxes.length; i < ilen; ++i) {
7141			layout = boxes[i];
7142			box = layout.box;
7143	
7144			box.update(
7145				layout.width || chartArea.w,
7146				layout.height || chartArea.h,
7147				getMargins(layout.horizontal, chartArea)
7148			);
7149			if (updateDims(chartArea, params, layout)) {
7150				changed = true;
7151				if (refitBoxes.length) {
7152					// Dimensions changed and there were non full width boxes before this
7153					// -> we have to refit those
7154					refit = true;
7155				}
7156			}
7157			if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case
7158				refitBoxes.push(layout);
7159			}
7160		}
7161	
7162		return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed;
7163	}
7164	
7165	function placeBoxes(boxes, chartArea, params) {
7166		var userPadding = params.padding;
7167		var x = chartArea.x;
7168		var y = chartArea.y;
7169		var i, ilen, layout, box;
7170	
7171		for (i = 0, ilen = boxes.length; i < ilen; ++i) {
7172			layout = boxes[i];
7173			box = layout.box;
7174			if (layout.horizontal) {
7175				box.left = box.fullWidth ? userPadding.left : chartArea.left;
7176				box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w;
7177				box.top = y;
7178				box.bottom = y + box.height;
7179				box.width = box.right - box.left;
7180				y = box.bottom;
7181			} else {
7182				box.left = x;
7183				box.right = x + box.width;
7184				box.top = chartArea.top;
7185				box.bottom = chartArea.top + chartArea.h;
7186				box.height = box.bottom - box.top;
7187				x = box.right;
7188			}
7189		}
7190	
7191		chartArea.x = x;
7192		chartArea.y = y;
7193	}
7194	
7195	core_defaults._set('global', {
7196		layout: {
7197			padding: {
7198				top: 0,
7199				right: 0,
7200				bottom: 0,
7201				left: 0
7202			}
7203		}
7204	});
7205	
7206	/**
7207	 * @interface ILayoutItem
7208	 * @prop {string} position - The position of the item in the chart layout. Possible values are
7209	 * 'left', 'top', 'right', 'bottom', and 'chartArea'
7210	 * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area
7211	 * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
7212	 * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
7213	 * @prop {function} update - Takes two parameters: width and height. Returns size of item
7214	 * @prop {function} getPadding -  Returns an object with padding on the edges
7215	 * @prop {number} width - Width of item. Must be valid after update()
7216	 * @prop {number} height - Height of item. Must be valid after update()
7217	 * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update
7218	 * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update
7219	 * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update
7220	 * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
7221	 */
7222	
7223	// The layout service is very self explanatory.  It's responsible for the layout within a chart.
7224	// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
7225	// It is this service's responsibility of carrying out that layout.
7226	var core_layouts = {
7227		defaults: {},
7228	
7229		/**
7230		 * Register a box to a chart.
7231		 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
7232		 * @param {Chart} chart - the chart to use
7233		 * @param {ILayoutItem} item - the item to add to be layed out
7234		 */
7235		addBox: function(chart, item) {
7236			if (!chart.boxes) {
7237				chart.boxes = [];
7238			}
7239	
7240			// initialize item with default values
7241			item.fullWidth = item.fullWidth || false;
7242			item.position = item.position || 'top';
7243			item.weight = item.weight || 0;
7244			item._layers = item._layers || function() {
7245				return [{
7246					z: 0,
7247					draw: function() {
7248						item.draw.apply(item, arguments);
7249					}
7250				}];
7251			};
7252	
7253			chart.boxes.push(item);
7254		},
7255	
7256		/**
7257		 * Remove a layoutItem from a chart
7258		 * @param {Chart} chart - the chart to remove the box from
7259		 * @param {ILayoutItem} layoutItem - the item to remove from the layout
7260		 */
7261		removeBox: function(chart, layoutItem) {
7262			var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
7263			if (index !== -1) {
7264				chart.boxes.splice(index, 1);
7265			}
7266		},
7267	
7268		/**
7269		 * Sets (or updates) options on the given `item`.
7270		 * @param {Chart} chart - the chart in which the item lives (or will be added to)
7271		 * @param {ILayoutItem} item - the item to configure with the given options
7272		 * @param {object} options - the new item options.
7273		 */
7274		configure: function(chart, item, options) {
7275			var props = ['fullWidth', 'position', 'weight'];
7276			var ilen = props.length;
7277			var i = 0;
7278			var prop;
7279	
7280			for (; i < ilen; ++i) {
7281				prop = props[i];
7282				if (options.hasOwnProperty(prop)) {
7283					item[prop] = options[prop];
7284				}
7285			}
7286		},
7287	
7288		/**
7289		 * Fits boxes of the given chart into the given size by having each box measure itself
7290		 * then running a fitting algorithm
7291		 * @param {Chart} chart - the chart
7292		 * @param {number} width - the width to fit into
7293		 * @param {number} height - the height to fit into
7294		 */
7295		update: function(chart, width, height) {
7296			if (!chart) {
7297				return;
7298			}
7299	
7300			var layoutOptions = chart.options.layout || {};
7301			var padding = helpers$1.options.toPadding(layoutOptions.padding);
7302	
7303			var availableWidth = width - padding.width;
7304			var availableHeight = height - padding.height;
7305			var boxes = buildLayoutBoxes(chart.boxes);
7306			var verticalBoxes = boxes.vertical;
7307			var horizontalBoxes = boxes.horizontal;
7308	
7309			// Essentially we now have any number of boxes on each of the 4 sides.
7310			// Our canvas looks like the following.
7311			// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
7312			// B1 is the bottom axis
7313			// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
7314			// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
7315			// an error will be thrown.
7316			//
7317			// |----------------------------------------------------|
7318			// |                  T1 (Full Width)                   |
7319			// |----------------------------------------------------|
7320			// |    |    |                 T2                  |    |
7321			// |    |----|-------------------------------------|----|
7322			// |    |    | C1 |                           | C2 |    |
7323			// |    |    |----|                           |----|    |
7324			// |    |    |                                     |    |
7325			// | L1 | L2 |           ChartArea (C0)            | R1 |
7326			// |    |    |                                     |    |
7327			// |    |    |----|                           |----|    |
7328			// |    |    | C3 |                           | C4 |    |
7329			// |    |----|-------------------------------------|----|
7330			// |    |    |                 B1                  |    |
7331			// |----------------------------------------------------|
7332			// |                  B2 (Full Width)                   |
7333			// |----------------------------------------------------|
7334			//
7335	
7336			var params = Object.freeze({
7337				outerWidth: width,
7338				outerHeight: height,
7339				padding: padding,
7340				availableWidth: availableWidth,
7341				vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length,
7342				hBoxMaxHeight: availableHeight / 2
7343			});
7344			var chartArea = extend({
7345				maxPadding: extend({}, padding),
7346				w: availableWidth,
7347				h: availableHeight,
7348				x: padding.left,
7349				y: padding.top
7350			}, padding);
7351	
7352			setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
7353	
7354			// First fit vertical boxes
7355			fitBoxes(verticalBoxes, chartArea, params);
7356	
7357			// Then fit horizontal boxes
7358			if (fitBoxes(horizontalBoxes, chartArea, params)) {
7359				// if the area changed, re-fit vertical boxes
7360				fitBoxes(verticalBoxes, chartArea, params);
7361			}
7362	
7363			handleMaxPadding(chartArea);
7364	
7365			// Finally place the boxes to correct coordinates
7366			placeBoxes(boxes.leftAndTop, chartArea, params);
7367	
7368			// Move to opposite side of chart
7369			chartArea.x += chartArea.w;
7370			chartArea.y += chartArea.h;
7371	
7372			placeBoxes(boxes.rightAndBottom, chartArea, params);
7373	
7374			chart.chartArea = {
7375				left: chartArea.left,
7376				top: chartArea.top,
7377				right: chartArea.left + chartArea.w,
7378				bottom: chartArea.top + chartArea.h
7379			};
7380	
7381			// Finally update boxes in chartArea (radial scale for example)
7382			helpers$1.each(boxes.chartArea, function(layout) {
7383				var box = layout.box;
7384				extend(box, chart.chartArea);
7385				box.update(chartArea.w, chartArea.h);
7386			});
7387		}
7388	};
7389	
7390	/**
7391	 * Platform fallback implementation (minimal).
7392	 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
7393	 */
7394	
7395	var platform_basic = {
7396		acquireContext: function(item) {
7397			if (item && item.canvas) {
7398				// Support for any object associated to a canvas (including a context2d)
7399				item = item.canvas;
7400			}
7401	
7402			return item && item.getContext('2d') || null;
7403		}
7404	};
7405	
7406	var platform_dom = "/*\r\n * DOM element rendering detection\r\n * https://davidwalsh.name/detect-node-insertion\r\n */\r\n@keyframes chartjs-render-animation {\r\n\tfrom { opacity: 0.99; }\r\n\tto { opacity: 1; }\r\n}\r\n\r\n.chartjs-render-monitor {\r\n\tanimation: chartjs-render-animation 0.001s;\r\n}\r\n\r\n/*\r\n * DOM element resizing detection\r\n * https://github.com/marcj/css-element-queries\r\n */\r\n.chartjs-size-monitor,\r\n.chartjs-size-monitor-expand,\r\n.chartjs-size-monitor-shrink {\r\n\tposition: absolute;\r\n\tdirection: ltr;\r\n\tleft: 0;\r\n\ttop: 0;\r\n\tright: 0;\r\n\tbottom: 0;\r\n\toverflow: hidden;\r\n\tpointer-events: none;\r\n\tvisibility: hidden;\r\n\tz-index: -1;\r\n}\r\n\r\n.chartjs-size-monitor-expand > div {\r\n\tposition: absolute;\r\n\twidth: 1000000px;\r\n\theight: 1000000px;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n\r\n.chartjs-size-monitor-shrink > div {\r\n\tposition: absolute;\r\n\twidth: 200%;\r\n\theight: 200%;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n";
7407	
7408	var platform_dom$1 = /*#__PURE__*/Object.freeze({
7409	__proto__: null,
7410	'default': platform_dom
7411	});
7412	
7413	var stylesheet = getCjsExportFromNamespace(platform_dom$1);
7414	
7415	var EXPANDO_KEY = '$chartjs';
7416	var CSS_PREFIX = 'chartjs-';
7417	var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor';
7418	var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
7419	var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
7420	var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
7421	
7422	/**
7423	 * DOM event types -> Chart.js event types.
7424	 * Note: only events with different types are mapped.
7425	 * @see https://developer.mozilla.org/en-US/docs/Web/Events
7426	 */
7427	var EVENT_TYPES = {
7428		touchstart: 'mousedown',
7429		touchmove: 'mousemove',
7430		touchend: 'mouseup',
7431		pointerenter: 'mouseenter',
7432		pointerdown: 'mousedown',
7433		pointermove: 'mousemove',
7434		pointerup: 'mouseup',
7435		pointerleave: 'mouseout',
7436		pointerout: 'mouseout'
7437	};
7438	
7439	/**
7440	 * The "used" size is the final value of a dimension property after all calculations have
7441	 * been performed. This method uses the computed style of `element` but returns undefined
7442	 * if the computed style is not expressed in pixels. That can happen in some cases where
7443	 * `element` has a size relative to its parent and this last one is not yet displayed,
7444	 * for example because of `display: none` on a parent node.
7445	 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
7446	 * @returns {number} Size in pixels or undefined if unknown.
7447	 */
7448	function readUsedSize(element, property) {
7449		var value = helpers$1.getStyle(element, property);
7450		var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
7451		return matches ? Number(matches[1]) : undefined;
7452	}
7453	
7454	/**
7455	 * Initializes the canvas style and render size without modifying the canvas display size,
7456	 * since responsiveness is handled by the controller.resize() method. The config is used
7457	 * to determine the aspect ratio to apply in case no explicit height has been specified.
7458	 */
7459	function initCanvas(canvas, config) {
7460		var style = canvas.style;
7461	
7462		// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
7463		// returns null or '' if no explicit value has been set to the canvas attribute.
7464		var renderHeight = canvas.getAttribute('height');
7465		var renderWidth = canvas.getAttribute('width');
7466	
7467		// Chart.js modifies some canvas values that we want to restore on destroy
7468		canvas[EXPANDO_KEY] = {
7469			initial: {
7470				height: renderHeight,
7471				width: renderWidth,
7472				style: {
7473					display: style.display,
7474					height: style.height,
7475					width: style.width
7476				}
7477			}
7478		};
7479	
7480		// Force canvas to display as block to avoid extra space caused by inline
7481		// elements, which would interfere with the responsive resize process.
7482		// https://github.com/chartjs/Chart.js/issues/2538
7483		style.display = style.display || 'block';
7484	
7485		if (renderWidth === null || renderWidth === '') {
7486			var displayWidth = readUsedSize(canvas, 'width');
7487			if (displayWidth !== undefined) {
7488				canvas.width = displayWidth;
7489			}
7490		}
7491	
7492		if (renderHeight === null || renderHeight === '') {
7493			if (canvas.style.height === '') {
7494				// If no explicit render height and style height, let's apply the aspect ratio,
7495				// which one can be specified by the user but also by charts as default option
7496				// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
7497				canvas.height = canvas.width / (config.options.aspectRatio || 2);
7498			} else {
7499				var displayHeight = readUsedSize(canvas, 'height');
7500				if (displayWidth !== undefined) {
7501					canvas.height = displayHeight;
7502				}
7503			}
7504		}
7505	
7506		return canvas;
7507	}
7508	
7509	/**
7510	 * Detects support for options object argument in addEventListener.
7511	 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
7512	 * @private
7513	 */
7514	var supportsEventListenerOptions = (function() {
7515		var supports = false;
7516		try {
7517			var options = Object.defineProperty({}, 'passive', {
7518				// eslint-disable-next-line getter-return
7519				get: function() {
7520					supports = true;
7521				}
7522			});
7523			window.addEventListener('e', null, options);
7524		} catch (e) {
7525			// continue regardless of error
7526		}
7527		return supports;
7528	}());
7529	
7530	// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
7531	// https://github.com/chartjs/Chart.js/issues/4287
7532	var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
7533	
7534	function addListener(node, type, listener) {
7535		node.addEventListener(type, listener, eventListenerOptions);
7536	}
7537	
7538	function removeListener(node, type, listener) {
7539		node.removeEventListener(type, listener, eventListenerOptions);
7540	}
7541	
7542	function createEvent(type, chart, x, y, nativeEvent) {
7543		return {
7544			type: type,
7545			chart: chart,
7546			native: nativeEvent || null,
7547			x: x !== undefined ? x : null,
7548			y: y !== undefined ? y : null,
7549		};
7550	}
7551	
7552	function fromNativeEvent(event, chart) {
7553		var type = EVENT_TYPES[event.type] || event.type;
7554		var pos = helpers$1.getRelativePosition(event, chart);
7555		return createEvent(type, chart, pos.x, pos.y, event);
7556	}
7557	
7558	function throttled(fn, thisArg) {
7559		var ticking = false;
7560		var args = [];
7561	
7562		return function() {
7563			args = Array.prototype.slice.call(arguments);
7564			thisArg = thisArg || this;
7565	
7566			if (!ticking) {
7567				ticking = true;
7568				helpers$1.requestAnimFrame.call(window, function() {
7569					ticking = false;
7570					fn.apply(thisArg, args);
7571				});
7572			}
7573		};
7574	}
7575	
7576	function createDiv(cls) {
7577		var el = document.createElement('div');
7578		el.className = cls || '';
7579		return el;
7580	}
7581	
7582	// Implementation based on https://github.com/marcj/css-element-queries
7583	function createResizer(handler) {
7584		var maxSize = 1000000;
7585	
7586		// NOTE(SB) Don't use innerHTML because it could be considered unsafe.
7587		// https://github.com/chartjs/Chart.js/issues/5902
7588		var resizer = createDiv(CSS_SIZE_MONITOR);
7589		var expand = createDiv(CSS_SIZE_MONITOR + '-expand');
7590		var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink');
7591	
7592		expand.appendChild(createDiv());
7593		shrink.appendChild(createDiv());
7594	
7595		resizer.appendChild(expand);
7596		resizer.appendChild(shrink);
7597		resizer._reset = function() {
7598			expand.scrollLeft = maxSize;
7599			expand.scrollTop = maxSize;
7600			shrink.scrollLeft = maxSize;
7601			shrink.scrollTop = maxSize;
7602		};
7603	
7604		var onScroll = function() {
7605			resizer._reset();
7606			handler();
7607		};
7608	
7609		addListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
7610		addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
7611	
7612		return resizer;
7613	}
7614	
7615	// https://davidwalsh.name/detect-node-insertion
7616	function watchForRender(node, handler) {
7617		var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
7618		var proxy = expando.renderProxy = function(e) {
7619			if (e.animationName === CSS_RENDER_ANIMATION) {
7620				handler();
7621			}
7622		};
7623	
7624		helpers$1.each(ANIMATION_START_EVENTS, function(type) {
7625			addListener(node, type, proxy);
7626		});
7627	
7628		// #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
7629		// is removed then added back immediately (same animation frame?). Accessing the
7630		// `offsetParent` property will force a reflow and re-evaluate the CSS animation.
7631		// https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
7632		// https://github.com/chartjs/Chart.js/issues/4737
7633		expando.reflow = !!node.offsetParent;
7634	
7635		node.classList.add(CSS_RENDER_MONITOR);
7636	}
7637	
7638	function unwatchForRender(node) {
7639		var expando = node[EXPANDO_KEY] || {};
7640		var proxy = expando.renderProxy;
7641	
7642		if (proxy) {
7643			helpers$1.each(ANIMATION_START_EVENTS, function(type) {
7644				removeListener(node, type, proxy);
7645			});
7646	
7647			delete expando.renderProxy;
7648		}
7649	
7650		node.classList.remove(CSS_RENDER_MONITOR);
7651	}
7652	
7653	function addResizeListener(node, listener, chart) {
7654		var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
7655	
7656		// Let's keep track of this added resizer and thus avoid DOM query when removing it.
7657		var resizer = expando.resizer = createResizer(throttled(function() {
7658			if (expando.resizer) {
7659				var container = chart.options.maintainAspectRatio && node.parentNode;
7660				var w = container ? container.clientWidth : 0;
7661				listener(createEvent('resize', chart));
7662				if (container && container.clientWidth < w && chart.canvas) {
7663					// If the container size shrank during chart resize, let's assume
7664					// scrollbar appeared. So we resize again with the scrollbar visible -
7665					// effectively making chart smaller and the scrollbar hidden again.
7666					// Because we are inside `throttled`, and currently `ticking`, scroll
7667					// events are ignored during this whole 2 resize process.
7668					// If we assumed wrong and something else happened, we are resizing
7669					// twice in a frame (potential performance issue)
7670					listener(createEvent('resize', chart));
7671				}
7672			}
7673		}));
7674	
7675		// The resizer needs to be attached to the node parent, so we first need to be
7676		// sure that `node` is attached to the DOM before injecting the resizer element.
7677		watchForRender(node, function() {
7678			if (expando.resizer) {
7679				var container = node.parentNode;
7680				if (container && container !== resizer.parentNode) {
7681					container.insertBefore(resizer, container.firstChild);
7682				}
7683	
7684				// The container size might have changed, let's reset the resizer state.
7685				resizer._reset();
7686			}
7687		});
7688	}
7689	
7690	function removeResizeListener(node) {
7691		var expando = node[EXPANDO_KEY] || {};
7692		var resizer = expando.resizer;
7693	
7694		delete expando.resizer;
7695		unwatchForRender(node);
7696	
7697		if (resizer && resizer.parentNode) {
7698			resizer.parentNode.removeChild(resizer);
7699		}
7700	}
7701	
7702	/**
7703	 * Injects CSS styles inline if the styles are not already present.
7704	 * @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the <style>.
7705	 * @param {string} css - the CSS to be injected.
7706	 */
7707	function injectCSS(rootNode, css) {
7708		// https://stackoverflow.com/q/3922139
7709		var expando = rootNode[EXPANDO_KEY] || (rootNode[EXPANDO_KEY] = {});
7710		if (!expando.containsStyles) {
7711			expando.containsStyles = true;
7712			css = '/* Chart.js */\n' + css;
7713			var style = document.createElement('style');
7714			style.setAttribute('type', 'text/css');
7715			style.appendChild(document.createTextNode(css));
7716			rootNode.appendChild(style);
7717		}
7718	}
7719	
7720	var platform_dom$2 = {
7721		/**
7722		 * When `true`, prevents the automatic injection of the stylesheet required to
7723		 * correctly detect when the chart is added to the DOM and then resized. This
7724		 * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
7725		 * to be manually imported to make this library compatible with any CSP.
7726		 * See https://github.com/chartjs/Chart.js/issues/5208
7727		 */
7728		disableCSSInjection: false,
7729	
7730		/**
7731		 * This property holds whether this platform is enabled for the current environment.
7732		 * Currently used by platform.js to select the proper implementation.
7733		 * @private
7734		 */
7735		_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
7736	
7737		/**
7738		 * Initializes resources that depend on platform options.
7739		 * @param {HTMLCanvasElement} canvas - The Canvas element.
7740		 * @private
7741		 */
7742		_ensureLoaded: function(canvas) {
7743			if (!this.disableCSSInjection) {
7744				// If the canvas is in a shadow DOM, then the styles must also be inserted
7745				// into the same shadow DOM.
7746				// https://github.com/chartjs/Chart.js/issues/5763
7747				var root = canvas.getRootNode ? canvas.getRootNode() : document;
7748				var targetNode = root.host ? root : document.head;
7749				injectCSS(targetNode, stylesheet);
7750			}
7751		},
7752	
7753		acquireContext: function(item, config) {
7754			if (typeof item === 'string') {
7755				item = document.getElementById(item);
7756			} else if (item.length) {
7757				// Support for array based queries (such as jQuery)
7758				item = item[0];
7759			}
7760	
7761			if (item && item.canvas) {
7762				// Support for any object associated to a canvas (including a context2d)
7763				item = item.canvas;
7764			}
7765	
7766			// To prevent canvas fingerprinting, some add-ons undefine the getContext
7767			// method, for example: https://github.com/kkapsner/CanvasBlocker
7768			// https://github.com/chartjs/Chart.js/issues/2807
7769			var context = item && item.getContext && item.getContext('2d');
7770	
7771			// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
7772			// inside an iframe or when running in a protected environment. We could guess the
7773			// types from their toString() value but let's keep things flexible and assume it's
7774			// a sufficient condition if the item has a context2D which has item as `canvas`.
7775			// https://github.com/chartjs/Chart.js/issues/3887
7776			// https://github.com/chartjs/Chart.js/issues/4102
7777			// https://github.com/chartjs/Chart.js/issues/4152
7778			if (context && context.canvas === item) {
7779				// Load platform resources on first chart creation, to make it possible to
7780				// import the library before setting platform options.
7781				this._ensureLoaded(item);
7782				initCanvas(item, config);
7783				return context;
7784			}
7785	
7786			return null;
7787		},
7788	
7789		releaseContext: function(context) {
7790			var canvas = context.canvas;
7791			if (!canvas[EXPANDO_KEY]) {
7792				return;
7793			}
7794	
7795			var initial = canvas[EXPANDO_KEY].initial;
7796			['height', 'width'].forEach(function(prop) {
7797				var value = initial[prop];
7798				if (helpers$1.isNullOrUndef(value)) {
7799					canvas.removeAttribute(prop);
7800				} else {
7801					canvas.setAttribute(prop, value);
7802				}
7803			});
7804	
7805			helpers$1.each(initial.style || {}, function(value, key) {
7806				canvas.style[key] = value;
7807			});
7808	
7809			// The canvas render size might have been changed (and thus the state stack discarded),
7810			// we can't use save() and restore() to restore the initial state. So make sure that at
7811			// least the canvas context is reset to the default state by setting the canvas width.
7812			// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
7813			// eslint-disable-next-line no-self-assign
7814			canvas.width = canvas.width;
7815	
7816			delete canvas[EXPANDO_KEY];
7817		},
7818	
7819		addEventListener: function(chart, type, listener) {
7820			var canvas = chart.canvas;
7821			if (type === 'resize') {
7822				// Note: the resize event is not supported on all browsers.
7823				addResizeListener(canvas, listener, chart);
7824				return;
7825			}
7826	
7827			var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
7828			var proxies = expando.proxies || (expando.proxies = {});
7829			var proxy = proxies[chart.id + '_' + type] = function(event) {
7830				listener(fromNativeEvent(event, chart));
7831			};
7832	
7833			addListener(canvas, type, proxy);
7834		},
7835	
7836		removeEventListener: function(chart, type, listener) {
7837			var canvas = chart.canvas;
7838			if (type === 'resize') {
7839				// Note: the resize event is not supported on all browsers.
7840				removeResizeListener(canvas);
7841				return;
7842			}
7843	
7844			var expando = listener[EXPANDO_KEY] || {};
7845			var proxies = expando.proxies || {};
7846			var proxy = proxies[chart.id + '_' + type];
7847			if (!proxy) {
7848				return;
7849			}
7850	
7851			removeListener(canvas, type, proxy);
7852		}
7853	};
7854	
7855	// DEPRECATIONS
7856	
7857	/**
7858	 * Provided for backward compatibility, use EventTarget.addEventListener instead.
7859	 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
7860	 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
7861	 * @function Chart.helpers.addEvent
7862	 * @deprecated since version 2.7.0
7863	 * @todo remove at version 3
7864	 * @private
7865	 */
7866	helpers$1.addEvent = addListener;
7867	
7868	/**
7869	 * Provided for backward compatibility, use EventTarget.removeEventListener instead.
7870	 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
7871	 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
7872	 * @function Chart.helpers.removeEvent
7873	 * @deprecated since version 2.7.0
7874	 * @todo remove at version 3
7875	 * @private
7876	 */
7877	helpers$1.removeEvent = removeListener;
7878	
7879	// @TODO Make possible to select another platform at build time.
7880	var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic;
7881	
7882	/**
7883	 * @namespace Chart.platform
7884	 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
7885	 * @since 2.4.0
7886	 */
7887	var platform = helpers$1.extend({
7888		/**
7889		 * @since 2.7.0
7890		 */
7891		initialize: function() {},
7892	
7893		/**
7894		 * Called at chart construction time, returns a context2d instance implementing
7895		 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
7896		 * @param {*} item - The native item from which to acquire context (platform specific)
7897		 * @param {object} options - The chart options
7898		 * @returns {CanvasRenderingContext2D} context2d instance
7899		 */
7900		acquireContext: function() {},
7901	
7902		/**
7903		 * Called at chart destruction time, releases any resources associated to the context
7904		 * previously returned by the acquireContext() method.
7905		 * @param {CanvasRenderingContext2D} context - The context2d instance
7906		 * @returns {boolean} true if the method succeeded, else false
7907		 */
7908		releaseContext: function() {},
7909	
7910		/**
7911		 * Registers the specified listener on the given chart.
7912		 * @param {Chart} chart - Chart from which to listen for event
7913		 * @param {string} type - The ({@link IEvent}) type to listen for
7914		 * @param {function} listener - Receives a notification (an object that implements
7915		 * the {@link IEvent} interface) when an event of the specified type occurs.
7916		 */
7917		addEventListener: function() {},
7918	
7919		/**
7920		 * Removes the specified listener previously registered with addEventListener.
7921		 * @param {Chart} chart - Chart from which to remove the listener
7922		 * @param {string} type - The ({@link IEvent}) type to remove
7923		 * @param {function} listener - The listener function to remove from the event target.
7924		 */
7925		removeEventListener: function() {}
7926	
7927	}, implementation);
7928	
7929	core_defaults._set('global', {
7930		plugins: {}
7931	});
7932	
7933	/**
7934	 * The plugin service singleton
7935	 * @namespace Chart.plugins
7936	 * @since 2.1.0
7937	 */
7938	var core_plugins = {
7939		/**
7940		 * Globally registered plugins.
7941		 * @private
7942		 */
7943		_plugins: [],
7944	
7945		/**
7946		 * This identifier is used to invalidate the descriptors cache attached to each chart
7947		 * when a global plugin is registered or unregistered. In this case, the cache ID is
7948		 * incremented and descriptors are regenerated during following API calls.
7949		 * @private
7950		 */
7951		_cacheId: 0,
7952	
7953		/**
7954		 * Registers the given plugin(s) if not already registered.
7955		 * @param {IPlugin[]|IPlugin} plugins plugin instance(s).
7956		 */
7957		register: function(plugins) {
7958			var p = this._plugins;
7959			([]).concat(plugins).forEach(function(plugin) {
7960				if (p.indexOf(plugin) === -1) {
7961					p.push(plugin);
7962				}
7963			});
7964	
7965			this._cacheId++;
7966		},
7967	
7968		/**
7969		 * Unregisters the given plugin(s) only if registered.
7970		 * @param {IPlugin[]|IPlugin} plugins plugin instance(s).
7971		 */
7972		unregister: function(plugins) {
7973			var p = this._plugins;
7974			([]).concat(plugins).forEach(function(plugin) {
7975				var idx = p.indexOf(plugin);
7976				if (idx !== -1) {
7977					p.splice(idx, 1);
7978				}
7979			});
7980	
7981			this._cacheId++;
7982		},
7983	
7984		/**
7985		 * Remove all registered plugins.
7986		 * @since 2.1.5
7987		 */
7988		clear: function() {
7989			this._plugins = [];
7990			this._cacheId++;
7991		},
7992	
7993		/**
7994		 * Returns the number of registered plugins?
7995		 * @returns {number}
7996		 * @since 2.1.5
7997		 */
7998		count: function() {
7999			return this._plugins.length;
8000		},
8001	
8002		/**
8003		 * Returns all registered plugin instances.
8004		 * @returns {IPlugin[]} array of plugin objects.
8005		 * @since 2.1.5
8006		 */
8007		getAll: function() {
8008			return this._plugins;
8009		},
8010	
8011		/**
8012		 * Calls enabled plugins for `chart` on the specified hook and with the given args.
8013		 * This method immediately returns as soon as a plugin explicitly returns false. The
8014		 * returned value can be used, for instance, to interrupt the current action.
8015		 * @param {Chart} chart - The chart instance for which plugins should be called.
8016		 * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
8017		 * @param {Array} [args] - Extra arguments to apply to the hook call.
8018		 * @returns {boolean} false if any of the plugins return false, else returns true.
8019		 */
8020		notify: function(chart, hook, args) {
8021			var descriptors = this.descriptors(chart);
8022			var ilen = descriptors.length;
8023			var i, descriptor, plugin, params, method;
8024	
8025			for (i = 0; i < ilen; ++i) {
8026				descriptor = descriptors[i];
8027				plugin = descriptor.plugin;
8028				method = plugin[hook];
8029				if (typeof method === 'function') {
8030					params = [chart].concat(args || []);
8031					params.push(descriptor.options);
8032					if (method.apply(plugin, params) === false) {
8033						return false;
8034					}
8035				}
8036			}
8037	
8038			return true;
8039		},
8040	
8041		/**
8042		 * Returns descriptors of enabled plugins for the given chart.
8043		 * @returns {object[]} [{ plugin, options }]
8044		 * @private
8045		 */
8046		descriptors: function(chart) {
8047			var cache = chart.$plugins || (chart.$plugins = {});
8048			if (cache.id === this._cacheId) {
8049				return cache.descriptors;
8050			}
8051	
8052			var plugins = [];
8053			var descriptors = [];
8054			var config = (chart && chart.config) || {};
8055			var options = (config.options && config.options.plugins) || {};
8056	
8057			this._plugins.concat(config.plugins || []).forEach(function(plugin) {
8058				var idx = plugins.indexOf(plugin);
8059				if (idx !== -1) {
8060					return;
8061				}
8062	
8063				var id = plugin.id;
8064				var opts = options[id];
8065				if (opts === false) {
8066					return;
8067				}
8068	
8069				if (opts === true) {
8070					opts = helpers$1.clone(core_defaults.global.plugins[id]);
8071				}
8072	
8073				plugins.push(plugin);
8074				descriptors.push({
8075					plugin: plugin,
8076					options: opts || {}
8077				});
8078			});
8079	
8080			cache.descriptors = descriptors;
8081			cache.id = this._cacheId;
8082			return descriptors;
8083		},
8084	
8085		/**
8086		 * Invalidates cache for the given chart: descriptors hold a reference on plugin option,
8087		 * but in some cases, this reference can be changed by the user when updating options.
8088		 * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
8089		 * @private
8090		 */
8091		_invalidate: function(chart) {
8092			delete chart.$plugins;
8093		}
8094	};
8095	
8096	var core_scaleService = {
8097		// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
8098		// use the new chart options to grab the correct scale
8099		constructors: {},
8100		// Use a registration function so that we can move to an ES6 map when we no longer need to support
8101		// old browsers
8102	
8103		// Scale config defaults
8104		defaults: {},
8105		registerScaleType: function(type, scaleConstructor, scaleDefaults) {
8106			this.constructors[type] = scaleConstructor;
8107			this.defaults[type] = helpers$1.clone(scaleDefaults);
8108		},
8109		getScaleConstructor: function(type) {
8110			return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
8111		},
8112		getScaleDefaults: function(type) {
8113			// Return the scale defaults merged with the global settings so that we always use the latest ones
8114			return this.defaults.hasOwnProperty(type) ? helpers$1.merge(Object.create(null), [core_defaults.scale, this.defaults[type]]) : {};
8115		},
8116		updateScaleDefaults: function(type, additions) {
8117			var me = this;
8118			if (me.defaults.hasOwnProperty(type)) {
8119				me.defaults[type] = helpers$1.extend(me.defaults[type], additions);
8120			}
8121		},
8122		addScalesToLayout: function(chart) {
8123			// Adds each scale to the chart.boxes array to be sized accordingly
8124			helpers$1.each(chart.scales, function(scale) {
8125				// Set ILayoutItem parameters for backwards compatibility
8126				scale.fullWidth = scale.options.fullWidth;
8127				scale.position = scale.options.position;
8128				scale.weight = scale.options.weight;
8129				core_layouts.addBox(chart, scale);
8130			});
8131		}
8132	};
8133	
8134	var valueOrDefault$8 = helpers$1.valueOrDefault;
8135	var getRtlHelper = helpers$1.rtl.getRtlAdapter;
8136	
8137	core_defaults._set('global', {
8138		tooltips: {
8139			enabled: true,
8140			custom: null,
8141			mode: 'nearest',
8142			position: 'average',
8143			intersect: true,
8144			backgroundColor: 'rgba(0,0,0,0.8)',
8145			titleFontStyle: 'bold',
8146			titleSpacing: 2,
8147			titleMarginBottom: 6,
8148			titleFontColor: '#fff',
8149			titleAlign: 'left',
8150			bodySpacing: 2,
8151			bodyFontColor: '#fff',
8152			bodyAlign: 'left',
8153			footerFontStyle: 'bold',
8154			footerSpacing: 2,
8155			footerMarginTop: 6,
8156			footerFontColor: '#fff',
8157			footerAlign: 'left',
8158			yPadding: 6,
8159			xPadding: 6,
8160			caretPadding: 2,
8161			caretSize: 5,
8162			cornerRadius: 6,
8163			multiKeyBackground: '#fff',
8164			displayColors: true,
8165			borderColor: 'rgba(0,0,0,0)',
8166			borderWidth: 0,
8167			callbacks: {
8168				// Args are: (tooltipItems, data)
8169				beforeTitle: helpers$1.noop,
8170				title: function(tooltipItems, data) {
8171					var title = '';
8172					var labels = data.labels;
8173					var labelCount = labels ? labels.length : 0;
8174	
8175					if (tooltipItems.length > 0) {
8176						var item = tooltipItems[0];
8177						if (item.label) {
8178							title = item.label;
8179						} else if (item.xLabel) {
8180							title = item.xLabel;
8181						} else if (labelCount > 0 && item.index < labelCount) {
8182							title = labels[item.index];
8183						}
8184					}
8185	
8186					return title;
8187				},
8188				afterTitle: helpers$1.noop,
8189	
8190				// Args are: (tooltipItems, data)
8191				beforeBody: helpers$1.noop,
8192	
8193				// Args are: (tooltipItem, data)
8194				beforeLabel: helpers$1.noop,
8195				label: function(tooltipItem, data) {
8196					var label = data.datasets[tooltipItem.datasetIndex].label || '';
8197	
8198					if (label) {
8199						label += ': ';
8200					}
8201					if (!helpers$1.isNullOrUndef(tooltipItem.value)) {
8202						label += tooltipItem.value;
8203					} else {
8204						label += tooltipItem.yLabel;
8205					}
8206					return label;
8207				},
8208				labelColor: function(tooltipItem, chart) {
8209					var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
8210					var activeElement = meta.data[tooltipItem.index];
8211					var view = activeElement._view;
8212					return {
8213						borderColor: view.borderColor,
8214						backgroundColor: view.backgroundColor
8215					};
8216				},
8217				labelTextColor: function() {
8218					return this._options.bodyFontColor;
8219				},
8220				afterLabel: helpers$1.noop,
8221	
8222				// Args are: (tooltipItems, data)
8223				afterBody: helpers$1.noop,
8224	
8225				// Args are: (tooltipItems, data)
8226				beforeFooter: helpers$1.noop,
8227				footer: helpers$1.noop,
8228				afterFooter: helpers$1.noop
8229			}
8230		}
8231	});
8232	
8233	var positioners = {
8234		/**
8235		 * Average mode places the tooltip at the average position of the elements shown
8236		 * @function Chart.Tooltip.positioners.average
8237		 * @param elements {ChartElement[]} the elements being displayed in the tooltip
8238		 * @returns {object} tooltip position
8239		 */
8240		average: function(elements) {
8241			if (!elements.length) {
8242				return false;
8243			}
8244	
8245			var i, len;
8246			var x = 0;
8247			var y = 0;
8248			var count = 0;
8249	
8250			for (i = 0, len = elements.length; i < len; ++i) {
8251				var el = elements[i];
8252				if (el && el.hasValue()) {
8253					var pos = el.tooltipPosition();
8254					x += pos.x;
8255					y += pos.y;
8256					++count;
8257				}
8258			}
8259	
8260			return {
8261				x: x / count,
8262				y: y / count
8263			};
8264		},
8265	
8266		/**
8267		 * Gets the tooltip position nearest of the item nearest to the event position
8268		 * @function Chart.Tooltip.positioners.nearest
8269		 * @param elements {Chart.Element[]} the tooltip elements
8270		 * @param eventPosition {object} the position of the event in canvas coordinates
8271		 * @returns {object} the tooltip position
8272		 */
8273		nearest: function(elements, eventPosition) {
8274			var x = eventPosition.x;
8275			var y = eventPosition.y;
8276			var minDistance = Number.POSITIVE_INFINITY;
8277			var i, len, nearestElement;
8278	
8279			for (i = 0, len = elements.length; i < len; ++i) {
8280				var el = elements[i];
8281				if (el && el.hasValue()) {
8282					var center = el.getCenterPoint();
8283					var d = helpers$1.distanceBetweenPoints(eventPosition, center);
8284	
8285					if (d < minDistance) {
8286						minDistance = d;
8287						nearestElement = el;
8288					}
8289				}
8290			}
8291	
8292			if (nearestElement) {
8293				var tp = nearestElement.tooltipPosition();
8294				x = tp.x;
8295				y = tp.y;
8296			}
8297	
8298			return {
8299				x: x,
8300				y: y
8301			};
8302		}
8303	};
8304	
8305	// Helper to push or concat based on if the 2nd parameter is an array or not
8306	function pushOrConcat(base, toPush) {
8307		if (toPush) {
8308			if (helpers$1.isArray(toPush)) {
8309				// base = base.concat(toPush);
8310				Array.prototype.push.apply(base, toPush);
8311			} else {
8312				base.push(toPush);
8313			}
8314		}
8315	
8316		return base;
8317	}
8318	
8319	/**
8320	 * Returns array of strings split by newline
8321	 * @param {string} value - The value to split by newline.
8322	 * @returns {string[]} value if newline present - Returned from String split() method
8323	 * @function
8324	 */
8325	function splitNewlines(str) {
8326		if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
8327			return str.split('\n');
8328		}
8329		return str;
8330	}
8331	
8332	
8333	/**
8334	 * Private helper to create a tooltip item model
8335	 * @param element - the chart element (point, arc, bar) to create the tooltip item for
8336	 * @return new tooltip item
8337	 */
8338	function createTooltipItem(element) {
8339		var xScale = element._xScale;
8340		var yScale = element._yScale || element._scale; // handle radar || polarArea charts
8341		var index = element._index;
8342		var datasetIndex = element._datasetIndex;
8343		var controller = element._chart.getDatasetMeta(datasetIndex).controller;
8344		var indexScale = controller._getIndexScale();
8345		var valueScale = controller._getValueScale();
8346	
8347		return {
8348			xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
8349			yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
8350			label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '',
8351			value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '',
8352			index: index,
8353			datasetIndex: datasetIndex,
8354			x: element._model.x,
8355			y: element._model.y
8356		};
8357	}
8358	
8359	/**
8360	 * Helper to get the reset model for the tooltip
8361	 * @param tooltipOpts {object} the tooltip options
8362	 */
8363	function getBaseModel(tooltipOpts) {
8364		var globalDefaults = core_defaults.global;
8365	
8366		return {
8367			// Positioning
8368			xPadding: tooltipOpts.xPadding,
8369			yPadding: tooltipOpts.yPadding,
8370			xAlign: tooltipOpts.xAlign,
8371			yAlign: tooltipOpts.yAlign,
8372	
8373			// Drawing direction and text direction
8374			rtl: tooltipOpts.rtl,
8375			textDirection: tooltipOpts.textDirection,
8376	
8377			// Body
8378			bodyFontColor: tooltipOpts.bodyFontColor,
8379			_bodyFontFamily: valueOrDefault$8(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
8380			_bodyFontStyle: valueOrDefault$8(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
8381			_bodyAlign: tooltipOpts.bodyAlign,
8382			bodyFontSize: valueOrDefault$8(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
8383			bodySpacing: tooltipOpts.bodySpacing,
8384	
8385			// Title
8386			titleFontColor: tooltipOpts.titleFontColor,
8387			_titleFontFamily: valueOrDefault$8(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
8388			_titleFontStyle: valueOrDefault$8(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
8389			titleFontSize: valueOrDefault$8(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
8390			_titleAlign: tooltipOpts.titleAlign,
8391			titleSpacing: tooltipOpts.titleSpacing,
8392			titleMarginBottom: tooltipOpts.titleMarginBottom,
8393	
8394			// Footer
8395			footerFontColor: tooltipOpts.footerFontColor,
8396			_footerFontFamily: valueOrDefault$8(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
8397			_footerFontStyle: valueOrDefault$8(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
8398			footerFontSize: valueOrDefault$8(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
8399			_footerAlign: tooltipOpts.footerAlign,
8400			footerSpacing: tooltipOpts.footerSpacing,
8401			footerMarginTop: tooltipOpts.footerMarginTop,
8402	
8403			// Appearance
8404			caretSize: tooltipOpts.caretSize,
8405			cornerRadius: tooltipOpts.cornerRadius,
8406			backgroundColor: tooltipOpts.backgroundColor,
8407			opacity: 0,
8408			legendColorBackground: tooltipOpts.multiKeyBackground,
8409			displayColors: tooltipOpts.displayColors,
8410			borderColor: tooltipOpts.borderColor,
8411			borderWidth: tooltipOpts.borderWidth
8412		};
8413	}
8414	
8415	/**
8416	 * Get the size of the tooltip
8417	 */
8418	function getTooltipSize(tooltip, model) {
8419		var ctx = tooltip._chart.ctx;
8420	
8421		var height = model.yPadding * 2; // Tooltip Padding
8422		var width = 0;
8423	
8424		// Count of all lines in the body
8425		var body = model.body;
8426		var combinedBodyLength = body.reduce(function(count, bodyItem) {
8427			return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
8428		}, 0);
8429		combinedBodyLength += model.beforeBody.length + model.afterBody.length;
8430	
8431		var titleLineCount = model.title.length;
8432		var footerLineCount = model.footer.length;
8433		var titleFontSize = model.titleFontSize;
8434		var bodyFontSize = model.bodyFontSize;
8435		var footerFontSize = model.footerFontSize;
8436	
8437		height += titleLineCount * titleFontSize; // Title Lines
8438		height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
8439		height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
8440		height += combinedBodyLength * bodyFontSize; // Body Lines
8441		height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
8442		height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
8443		height += footerLineCount * (footerFontSize); // Footer Lines
8444		height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
8445	
8446		// Title width
8447		var widthPadding = 0;
8448		var maxLineWidth = function(line) {
8449			width = Math.max(width, ctx.measureText(line).width + widthPadding);
8450		};
8451	
8452		ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
8453		helpers$1.each(model.title, maxLineWidth);
8454	
8455		// Body width
8456		ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
8457		helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
8458	
8459		// Body lines may include some extra width due to the color box
8460		widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
8461		helpers$1.each(body, function(bodyItem) {
8462			helpers$1.each(bodyItem.before, maxLineWidth);
8463			helpers$1.each(bodyItem.lines, maxLineWidth);
8464			helpers$1.each(bodyItem.after, maxLineWidth);
8465		});
8466	
8467		// Reset back to 0
8468		widthPadding = 0;
8469	
8470		// Footer width
8471		ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
8472		helpers$1.each(model.footer, maxLineWidth);
8473	
8474		// Add padding
8475		width += 2 * model.xPadding;
8476	
8477		return {
8478			width: width,
8479			height: height
8480		};
8481	}
8482	
8483	/**
8484	 * Helper to get the alignment of a tooltip given the size
8485	 */
8486	function determineAlignment(tooltip, size) {
8487		var model = tooltip._model;
8488		var chart = tooltip._chart;
8489		var chartArea = tooltip._chart.chartArea;
8490		var xAlign = 'center';
8491		var yAlign = 'center';
8492	
8493		if (model.y < size.height) {
8494			yAlign = 'top';
8495		} else if (model.y > (chart.height - size.height)) {
8496			yAlign = 'bottom';
8497		}
8498	
8499		var lf, rf; // functions to determine left, right alignment
8500		var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
8501		var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
8502		var midX = (chartArea.left + chartArea.right) / 2;
8503		var midY = (chartArea.top + chartArea.bottom) / 2;
8504	
8505		if (yAlign === 'center') {
8506			lf = function(x) {
8507				return x <= midX;
8508			};
8509			rf = function(x) {
8510				return x > midX;
8511			};
8512		} else {
8513			lf = function(x) {
8514				return x <= (size.width / 2);
8515			};
8516			rf = function(x) {
8517				return x >= (chart.width - (size.width / 2));
8518			};
8519		}
8520	
8521		olf = function(x) {
8522			return x + size.width + model.caretSize + model.caretPadding > chart.width;
8523		};
8524		orf = function(x) {
8525			return x - size.width - model.caretSize - model.caretPadding < 0;
8526		};
8527		yf = function(y) {
8528			return y <= midY ? 'top' : 'bottom';
8529		};
8530	
8531		if (lf(model.x)) {
8532			xAlign = 'left';
8533	
8534			// Is tooltip too wide and goes over the right side of the chart.?
8535			if (olf(model.x)) {
8536				xAlign = 'center';
8537				yAlign = yf(model.y);
8538			}
8539		} else if (rf(model.x)) {
8540			xAlign = 'right';
8541	
8542			// Is tooltip too wide and goes outside left edge of canvas?
8543			if (orf(model.x)) {
8544				xAlign = 'center';
8545				yAlign = yf(model.y);
8546			}
8547		}
8548	
8549		var opts = tooltip._options;
8550		return {
8551			xAlign: opts.xAlign ? opts.xAlign : xAlign,
8552			yAlign: opts.yAlign ? opts.yAlign : yAlign
8553		};
8554	}
8555	
8556	/**
8557	 * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
8558	 */
8559	function getBackgroundPoint(vm, size, alignment, chart) {
8560		// Background Position
8561		var x = vm.x;
8562		var y = vm.y;
8563	
8564		var caretSize = vm.caretSize;
8565		var caretPadding = vm.caretPadding;
8566		var cornerRadius = vm.cornerRadius;
8567		var xAlign = alignment.xAlign;
8568		var yAlign = alignment.yAlign;
8569		var paddingAndSize = caretSize + caretPadding;
8570		var radiusAndPadding = cornerRadius + caretPadding;
8571	
8572		if (xAlign === 'right') {
8573			x -= size.width;
8574		} else if (xAlign === 'center') {
8575			x -= (size.width / 2);
8576			if (x + size.width > chart.width) {
8577				x = chart.width - size.width;
8578			}
8579			if (x < 0) {
8580				x = 0;
8581			}
8582		}
8583	
8584		if (yAlign === 'top') {
8585			y += paddingAndSize;
8586		} else if (yAlign === 'bottom') {
8587			y -= size.height + paddingAndSize;
8588		} else {
8589			y -= (size.height / 2);
8590		}
8591	
8592		if (yAlign === 'center') {
8593			if (xAlign === 'left') {
8594				x += paddingAndSize;
8595			} else if (xAlign === 'right') {
8596				x -= paddingAndSize;
8597			}
8598		} else if (xAlign === 'left') {
8599			x -= radiusAndPadding;
8600		} else if (xAlign === 'right') {
8601			x += radiusAndPadding;
8602		}
8603	
8604		return {
8605			x: x,
8606			y: y
8607		};
8608	}
8609	
8610	function getAlignedX(vm, align) {
8611		return align === 'center'
8612			? vm.x + vm.width / 2
8613			: align === 'right'
8614				? vm.x + vm.width - vm.xPadding
8615				: vm.x + vm.xPadding;
8616	}
8617	
8618	/**
8619	 * Helper to build before and after body lines
8620	 */
8621	function getBeforeAfterBodyLines(callback) {
8622		return pushOrConcat([], splitNewlines(callback));
8623	}
8624	
8625	var exports$4 = core_element.extend({
8626		initialize: function() {
8627			this._model = getBaseModel(this._options);
8628			this._lastActive = [];
8629		},
8630	
8631		// Get the title
8632		// Args are: (tooltipItem, data)
8633		getTitle: function() {
8634			var me = this;
8635			var opts = me._options;
8636			var callbacks = opts.callbacks;
8637	
8638			var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
8639			var title = callbacks.title.apply(me, arguments);
8640			var afterTitle = callbacks.afterTitle.apply(me, arguments);
8641	
8642			var lines = [];
8643			lines = pushOrConcat(lines, splitNewlines(beforeTitle));
8644			lines = pushOrConcat(lines, splitNewlines(title));
8645			lines = pushOrConcat(lines, splitNewlines(afterTitle));
8646	
8647			return lines;
8648		},
8649	
8650		// Args are: (tooltipItem, data)
8651		getBeforeBody: function() {
8652			return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
8653		},
8654	
8655		// Args are: (tooltipItem, data)
8656		getBody: function(tooltipItems, data) {
8657			var me = this;
8658			var callbacks = me._options.callbacks;
8659			var bodyItems = [];
8660	
8661			helpers$1.each(tooltipItems, function(tooltipItem) {
8662				var bodyItem = {
8663					before: [],
8664					lines: [],
8665					after: []
8666				};
8667				pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
8668				pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
8669				pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));
8670	
8671				bodyItems.push(bodyItem);
8672			});
8673	
8674			return bodyItems;
8675		},
8676	
8677		// Args are: (tooltipItem, data)
8678		getAfterBody: function() {
8679			return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
8680		},
8681	
8682		// Get the footer and beforeFooter and afterFooter lines
8683		// Args are: (tooltipItem, data)
8684		getFooter: function() {
8685			var me = this;
8686			var callbacks = me._options.callbacks;
8687	
8688			var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
8689			var footer = callbacks.footer.apply(me, arguments);
8690			var afterFooter = callbacks.afterFooter.apply(me, arguments);
8691	
8692			var lines = [];
8693			lines = pushOrConcat(lines, splitNewlines(beforeFooter));
8694			lines = pushOrConcat(lines, splitNewlines(footer));
8695			lines = pushOrConcat(lines, splitNewlines(afterFooter));
8696	
8697			return lines;
8698		},
8699	
8700		update: function(changed) {
8701			var me = this;
8702			var opts = me._options;
8703	
8704			// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
8705			// 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
8706			// which breaks any animations.
8707			var existingModel = me._model;
8708			var model = me._model = getBaseModel(opts);
8709			var active = me._active;
8710	
8711			var data = me._data;
8712	
8713			// In the case where active.length === 0 we need to keep these at existing values for good animations
8714			var alignment = {
8715				xAlign: existingModel.xAlign,
8716				yAlign: existingModel.yAlign
8717			};
8718			var backgroundPoint = {
8719				x: existingModel.x,
8720				y: existingModel.y
8721			};
8722			var tooltipSize = {
8723				width: existingModel.width,
8724				height: existingModel.height
8725			};
8726			var tooltipPosition = {
8727				x: existingModel.caretX,
8728				y: existingModel.caretY
8729			};
8730	
8731			var i, len;
8732	
8733			if (active.length) {
8734				model.opacity = 1;
8735	
8736				var labelColors = [];
8737				var labelTextColors = [];
8738				tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);
8739	
8740				var tooltipItems = [];
8741				for (i = 0, len = active.length; i < len; ++i) {
8742					tooltipItems.push(createTooltipItem(active[i]));
8743				}
8744	
8745				// If the user provided a filter function, use it to modify the tooltip items
8746				if (opts.filter) {
8747					tooltipItems = tooltipItems.filter(function(a) {
8748						return opts.filter(a, data);
8749					});
8750				}
8751	
8752				// If the user provided a sorting function, use it to modify the tooltip items
8753				if (opts.itemSort) {
8754					tooltipItems = tooltipItems.sort(function(a, b) {
8755						return opts.itemSort(a, b, data);
8756					});
8757				}
8758	
8759				// Determine colors for boxes
8760				helpers$1.each(tooltipItems, function(tooltipItem) {
8761					labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
8762					labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
8763				});
8764	
8765	
8766				// Build the Text Lines
8767				model.title = me.getTitle(tooltipItems, data);
8768				model.beforeBody = me.getBeforeBody(tooltipItems, data);
8769				model.body = me.getBody(tooltipItems, data);
8770				model.afterBody = me.getAfterBody(tooltipItems, data);
8771				model.footer = me.getFooter(tooltipItems, data);
8772	
8773				// Initial positioning and colors
8774				model.x = tooltipPosition.x;
8775				model.y = tooltipPosition.y;
8776				model.caretPadding = opts.caretPadding;
8777				model.labelColors = labelColors;
8778				model.labelTextColors = labelTextColors;
8779	
8780				// data points
8781				model.dataPoints = tooltipItems;
8782	
8783				// We need to determine alignment of the tooltip
8784				tooltipSize = getTooltipSize(this, model);
8785				alignment = determineAlignment(this, tooltipSize);
8786				// Final Size and Position
8787				backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
8788			} else {
8789				model.opacity = 0;
8790			}
8791	
8792			model.xAlign = alignment.xAlign;
8793			model.yAlign = alignment.yAlign;
8794			model.x = backgroundPoint.x;
8795			model.y = backgroundPoint.y;
8796			model.width = tooltipSize.width;
8797			model.height = tooltipSize.height;
8798	
8799			// Point where the caret on the tooltip points to
8800			model.caretX = tooltipPosition.x;
8801			model.caretY = tooltipPosition.y;
8802	
8803			me._model = model;
8804	
8805			if (changed && opts.custom) {
8806				opts.custom.call(me, model);
8807			}
8808	
8809			return me;
8810		},
8811	
8812		drawCaret: function(tooltipPoint, size) {
8813			var ctx = this._chart.ctx;
8814			var vm = this._view;
8815			var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
8816	
8817			ctx.lineTo(caretPosition.x1, caretPosition.y1);
8818			ctx.lineTo(caretPosition.x2, caretPosition.y2);
8819			ctx.lineTo(caretPosition.x3, caretPosition.y3);
8820		},
8821		getCaretPosition: function(tooltipPoint, size, vm) {
8822			var x1, x2, x3, y1, y2, y3;
8823			var caretSize = vm.caretSize;
8824			var cornerRadius = vm.cornerRadius;
8825			var xAlign = vm.xAlign;
8826			var yAlign = vm.yAlign;
8827			var ptX = tooltipPoint.x;
8828			var ptY = tooltipPoint.y;
8829			var width = size.width;
8830			var height = size.height;
8831	
8832			if (yAlign === 'center') {
8833				y2 = ptY + (height / 2);
8834	
8835				if (xAlign === 'left') {
8836					x1 = ptX;
8837					x2 = x1 - caretSize;
8838					x3 = x1;
8839	
8840					y1 = y2 + caretSize;
8841					y3 = y2 - caretSize;
8842				} else {
8843					x1 = ptX + width;
8844					x2 = x1 + caretSize;
8845					x3 = x1;
8846	
8847					y1 = y2 - caretSize;
8848					y3 = y2 + caretSize;
8849				}
8850			} else {
8851				if (xAlign === 'left') {
8852					x2 = ptX + cornerRadius + (caretSize);
8853					x1 = x2 - caretSize;
8854					x3 = x2 + caretSize;
8855				} else if (xAlign === 'right') {
8856					x2 = ptX + width - cornerRadius - caretSize;
8857					x1 = x2 - caretSize;
8858					x3 = x2 + caretSize;
8859				} else {
8860					x2 = vm.caretX;
8861					x1 = x2 - caretSize;
8862					x3 = x2 + caretSize;
8863				}
8864				if (yAlign === 'top') {
8865					y1 = ptY;
8866					y2 = y1 - caretSize;
8867					y3 = y1;
8868				} else {
8869					y1 = ptY + height;
8870					y2 = y1 + caretSize;
8871					y3 = y1;
8872					// invert drawing order
8873					var tmp = x3;
8874					x3 = x1;
8875					x1 = tmp;
8876				}
8877			}
8878			return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
8879		},
8880	
8881		drawTitle: function(pt, vm, ctx) {
8882			var title = vm.title;
8883			var length = title.length;
8884			var titleFontSize, titleSpacing, i;
8885	
8886			if (length) {
8887				var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
8888	
8889				pt.x = getAlignedX(vm, vm._titleAlign);
8890	
8891				ctx.textAlign = rtlHelper.textAlign(vm._titleAlign);
8892				ctx.textBaseline = 'middle';
8893	
8894				titleFontSize = vm.titleFontSize;
8895				titleSpacing = vm.titleSpacing;
8896	
8897				ctx.fillStyle = vm.titleFontColor;
8898				ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
8899	
8900				for (i = 0; i < length; ++i) {
8901					ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2);
8902					pt.y += titleFontSize + titleSpacing; // Line Height and spacing
8903	
8904					if (i + 1 === length) {
8905						pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
8906					}
8907				}
8908			}
8909		},
8910	
8911		drawBody: function(pt, vm, ctx) {
8912			var bodyFontSize = vm.bodyFontSize;
8913			var bodySpacing = vm.bodySpacing;
8914			var bodyAlign = vm._bodyAlign;
8915			var body = vm.body;
8916			var drawColorBoxes = vm.displayColors;
8917			var xLinePadding = 0;
8918			var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0;
8919	
8920			var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
8921	
8922			var fillLineOfText = function(line) {
8923				ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2);
8924				pt.y += bodyFontSize + bodySpacing;
8925			};
8926	
8927			var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen;
8928			var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);
8929	
8930			ctx.textAlign = bodyAlign;
8931			ctx.textBaseline = 'middle';
8932			ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
8933	
8934			pt.x = getAlignedX(vm, bodyAlignForCalculation);
8935	
8936			// Before body lines
8937			ctx.fillStyle = vm.bodyFontColor;
8938			helpers$1.each(vm.beforeBody, fillLineOfText);
8939	
8940			xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right'
8941				? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
8942				: 0;
8943	
8944			// Draw body lines now
8945			for (i = 0, ilen = body.length; i < ilen; ++i) {
8946				bodyItem = body[i];
8947				textColor = vm.labelTextColors[i];
8948				labelColors = vm.labelColors[i];
8949	
8950				ctx.fillStyle = textColor;
8951				helpers$1.each(bodyItem.before, fillLineOfText);
8952	
8953				lines = bodyItem.lines;
8954				for (j = 0, jlen = lines.length; j < jlen; ++j) {
8955					// Draw Legend-like boxes if needed
8956					if (drawColorBoxes) {
8957						var rtlColorX = rtlHelper.x(colorX);
8958	
8959						// Fill a white rect so that colours merge nicely if the opacity is < 1
8960						ctx.fillStyle = vm.legendColorBackground;
8961						ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
8962	
8963						// Border
8964						ctx.lineWidth = 1;
8965						ctx.strokeStyle = labelColors.borderColor;
8966						ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
8967	
8968						// Inner square
8969						ctx.fillStyle = labelColors.backgroundColor;
8970						ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
8971						ctx.fillStyle = textColor;
8972					}
8973	
8974					fillLineOfText(lines[j]);
8975				}
8976	
8977				helpers$1.each(bodyItem.after, fillLineOfText);
8978			}
8979	
8980			// Reset back to 0 for after body
8981			xLinePadding = 0;
8982	
8983			// After body lines
8984			helpers$1.each(vm.afterBody, fillLineOfText);
8985			pt.y -= bodySpacing; // Remove last body spacing
8986		},
8987	
8988		drawFooter: function(pt, vm, ctx) {
8989			var footer = vm.footer;
8990			var length = footer.length;
8991			var footerFontSize, i;
8992	
8993			if (length) {
8994				var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
8995	
8996				pt.x = getAlignedX(vm, vm._footerAlign);
8997				pt.y += vm.footerMarginTop;
8998	
8999				ctx.textAlign = rtlHelper.textAlign(vm._footerAlign);
9000				ctx.textBaseline = 'middle';
9001	
9002				footerFontSize = vm.footerFontSize;
9003	
9004				ctx.fillStyle = vm.footerFontColor;
9005				ctx.font = helpers$1.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
9006	
9007				for (i = 0; i < length; ++i) {
9008					ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2);
9009					pt.y += footerFontSize + vm.footerSpacing;
9010				}
9011			}
9012		},
9013	
9014		drawBackground: function(pt, vm, ctx, tooltipSize) {
9015			ctx.fillStyle = vm.backgroundColor;
9016			ctx.strokeStyle = vm.borderColor;
9017			ctx.lineWidth = vm.borderWidth;
9018			var xAlign = vm.xAlign;
9019			var yAlign = vm.yAlign;
9020			var x = pt.x;
9021			var y = pt.y;
9022			var width = tooltipSize.width;
9023			var height = tooltipSize.height;
9024			var radius = vm.cornerRadius;
9025	
9026			ctx.beginPath();
9027			ctx.moveTo(x + radius, y);
9028			if (yAlign === 'top') {
9029				this.drawCaret(pt, tooltipSize);
9030			}
9031			ctx.lineTo(x + width - radius, y);
9032			ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
9033			if (yAlign === 'center' && xAlign === 'right') {
9034				this.drawCaret(pt, tooltipSize);
9035			}
9036			ctx.lineTo(x + width, y + height - radius);
9037			ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
9038			if (yAlign === 'bottom') {
9039				this.drawCaret(pt, tooltipSize);
9040			}
9041			ctx.lineTo(x + radius, y + height);
9042			ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
9043			if (yAlign === 'center' && xAlign === 'left') {
9044				this.drawCaret(pt, tooltipSize);
9045			}
9046			ctx.lineTo(x, y + radius);
9047			ctx.quadraticCurveTo(x, y, x + radius, y);
9048			ctx.closePath();
9049	
9050			ctx.fill();
9051	
9052			if (vm.borderWidth > 0) {
9053				ctx.stroke();
9054			}
9055		},
9056	
9057		draw: function() {
9058			var ctx = this._chart.ctx;
9059			var vm = this._view;
9060	
9061			if (vm.opacity === 0) {
9062				return;
9063			}
9064	
9065			var tooltipSize = {
9066				width: vm.width,
9067				height: vm.height
9068			};
9069			var pt = {
9070				x: vm.x,
9071				y: vm.y
9072			};
9073	
9074			// IE11/Edge does not like very small opacities, so snap to 0
9075			var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
9076	
9077			// Truthy/falsey value for empty tooltip
9078			var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
9079	
9080			if (this._options.enabled && hasTooltipContent) {
9081				ctx.save();
9082				ctx.globalAlpha = opacity;
9083	
9084				// Draw Background
9085				this.drawBackground(pt, vm, ctx, tooltipSize);
9086	
9087				// Draw Title, Body, and Footer
9088				pt.y += vm.yPadding;
9089	
9090				helpers$1.rtl.overrideTextDirection(ctx, vm.textDirection);
9091	
9092				// Titles
9093				this.drawTitle(pt, vm, ctx);
9094	
9095				// Body
9096				this.drawBody(pt, vm, ctx);
9097	
9098				// Footer
9099				this.drawFooter(pt, vm, ctx);
9100	
9101				helpers$1.rtl.restoreTextDirection(ctx, vm.textDirection);
9102	
9103				ctx.restore();
9104			}
9105		},
9106	
9107		/**
9108		 * Handle an event
9109		 * @private
9110		 * @param {IEvent} event - The event to handle
9111		 * @returns {boolean} true if the tooltip changed
9112		 */
9113		handleEvent: function(e) {
9114			var me = this;
9115			var options = me._options;
9116			var changed = false;
9117	
9118			me._lastActive = me._lastActive || [];
9119	
9120			// Find Active Elements for tooltips
9121			if (e.type === 'mouseout') {
9122				me._active = [];
9123			} else {
9124				me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
9125				if (options.reverse) {
9126					me._active.reverse();
9127				}
9128			}
9129	
9130			// Remember Last Actives
9131			changed = !helpers$1.arrayEquals(me._active, me._lastActive);
9132	
9133			// Only handle target event on tooltip change
9134			if (changed) {
9135				me._lastActive = me._active;
9136	
9137				if (options.enabled || options.custom) {
9138					me._eventPosition = {
9139						x: e.x,
9140						y: e.y
9141					};
9142	
9143					me.update(true);
9144					me.pivot();
9145				}
9146			}
9147	
9148			return changed;
9149		}
9150	});
9151	
9152	/**
9153	 * @namespace Chart.Tooltip.positioners
9154	 */
9155	var positioners_1 = positioners;
9156	
9157	var core_tooltip = exports$4;
9158	core_tooltip.positioners = positioners_1;
9159	
9160	var valueOrDefault$9 = helpers$1.valueOrDefault;
9161	
9162	core_defaults._set('global', {
9163		elements: {},
9164		events: [
9165			'mousemove',
9166			'mouseout',
9167			'click',
9168			'touchstart',
9169			'touchmove'
9170		],
9171		hover: {
9172			onHover: null,
9173			mode: 'nearest',
9174			intersect: true,
9175			animationDuration: 400
9176		},
9177		onClick: null,
9178		maintainAspectRatio: true,
9179		responsive: true,
9180		responsiveAnimationDuration: 0
9181	});
9182	
9183	/**
9184	 * Recursively merge the given config objects representing the `scales` option
9185	 * by incorporating scale defaults in `xAxes` and `yAxes` array items, then
9186	 * returns a deep copy of the result, thus doesn't alter inputs.
9187	 */
9188	function mergeScaleConfig(/* config objects ... */) {
9189		return helpers$1.merge(Object.create(null), [].slice.call(arguments), {
9190			merger: function(key, target, source, options) {
9191				if (key === 'xAxes' || key === 'yAxes') {
9192					var slen = source[key].length;
9193					var i, type, scale;
9194	
9195					if (!target[key]) {
9196						target[key] = [];
9197					}
9198	
9199					for (i = 0; i < slen; ++i) {
9200						scale = source[key][i];
9201						type = valueOrDefault$9(scale.type, key === 'xAxes' ? 'category' : 'linear');
9202	
9203						if (i >= target[key].length) {
9204							target[key].push({});
9205						}
9206	
9207						if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
9208							// new/untyped scale or type changed: let's apply the new defaults
9209							// then merge source scale to correctly overwrite the defaults.
9210							helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]);
9211						} else {
9212							// scales type are the same
9213							helpers$1.merge(target[key][i], scale);
9214						}
9215					}
9216				} else {
9217					helpers$1._merger(key, target, source, options);
9218				}
9219			}
9220		});
9221	}
9222	
9223	/**
9224	 * Recursively merge the given config objects as the root options by handling
9225	 * default scale options for the `scales` and `scale` properties, then returns
9226	 * a deep copy of the result, thus doesn't alter inputs.
9227	 */
9228	function mergeConfig(/* config objects ... */) {
9229		return helpers$1.merge(Object.create(null), [].slice.call(arguments), {
9230			merger: function(key, target, source, options) {
9231				var tval = target[key] || Object.create(null);
9232				var sval = source[key];
9233	
9234				if (key === 'scales') {
9235					// scale config merging is complex. Add our own function here for that
9236					target[key] = mergeScaleConfig(tval, sval);
9237				} else if (key === 'scale') {
9238					// used in polar area & radar charts since there is only one scale
9239					target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]);
9240				} else {
9241					helpers$1._merger(key, target, source, options);
9242				}
9243			}
9244		});
9245	}
9246	
9247	function initConfig(config) {
9248		config = config || Object.create(null);
9249	
9250		// Do NOT use mergeConfig for the data object because this method merges arrays
9251		// and so would change references to labels and datasets, preventing data updates.
9252		var data = config.data = config.data || {};
9253		data.datasets = data.datasets || [];
9254		data.labels = data.labels || [];
9255	
9256		config.options = mergeConfig(
9257			core_defaults.global,
9258			core_defaults[config.type],
9259			config.options || {});
9260	
9261		return config;
9262	}
9263	
9264	function updateConfig(chart) {
9265		var newOptions = chart.options;
9266	
9267		helpers$1.each(chart.scales, function(scale) {
9268			core_layouts.removeBox(chart, scale);
9269		});
9270	
9271		newOptions = mergeConfig(
9272			core_defaults.global,
9273			core_defaults[chart.config.type],
9274			newOptions);
9275	
9276		chart.options = chart.config.options = newOptions;
9277		chart.ensureScalesHaveIDs();
9278		chart.buildOrUpdateScales();
9279	
9280		// Tooltip
9281		chart.tooltip._options = newOptions.tooltips;
9282		chart.tooltip.initialize();
9283	}
9284	
9285	function nextAvailableScaleId(axesOpts, prefix, index) {
9286		var id;
9287		var hasId = function(obj) {
9288			return obj.id === id;
9289		};
9290	
9291		do {
9292			id = prefix + index++;
9293		} while (helpers$1.findIndex(axesOpts, hasId) >= 0);
9294	
9295		return id;
9296	}
9297	
9298	function positionIsHorizontal(position) {
9299		return position === 'top' || position === 'bottom';
9300	}
9301	
9302	function compare2Level(l1, l2) {
9303		return function(a, b) {
9304			return a[l1] === b[l1]
9305				? a[l2] - b[l2]
9306				: a[l1] - b[l1];
9307		};
9308	}
9309	
9310	var Chart = function(item, config) {
9311		this.construct(item, config);
9312		return this;
9313	};
9314	
9315	helpers$1.extend(Chart.prototype, /** @lends Chart */ {
9316		/**
9317		 * @private
9318		 */
9319		construct: function(item, config) {
9320			var me = this;
9321	
9322			config = initConfig(config);
9323	
9324			var context = platform.acquireContext(item, config);
9325			var canvas = context && context.canvas;
9326			var height = canvas && canvas.height;
9327			var width = canvas && canvas.width;
9328	
9329			me.id = helpers$1.uid();
9330			me.ctx = context;
9331			me.canvas = canvas;
9332			me.config = config;
9333			me.width = width;
9334			me.height = height;
9335			me.aspectRatio = height ? width / height : null;
9336			me.options = config.options;
9337			me._bufferedRender = false;
9338			me._layers = [];
9339	
9340			/**
9341			 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
9342			 * the "instance" still need to be defined since it might be called from plugins.
9343			 * @prop Chart#chart
9344			 * @deprecated since version 2.6.0
9345			 * @todo remove at version 3
9346			 * @private
9347			 */
9348			me.chart = me;
9349			me.controller = me; // chart.chart.controller #inception
9350	
9351			// Add the chart instance to the global namespace
9352			Chart.instances[me.id] = me;
9353	
9354			// Define alias to the config data: `chart.data === chart.config.data`
9355			Object.defineProperty(me, 'data', {
9356				get: function() {
9357					return me.config.data;
9358				},
9359				set: function(value) {
9360					me.config.data = value;
9361				}
9362			});
9363	
9364			if (!context || !canvas) {
9365				// The given item is not a compatible context2d element, let's return before finalizing
9366				// the chart initialization but after setting basic chart / controller properties that
9367				// can help to figure out that the chart is not valid (e.g chart.canvas !== null);
9368				// https://github.com/chartjs/Chart.js/issues/2807
9369				console.error("Failed to create chart: can't acquire context from the given item");
9370				return;
9371			}
9372	
9373			me.initialize();
9374			me.update();
9375		},
9376	
9377		/**
9378		 * @private
9379		 */
9380		initialize: function() {
9381			var me = this;
9382	
9383			// Before init plugin notification
9384			core_plugins.notify(me, 'beforeInit');
9385	
9386			helpers$1.retinaScale(me, me.options.devicePixelRatio);
9387	
9388			me.bindEvents();
9389	
9390			if (me.options.responsive) {
9391				// Initial resize before chart draws (must be silent to preserve initial animations).
9392				me.resize(true);
9393			}
9394	
9395			me.initToolTip();
9396	
9397			// After init plugin notification
9398			core_plugins.notify(me, 'afterInit');
9399	
9400			return me;
9401		},
9402	
9403		clear: function() {
9404			helpers$1.canvas.clear(this);
9405			return this;
9406		},
9407	
9408		stop: function() {
9409			// Stops any current animation loop occurring
9410			core_animations.cancelAnimation(this);
9411			return this;
9412		},
9413	
9414		resize: function(silent) {
9415			var me = this;
9416			var options = me.options;
9417			var canvas = me.canvas;
9418			var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
9419	
9420			// the canvas render width and height will be casted to integers so make sure that
9421			// the canvas display style uses the same integer values to avoid blurring effect.
9422	
9423			// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
9424			var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas)));
9425			var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas)));
9426	
9427			if (me.width === newWidth && me.height === newHeight) {
9428				return;
9429			}
9430	
9431			canvas.width = me.width = newWidth;
9432			canvas.height = me.height = newHeight;
9433			canvas.style.width = newWidth + 'px';
9434			canvas.style.height = newHeight + 'px';
9435	
9436			helpers$1.retinaScale(me, options.devicePixelRatio);
9437	
9438			if (!silent) {
9439				// Notify any plugins about the resize
9440				var newSize = {width: newWidth, height: newHeight};
9441				core_plugins.notify(me, 'resize', [newSize]);
9442	
9443				// Notify of resize
9444				if (options.onResize) {
9445					options.onResize(me, newSize);
9446				}
9447	
9448				me.stop();
9449				me.update({
9450					duration: options.responsiveAnimationDuration
9451				});
9452			}
9453		},
9454	
9455		ensureScalesHaveIDs: function() {
9456			var options = this.options;
9457			var scalesOptions = options.scales || {};
9458			var scaleOptions = options.scale;
9459	
9460			helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) {
9461				if (!xAxisOptions.id) {
9462					xAxisOptions.id = nextAvailableScaleId(scalesOptions.xAxes, 'x-axis-', index);
9463				}
9464			});
9465	
9466			helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) {
9467				if (!yAxisOptions.id) {
9468					yAxisOptions.id = nextAvailableScaleId(scalesOptions.yAxes, 'y-axis-', index);
9469				}
9470			});
9471	
9472			if (scaleOptions) {
9473				scaleOptions.id = scaleOptions.id || 'scale';
9474			}
9475		},
9476	
9477		/**
9478		 * Builds a map of scale ID to scale object for future lookup.
9479		 */
9480		buildOrUpdateScales: function() {
9481			var me = this;
9482			var options = me.options;
9483			var scales = me.scales || {};
9484			var items = [];
9485			var updated = Object.keys(scales).reduce(function(obj, id) {
9486				obj[id] = false;
9487				return obj;
9488			}, {});
9489	
9490			if (options.scales) {
9491				items = items.concat(
9492					(options.scales.xAxes || []).map(function(xAxisOptions) {
9493						return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
9494					}),
9495					(options.scales.yAxes || []).map(function(yAxisOptions) {
9496						return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
9497					})
9498				);
9499			}
9500	
9501			if (options.scale) {
9502				items.push({
9503					options: options.scale,
9504					dtype: 'radialLinear',
9505					isDefault: true,
9506					dposition: 'chartArea'
9507				});
9508			}
9509	
9510			helpers$1.each(items, function(item) {
9511				var scaleOptions = item.options;
9512				var id = scaleOptions.id;
9513				var scaleType = valueOrDefault$9(scaleOptions.type, item.dtype);
9514	
9515				if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
9516					scaleOptions.position = item.dposition;
9517				}
9518	
9519				updated[id] = true;
9520				var scale = null;
9521				if (id in scales && scales[id].type === scaleType) {
9522					scale = scales[id];
9523					scale.options = scaleOptions;
9524					scale.ctx = me.ctx;
9525					scale.chart = me;
9526				} else {
9527					var scaleClass = core_scaleService.getScaleConstructor(scaleType);
9528					if (!scaleClass) {
9529						return;
9530					}
9531					scale = new scaleClass({
9532						id: id,
9533						type: scaleType,
9534						options: scaleOptions,
9535						ctx: me.ctx,
9536						chart: me
9537					});
9538					scales[scale.id] = scale;
9539				}
9540	
9541				scale.mergeTicksOptions();
9542	
9543				// TODO(SB): I think we should be able to remove this custom case (options.scale)
9544				// and consider it as a regular scale part of the "scales"" map only! This would
9545				// make the logic easier and remove some useless? custom code.
9546				if (item.isDefault) {
9547					me.scale = scale;
9548				}
9549			});
9550			// clear up discarded scales
9551			helpers$1.each(updated, function(hasUpdated, id) {
9552				if (!hasUpdated) {
9553					delete scales[id];
9554				}
9555			});
9556	
9557			me.scales = scales;
9558	
9559			core_scaleService.addScalesToLayout(this);
9560		},
9561	
9562		buildOrUpdateControllers: function() {
9563			var me = this;
9564			var newControllers = [];
9565			var datasets = me.data.datasets;
9566			var i, ilen;
9567	
9568			for (i = 0, ilen = datasets.length; i < ilen; i++) {
9569				var dataset = datasets[i];
9570				var meta = me.getDatasetMeta(i);
9571				var type = dataset.type || me.config.type;
9572	
9573				if (meta.type && meta.type !== type) {
9574					me.destroyDatasetMeta(i);
9575					meta = me.getDatasetMeta(i);
9576				}
9577				meta.type = type;
9578				meta.order = dataset.order || 0;
9579				meta.index = i;
9580	
9581				if (meta.controller) {
9582					meta.controller.updateIndex(i);
9583					meta.controller.linkScales();
9584				} else {
9585					var ControllerClass = controllers[meta.type];
9586					if (ControllerClass === undefined) {
9587						throw new Error('"' + meta.type + '" is not a chart type.');
9588					}
9589	
9590					meta.controller = new ControllerClass(me, i);
9591					newControllers.push(meta.controller);
9592				}
9593			}
9594	
9595			return newControllers;
9596		},
9597	
9598		/**
9599		 * Reset the elements of all datasets
9600		 * @private
9601		 */
9602		resetElements: function() {
9603			var me = this;
9604			helpers$1.each(me.data.datasets, function(dataset, datasetIndex) {
9605				me.getDatasetMeta(datasetIndex).controller.reset();
9606			}, me);
9607		},
9608	
9609		/**
9610		* Resets the chart back to it's state before the initial animation
9611		*/
9612		reset: function() {
9613			this.resetElements();
9614			this.tooltip.initialize();
9615		},
9616	
9617		update: function(config) {
9618			var me = this;
9619			var i, ilen;
9620	
9621			if (!config || typeof config !== 'object') {
9622				// backwards compatibility
9623				config = {
9624					duration: config,
9625					lazy: arguments[1]
9626				};
9627			}
9628	
9629			updateConfig(me);
9630	
9631			// plugins options references might have change, let's invalidate the cache
9632			// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
9633			core_plugins._invalidate(me);
9634	
9635			if (core_plugins.notify(me, 'beforeUpdate') === false) {
9636				return;
9637			}
9638	
9639			// In case the entire data object changed
9640			me.tooltip._data = me.data;
9641	
9642			// Make sure dataset controllers are updated and new controllers are reset
9643			var newControllers = me.buildOrUpdateControllers();
9644	
9645			// Make sure all dataset controllers have correct meta data counts
9646			for (i = 0, ilen = me.data.datasets.length; i < ilen; i++) {
9647				me.getDatasetMeta(i).controller.buildOrUpdateElements();
9648			}
9649	
9650			me.updateLayout();
9651	
9652			// Can only reset the new controllers after the scales have been updated
9653			if (me.options.animation && me.options.animation.duration) {
9654				helpers$1.each(newControllers, function(controller) {
9655					controller.reset();
9656				});
9657			}
9658	
9659			me.updateDatasets();
9660	
9661			// Need to reset tooltip in case it is displayed with elements that are removed
9662			// after update.
9663			me.tooltip.initialize();
9664	
9665			// Last active contains items that were previously in the tooltip.
9666			// When we reset the tooltip, we need to clear it
9667			me.lastActive = [];
9668	
9669			// Do this before render so that any plugins that need final scale updates can use it
9670			core_plugins.notify(me, 'afterUpdate');
9671	
9672			me._layers.sort(compare2Level('z', '_idx'));
9673	
9674			if (me._bufferedRender) {
9675				me._bufferedRequest = {
9676					duration: config.duration,
9677					easing: config.easing,
9678					lazy: config.lazy
9679				};
9680			} else {
9681				me.render(config);
9682			}
9683		},
9684	
9685		/**
9686		 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
9687		 * hook, in which case, plugins will not be called on `afterLayout`.
9688		 * @private
9689		 */
9690		updateLayout: function() {
9691			var me = this;
9692	
9693			if (core_plugins.notify(me, 'beforeLayout') === false) {
9694				return;
9695			}
9696	
9697			core_layouts.update(this, this.width, this.height);
9698	
9699			me._layers = [];
9700			helpers$1.each(me.boxes, function(box) {
9701				// _configure is called twice, once in core.scale.update and once here.
9702				// Here the boxes are fully updated and at their final positions.
9703				if (box._configure) {
9704					box._configure();
9705				}
9706				me._layers.push.apply(me._layers, box._layers());
9707			}, me);
9708	
9709			me._layers.forEach(function(item, index) {
9710				item._idx = index;
9711			});
9712	
9713			/**
9714			 * Provided for backward compatibility, use `afterLayout` instead.
9715			 * @method IPlugin#afterScaleUpdate
9716			 * @deprecated since version 2.5.0
9717			 * @todo remove at version 3
9718			 * @private
9719			 */
9720			core_plugins.notify(me, 'afterScaleUpdate');
9721			core_plugins.notify(me, 'afterLayout');
9722		},
9723	
9724		/**
9725		 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
9726		 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
9727		 * @private
9728		 */
9729		updateDatasets: function() {
9730			var me = this;
9731	
9732			if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) {
9733				return;
9734			}
9735	
9736			for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
9737				me.updateDataset(i);
9738			}
9739	
9740			core_plugins.notify(me, 'afterDatasetsUpdate');
9741		},
9742	
9743		/**
9744		 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
9745		 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
9746		 * @private
9747		 */
9748		updateDataset: function(index) {
9749			var me = this;
9750			var meta = me.getDatasetMeta(index);
9751			var args = {
9752				meta: meta,
9753				index: index
9754			};
9755	
9756			if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
9757				return;
9758			}
9759	
9760			meta.controller._update();
9761	
9762			core_plugins.notify(me, 'afterDatasetUpdate', [args]);
9763		},
9764	
9765		render: function(config) {
9766			var me = this;
9767	
9768			if (!config || typeof config !== 'object') {
9769				// backwards compatibility
9770				config = {
9771					duration: config,
9772					lazy: arguments[1]
9773				};
9774			}
9775	
9776			var animationOptions = me.options.animation;
9777			var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration);
9778			var lazy = config.lazy;
9779	
9780			if (core_plugins.notify(me, 'beforeRender') === false) {
9781				return;
9782			}
9783	
9784			var onComplete = function(animation) {
9785				core_plugins.notify(me, 'afterRender');
9786				helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me);
9787			};
9788	
9789			if (animationOptions && duration) {
9790				var animation = new core_animation({
9791					numSteps: duration / 16.66, // 60 fps
9792					easing: config.easing || animationOptions.easing,
9793	
9794					render: function(chart, animationObject) {
9795						var easingFunction = helpers$1.easing.effects[animationObject.easing];
9796						var currentStep = animationObject.currentStep;
9797						var stepDecimal = currentStep / animationObject.numSteps;
9798	
9799						chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
9800					},
9801	
9802					onAnimationProgress: animationOptions.onProgress,
9803					onAnimationComplete: onComplete
9804				});
9805	
9806				core_animations.addAnimation(me, animation, duration, lazy);
9807			} else {
9808				me.draw();
9809	
9810				// See https://github.com/chartjs/Chart.js/issues/3781
9811				onComplete(new core_animation({numSteps: 0, chart: me}));
9812			}
9813	
9814			return me;
9815		},
9816	
9817		draw: function(easingValue) {
9818			var me = this;
9819			var i, layers;
9820	
9821			me.clear();
9822	
9823			if (helpers$1.isNullOrUndef(easingValue)) {
9824				easingValue = 1;
9825			}
9826	
9827			me.transition(easingValue);
9828	
9829			if (me.width <= 0 || me.height <= 0) {
9830				return;
9831			}
9832	
9833			if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
9834				return;
9835			}
9836	
9837			// Because of plugin hooks (before/afterDatasetsDraw), datasets can't
9838			// currently be part of layers. Instead, we draw
9839			// layers <= 0 before(default, backward compat), and the rest after
9840			layers = me._layers;
9841			for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
9842				layers[i].draw(me.chartArea);
9843			}
9844	
9845			me.drawDatasets(easingValue);
9846	
9847			// Rest of layers
9848			for (; i < layers.length; ++i) {
9849				layers[i].draw(me.chartArea);
9850			}
9851	
9852			me._drawTooltip(easingValue);
9853	
9854			core_plugins.notify(me, 'afterDraw', [easingValue]);
9855		},
9856	
9857		/**
9858		 * @private
9859		 */
9860		transition: function(easingValue) {
9861			var me = this;
9862	
9863			for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
9864				if (me.isDatasetVisible(i)) {
9865					me.getDatasetMeta(i).controller.transition(easingValue);
9866				}
9867			}
9868	
9869			me.tooltip.transition(easingValue);
9870		},
9871	
9872		/**
9873		 * @private
9874		 */
9875		_getSortedDatasetMetas: function(filterVisible) {
9876			var me = this;
9877			var datasets = me.data.datasets || [];
9878			var result = [];
9879			var i, ilen;
9880	
9881			for (i = 0, ilen = datasets.length; i < ilen; ++i) {
9882				if (!filterVisible || me.isDatasetVisible(i)) {
9883					result.push(me.getDatasetMeta(i));
9884				}
9885			}
9886	
9887			result.sort(compare2Level('order', 'index'));
9888	
9889			return result;
9890		},
9891	
9892		/**
9893		 * @private
9894		 */
9895		_getSortedVisibleDatasetMetas: function() {
9896			return this._getSortedDatasetMetas(true);
9897		},
9898	
9899		/**
9900		 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
9901		 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
9902		 * @private
9903		 */
9904		drawDatasets: function(easingValue) {
9905			var me = this;
9906			var metasets, i;
9907	
9908			if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
9909				return;
9910			}
9911	
9912			metasets = me._getSortedVisibleDatasetMetas();
9913			for (i = metasets.length - 1; i >= 0; --i) {
9914				me.drawDataset(metasets[i], easingValue);
9915			}
9916	
9917			core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
9918		},
9919	
9920		/**
9921		 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
9922		 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
9923		 * @private
9924		 */
9925		drawDataset: function(meta, easingValue) {
9926			var me = this;
9927			var args = {
9928				meta: meta,
9929				index: meta.index,
9930				easingValue: easingValue
9931			};
9932	
9933			if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
9934				return;
9935			}
9936	
9937			meta.controller.draw(easingValue);
9938	
9939			core_plugins.notify(me, 'afterDatasetDraw', [args]);
9940		},
9941	
9942		/**
9943		 * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
9944		 * hook, in which case, plugins will not be called on `afterTooltipDraw`.
9945		 * @private
9946		 */
9947		_drawTooltip: function(easingValue) {
9948			var me = this;
9949			var tooltip = me.tooltip;
9950			var args = {
9951				tooltip: tooltip,
9952				easingValue: easingValue
9953			};
9954	
9955			if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
9956				return;
9957			}
9958	
9959			tooltip.draw();
9960	
9961			core_plugins.notify(me, 'afterTooltipDraw', [args]);
9962		},
9963	
9964		/**
9965		 * Get the single element that was clicked on
9966		 * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
9967		 */
9968		getElementAtEvent: function(e) {
9969			return core_interaction.modes.single(this, e);
9970		},
9971	
9972		getElementsAtEvent: function(e) {
9973			return core_interaction.modes.label(this, e, {intersect: true});
9974		},
9975	
9976		getElementsAtXAxis: function(e) {
9977			return core_interaction.modes['x-axis'](this, e, {intersect: true});
9978		},
9979	
9980		getElementsAtEventForMode: function(e, mode, options) {
9981			var method = core_interaction.modes[mode];
9982			if (typeof method === 'function') {
9983				return method(this, e, options);
9984			}
9985	
9986			return [];
9987		},
9988	
9989		getDatasetAtEvent: function(e) {
9990			return core_interaction.modes.dataset(this, e, {intersect: true});
9991		},
9992	
9993		getDatasetMeta: function(datasetIndex) {
9994			var me = this;
9995			var dataset = me.data.datasets[datasetIndex];
9996			if (!dataset._meta) {
9997				dataset._meta = {};
9998			}
9999	
10000			var meta = dataset._meta[me.id];
10001			if (!meta) {
10002				meta = dataset._meta[me.id] = {
10003					type: null,
10004					data: [],
10005					dataset: null,
10006					controller: null,
10007					hidden: null,			// See isDatasetVisible() comment
10008					xAxisID: null,
10009					yAxisID: null,
10010					order: dataset.order || 0,
10011					index: datasetIndex
10012				};
10013			}
10014	
10015			return meta;
10016		},
10017	
10018		getVisibleDatasetCount: function() {
10019			var count = 0;
10020			for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
10021				if (this.isDatasetVisible(i)) {
10022					count++;
10023				}
10024			}
10025			return count;
10026		},
10027	
10028		isDatasetVisible: function(datasetIndex) {
10029			var meta = this.getDatasetMeta(datasetIndex);
10030	
10031			// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
10032			// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
10033			return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
10034		},
10035	
10036		generateLegend: function() {
10037			return this.options.legendCallback(this);
10038		},
10039	
10040		/**
10041		 * @private
10042		 */
10043		destroyDatasetMeta: function(datasetIndex) {
10044			var id = this.id;
10045			var dataset = this.data.datasets[datasetIndex];
10046			var meta = dataset._meta && dataset._meta[id];
10047	
10048			if (meta) {
10049				meta.controller.destroy();
10050				delete dataset._meta[id];
10051			}
10052		},
10053	
10054		destroy: function() {
10055			var me = this;
10056			var canvas = me.canvas;
10057			var i, ilen;
10058	
10059			me.stop();
10060	
10061			// dataset controllers need to cleanup associated data
10062			for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
10063				me.destroyDatasetMeta(i);
10064			}
10065	
10066			if (canvas) {
10067				me.unbindEvents();
10068				helpers$1.canvas.clear(me);
10069				platform.releaseContext(me.ctx);
10070				me.canvas = null;
10071				me.ctx = null;
10072			}
10073	
10074			core_plugins.notify(me, 'destroy');
10075	
10076			delete Chart.instances[me.id];
10077		},
10078	
10079		toBase64Image: function() {
10080			return this.canvas.toDataURL.apply(this.canvas, arguments);
10081		},
10082	
10083		initToolTip: function() {
10084			var me = this;
10085			me.tooltip = new core_tooltip({
10086				_chart: me,
10087				_chartInstance: me, // deprecated, backward compatibility
10088				_data: me.data,
10089				_options: me.options.tooltips
10090			}, me);
10091		},
10092	
10093		/**
10094		 * @private
10095		 */
10096		bindEvents: function() {
10097			var me = this;
10098			var listeners = me._listeners = {};
10099			var listener = function() {
10100				me.eventHandler.apply(me, arguments);
10101			};
10102	
10103			helpers$1.each(me.options.events, function(type) {
10104				platform.addEventListener(me, type, listener);
10105				listeners[type] = listener;
10106			});
10107	
10108			// Elements used to detect size change should not be injected for non responsive charts.
10109			// See https://github.com/chartjs/Chart.js/issues/2210
10110			if (me.options.responsive) {
10111				listener = function() {
10112					me.resize();
10113				};
10114	
10115				platform.addEventListener(me, 'resize', listener);
10116				listeners.resize = listener;
10117			}
10118		},
10119	
10120		/**
10121		 * @private
10122		 */
10123		unbindEvents: function() {
10124			var me = this;
10125			var listeners = me._listeners;
10126			if (!listeners) {
10127				return;
10128			}
10129	
10130			delete me._listeners;
10131			helpers$1.each(listeners, function(listener, type) {
10132				platform.removeEventListener(me, type, listener);
10133			});
10134		},
10135	
10136		updateHoverStyle: function(elements, mode, enabled) {
10137			var prefix = enabled ? 'set' : 'remove';
10138			var element, i, ilen;
10139	
10140			for (i = 0, ilen = elements.length; i < ilen; ++i) {
10141				element = elements[i];
10142				if (element) {
10143					this.getDatasetMeta(element._datasetIndex).controller[prefix + 'HoverStyle'](element);
10144				}
10145			}
10146	
10147			if (mode === 'dataset') {
10148				this.getDatasetMeta(elements[0]._datasetIndex).controller['_' + prefix + 'DatasetHoverStyle']();
10149			}
10150		},
10151	
10152		/**
10153		 * @private
10154		 */
10155		eventHandler: function(e) {
10156			var me = this;
10157			var tooltip = me.tooltip;
10158	
10159			if (core_plugins.notify(me, 'beforeEvent', [e]) === false) {
10160				return;
10161			}
10162	
10163			// Buffer any update calls so that renders do not occur
10164			me._bufferedRender = true;
10165			me._bufferedRequest = null;
10166	
10167			var changed = me.handleEvent(e);
10168			// for smooth tooltip animations issue #4989
10169			// the tooltip should be the source of change
10170			// Animation check workaround:
10171			// tooltip._start will be null when tooltip isn't animating
10172			if (tooltip) {
10173				changed = tooltip._start
10174					? tooltip.handleEvent(e)
10175					: changed | tooltip.handleEvent(e);
10176			}
10177	
10178			core_plugins.notify(me, 'afterEvent', [e]);
10179	
10180			var bufferedRequest = me._bufferedRequest;
10181			if (bufferedRequest) {
10182				// If we have an update that was triggered, we need to do a normal render
10183				me.render(bufferedRequest);
10184			} else if (changed && !me.animating) {
10185				// If entering, leaving, or changing elements, animate the change via pivot
10186				me.stop();
10187	
10188				// We only need to render at this point. Updating will cause scales to be
10189				// recomputed generating flicker & using more memory than necessary.
10190				me.render({
10191					duration: me.options.hover.animationDuration,
10192					lazy: true
10193				});
10194			}
10195	
10196			me._bufferedRender = false;
10197			me._bufferedRequest = null;
10198	
10199			return me;
10200		},
10201	
10202		/**
10203		 * Handle an event
10204		 * @private
10205		 * @param {IEvent} event the event to handle
10206		 * @return {boolean} true if the chart needs to re-render
10207		 */
10208		handleEvent: function(e) {
10209			var me = this;
10210			var options = me.options || {};
10211			var hoverOptions = options.hover;
10212			var changed = false;
10213	
10214			me.lastActive = me.lastActive || [];
10215	
10216			// Find Active Elements for hover and tooltips
10217			if (e.type === 'mouseout') {
10218				me.active = [];
10219			} else {
10220				me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
10221			}
10222	
10223			// Invoke onHover hook
10224			// Need to call with native event here to not break backwards compatibility
10225			helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
10226	
10227			if (e.type === 'mouseup' || e.type === 'click') {
10228				if (options.onClick) {
10229					// Use e.native here for backwards compatibility
10230					options.onClick.call(me, e.native, me.active);
10231				}
10232			}
10233	
10234			// Remove styling for last active (even if it may still be active)
10235			if (me.lastActive.length) {
10236				me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
10237			}
10238	
10239			// Built in hover styling
10240			if (me.active.length && hoverOptions.mode) {
10241				me.updateHoverStyle(me.active, hoverOptions.mode, true);
10242			}
10243	
10244			changed = !helpers$1.arrayEquals(me.active, me.lastActive);
10245	
10246			// Remember Last Actives
10247			me.lastActive = me.active;
10248	
10249			return changed;
10250		}
10251	});
10252	
10253	/**
10254	 * NOTE(SB) We actually don't use this container anymore but we need to keep it
10255	 * for backward compatibility. Though, it can still be useful for plugins that
10256	 * would need to work on multiple charts?!
10257	 */
10258	Chart.instances = {};
10259	
10260	var core_controller = Chart;
10261	
10262	// DEPRECATIONS
10263	
10264	/**
10265	 * Provided for backward compatibility, use Chart instead.
10266	 * @class Chart.Controller
10267	 * @deprecated since version 2.6
10268	 * @todo remove at version 3
10269	 * @private
10270	 */
10271	Chart.Controller = Chart;
10272	
10273	/**
10274	 * Provided for backward compatibility, not available anymore.
10275	 * @namespace Chart
10276	 * @deprecated since version 2.8
10277	 * @todo remove at version 3
10278	 * @private
10279	 */
10280	Chart.types = {};
10281	
10282	/**
10283	 * Provided for backward compatibility, not available anymore.
10284	 * @namespace Chart.helpers.configMerge
10285	 * @deprecated since version 2.8.0
10286	 * @todo remove at version 3
10287	 * @private
10288	 */
10289	helpers$1.configMerge = mergeConfig;
10290	
10291	/**
10292	 * Provided for backward compatibility, not available anymore.
10293	 * @namespace Chart.helpers.scaleMerge
10294	 * @deprecated since version 2.8.0
10295	 * @todo remove at version 3
10296	 * @private
10297	 */
10298	helpers$1.scaleMerge = mergeScaleConfig;
10299	
10300	var core_helpers = function() {
10301	
10302		// -- Basic js utility methods
10303	
10304		helpers$1.where = function(collection, filterCallback) {
10305			if (helpers$1.isArray(collection) && Array.prototype.filter) {
10306				return collection.filter(filterCallback);
10307			}
10308			var filtered = [];
10309	
10310			helpers$1.each(collection, function(item) {
10311				if (filterCallback(item)) {
10312					filtered.push(item);
10313				}
10314			});
10315	
10316			return filtered;
10317		};
10318		helpers$1.findIndex = Array.prototype.findIndex ?
10319			function(array, callback, scope) {
10320				return array.findIndex(callback, scope);
10321			} :
10322			function(array, callback, scope) {
10323				scope = scope === undefined ? array : scope;
10324				for (var i = 0, ilen = array.length; i < ilen; ++i) {
10325					if (callback.call(scope, array[i], i, array)) {
10326						return i;
10327					}
10328				}
10329				return -1;
10330			};
10331		helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
10332			// Default to start of the array
10333			if (helpers$1.isNullOrUndef(startIndex)) {
10334				startIndex = -1;
10335			}
10336			for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
10337				var currentItem = arrayToSearch[i];
10338				if (filterCallback(currentItem)) {
10339					return currentItem;
10340				}
10341			}
10342		};
10343		helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
10344			// Default to end of the array
10345			if (helpers$1.isNullOrUndef(startIndex)) {
10346				startIndex = arrayToSearch.length;
10347			}
10348			for (var i = startIndex - 1; i >= 0; i--) {
10349				var currentItem = arrayToSearch[i];
10350				if (filterCallback(currentItem)) {
10351					return currentItem;
10352				}
10353			}
10354		};
10355	
10356		// -- Math methods
10357		helpers$1.isNumber = function(n) {
10358			return !isNaN(parseFloat(n)) && isFinite(n);
10359		};
10360		helpers$1.almostEquals = function(x, y, epsilon) {
10361			return Math.abs(x - y) < epsilon;
10362		};
10363		helpers$1.almostWhole = function(x, epsilon) {
10364			var rounded = Math.round(x);
10365			return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);
10366		};
10367		helpers$1.max = function(array) {
10368			return array.reduce(function(max, value) {
10369				if (!isNaN(value)) {
10370					return Math.max(max, value);
10371				}
10372				return max;
10373			}, Number.NEGATIVE_INFINITY);
10374		};
10375		helpers$1.min = function(array) {
10376			return array.reduce(function(min, value) {
10377				if (!isNaN(value)) {
10378					return Math.min(min, value);
10379				}
10380				return min;
10381			}, Number.POSITIVE_INFINITY);
10382		};
10383		helpers$1.sign = Math.sign ?
10384			function(x) {
10385				return Math.sign(x);
10386			} :
10387			function(x) {
10388				x = +x; // convert to a number
10389				if (x === 0 || isNaN(x)) {
10390					return x;
10391				}
10392				return x > 0 ? 1 : -1;
10393			};
10394		helpers$1.toRadians = function(degrees) {
10395			return degrees * (Math.PI / 180);
10396		};
10397		helpers$1.toDegrees = function(radians) {
10398			return radians * (180 / Math.PI);
10399		};
10400	
10401		/**
10402		 * Returns the number of decimal places
10403		 * i.e. the number of digits after the decimal point, of the value of this Number.
10404		 * @param {number} x - A number.
10405		 * @returns {number} The number of decimal places.
10406		 * @private
10407		 */
10408		helpers$1._decimalPlaces = function(x) {
10409			if (!helpers$1.isFinite(x)) {
10410				return;
10411			}
10412			var e = 1;
10413			var p = 0;
10414			while (Math.round(x * e) / e !== x) {
10415				e *= 10;
10416				p++;
10417			}
10418			return p;
10419		};
10420	
10421		// Gets the angle from vertical upright to the point about a centre.
10422		helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) {
10423			var distanceFromXCenter = anglePoint.x - centrePoint.x;
10424			var distanceFromYCenter = anglePoint.y - centrePoint.y;
10425			var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
10426	
10427			var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
10428	
10429			if (angle < (-0.5 * Math.PI)) {
10430				angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
10431			}
10432	
10433			return {
10434				angle: angle,
10435				distance: radialDistanceFromCenter
10436			};
10437		};
10438		helpers$1.distanceBetweenPoints = function(pt1, pt2) {
10439			return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
10440		};
10441	
10442		/**
10443		 * Provided for backward compatibility, not available anymore
10444		 * @function Chart.helpers.aliasPixel
10445		 * @deprecated since version 2.8.0
10446		 * @todo remove at version 3
10447		 */
10448		helpers$1.aliasPixel = function(pixelWidth) {
10449			return (pixelWidth % 2 === 0) ? 0 : 0.5;
10450		};
10451	
10452		/**
10453		 * Returns the aligned pixel value to avoid anti-aliasing blur
10454		 * @param {Chart} chart - The chart instance.
10455		 * @param {number} pixel - A pixel value.
10456		 * @param {number} width - The width of the element.
10457		 * @returns {number} The aligned pixel value.
10458		 * @private
10459		 */
10460		helpers$1._alignPixel = function(chart, pixel, width) {
10461			var devicePixelRatio = chart.currentDevicePixelRatio;
10462			var halfWidth = width / 2;
10463			return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
10464		};
10465	
10466		helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
10467			// Props to Rob Spencer at scaled innovation for his post on splining between points
10468			// http://scaledinnovation.com/analytics/splines/aboutSplines.html
10469	
10470			// This function must also respect "skipped" points
10471	
10472			var previous = firstPoint.skip ? middlePoint : firstPoint;
10473			var current = middlePoint;
10474			var next = afterPoint.skip ? middlePoint : afterPoint;
10475	
10476			var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
10477			var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
10478	
10479			var s01 = d01 / (d01 + d12);
10480			var s12 = d12 / (d01 + d12);
10481	
10482			// If all points are the same, s01 & s02 will be inf
10483			s01 = isNaN(s01) ? 0 : s01;
10484			s12 = isNaN(s12) ? 0 : s12;
10485	
10486			var fa = t * s01; // scaling factor for triangle Ta
10487			var fb = t * s12;
10488	
10489			return {
10490				previous: {
10491					x: current.x - fa * (next.x - previous.x),
10492					y: current.y - fa * (next.y - previous.y)
10493				},
10494				next: {
10495					x: current.x + fb * (next.x - previous.x),
10496					y: current.y + fb * (next.y - previous.y)
10497				}
10498			};
10499		};
10500		helpers$1.EPSILON = Number.EPSILON || 1e-14;
10501		helpers$1.splineCurveMonotone = function(points) {
10502			// This function calculates Bézier control points in a similar way than |splineCurve|,
10503			// but preserves monotonicity of the provided data and ensures no local extremums are added
10504			// between the dataset discrete points due to the interpolation.
10505			// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
10506	
10507			var pointsWithTangents = (points || []).map(function(point) {
10508				return {
10509					model: point._model,
10510					deltaK: 0,
10511					mK: 0
10512				};
10513			});
10514	
10515			// Calculate slopes (deltaK) and initialize tangents (mK)
10516			var pointsLen = pointsWithTangents.length;
10517			var i, pointBefore, pointCurrent, pointAfter;
10518			for (i = 0; i < pointsLen; ++i) {
10519				pointCurrent = pointsWithTangents[i];
10520				if (pointCurrent.model.skip) {
10521					continue;
10522				}
10523	
10524				pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
10525				pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
10526				if (pointAfter && !pointAfter.model.skip) {
10527					var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
10528	
10529					// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
10530					pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
10531				}
10532	
10533				if (!pointBefore || pointBefore.model.skip) {
10534					pointCurrent.mK = pointCurrent.deltaK;
10535				} else if (!pointAfter || pointAfter.model.skip) {
10536					pointCurrent.mK = pointBefore.deltaK;
10537				} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
10538					pointCurrent.mK = 0;
10539				} else {
10540					pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
10541				}
10542			}
10543	
10544			// Adjust tangents to ensure monotonic properties
10545			var alphaK, betaK, tauK, squaredMagnitude;
10546			for (i = 0; i < pointsLen - 1; ++i) {
10547				pointCurrent = pointsWithTangents[i];
10548				pointAfter = pointsWithTangents[i + 1];
10549				if (pointCurrent.model.skip || pointAfter.model.skip) {
10550					continue;
10551				}
10552	
10553				if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
10554					pointCurrent.mK = pointAfter.mK = 0;
10555					continue;
10556				}
10557	
10558				alphaK = pointCurrent.mK / pointCurrent.deltaK;
10559				betaK = pointAfter.mK / pointCurrent.deltaK;
10560				squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
10561				if (squaredMagnitude <= 9) {
10562					continue;
10563				}
10564	
10565				tauK = 3 / Math.sqrt(squaredMagnitude);
10566				pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
10567				pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
10568			}
10569	
10570			// Compute control points
10571			var deltaX;
10572			for (i = 0; i < pointsLen; ++i) {
10573				pointCurrent = pointsWithTangents[i];
10574				if (pointCurrent.model.skip) {
10575					continue;
10576				}
10577	
10578				pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
10579				pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
10580				if (pointBefore && !pointBefore.model.skip) {
10581					deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
10582					pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
10583					pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
10584				}
10585				if (pointAfter && !pointAfter.model.skip) {
10586					deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
10587					pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
10588					pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
10589				}
10590			}
10591		};
10592		helpers$1.nextItem = function(collection, index, loop) {
10593			if (loop) {
10594				return index >= collection.length - 1 ? collection[0] : collection[index + 1];
10595			}
10596			return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
10597		};
10598		helpers$1.previousItem = function(collection, index, loop) {
10599			if (loop) {
10600				return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
10601			}
10602			return index <= 0 ? collection[0] : collection[index - 1];
10603		};
10604		// Implementation of the nice number algorithm used in determining where axis labels will go
10605		helpers$1.niceNum = function(range, round) {
10606			var exponent = Math.floor(helpers$1.log10(range));
10607			var fraction = range / Math.pow(10, exponent);
10608			var niceFraction;
10609	
10610			if (round) {
10611				if (fraction < 1.5) {
10612					niceFraction = 1;
10613				} else if (fraction < 3) {
10614					niceFraction = 2;
10615				} else if (fraction < 7) {
10616					niceFraction = 5;
10617				} else {
10618					niceFraction = 10;
10619				}
10620			} else if (fraction <= 1.0) {
10621				niceFraction = 1;
10622			} else if (fraction <= 2) {
10623				niceFraction = 2;
10624			} else if (fraction <= 5) {
10625				niceFraction = 5;
10626			} else {
10627				niceFraction = 10;
10628			}
10629	
10630			return niceFraction * Math.pow(10, exponent);
10631		};
10632		// Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
10633		helpers$1.requestAnimFrame = (function() {
10634			if (typeof window === 'undefined') {
10635				return function(callback) {
10636					callback();
10637				};
10638			}
10639			return window.requestAnimationFrame ||
10640				window.webkitRequestAnimationFrame ||
10641				window.mozRequestAnimationFrame ||
10642				window.oRequestAnimationFrame ||
10643				window.msRequestAnimationFrame ||
10644				function(callback) {
10645					return window.setTimeout(callback, 1000 / 60);
10646				};
10647		}());
10648		// -- DOM methods
10649		helpers$1.getRelativePosition = function(evt, chart) {
10650			var mouseX, mouseY;
10651			var e = evt.originalEvent || evt;
10652			var canvas = evt.target || evt.srcElement;
10653			var boundingRect = canvas.getBoundingClientRect();
10654	
10655			var touches = e.touches;
10656			if (touches && touches.length > 0) {
10657				mouseX = touches[0].clientX;
10658				mouseY = touches[0].clientY;
10659	
10660			} else {
10661				mouseX = e.clientX;
10662				mouseY = e.clientY;
10663			}
10664	
10665			// Scale mouse coordinates into canvas coordinates
10666			// by following the pattern laid out by 'jerryj' in the comments of
10667			// https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
10668			var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left'));
10669			var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top'));
10670			var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right'));
10671			var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom'));
10672			var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
10673			var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
10674	
10675			// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
10676			// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
10677			mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
10678			mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
10679	
10680			return {
10681				x: mouseX,
10682				y: mouseY
10683			};
10684	
10685		};
10686	
10687		// Private helper function to convert max-width/max-height values that may be percentages into a number
10688		function parseMaxStyle(styleValue, node, parentProperty) {
10689			var valueInPixels;
10690			if (typeof styleValue === 'string') {
10691				valueInPixels = parseInt(styleValue, 10);
10692	
10693				if (styleValue.indexOf('%') !== -1) {
10694					// percentage * size in dimension
10695					valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
10696				}
10697			} else {
10698				valueInPixels = styleValue;
10699			}
10700	
10701			return valueInPixels;
10702		}
10703	
10704		/**
10705		 * Returns if the given value contains an effective constraint.
10706		 * @private
10707		 */
10708		function isConstrainedValue(value) {
10709			return value !== undefined && value !== null && value !== 'none';
10710		}
10711	
10712		/**
10713		 * Returns the max width or height of the given DOM node in a cross-browser compatible fashion
10714		 * @param {HTMLElement} domNode - the node to check the constraint on
10715		 * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height')
10716		 * @param {string} percentageProperty - property of parent to use when calculating width as a percentage
10717		 * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser}
10718		 */
10719		function getConstraintDimension(domNode, maxStyle, percentageProperty) {
10720			var view = document.defaultView;
10721			var parentNode = helpers$1._getParentNode(domNode);
10722			var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
10723			var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
10724			var hasCNode = isConstrainedValue(constrainedNode);
10725			var hasCContainer = isConstrainedValue(constrainedContainer);
10726			var infinity = Number.POSITIVE_INFINITY;
10727	
10728			if (hasCNode || hasCContainer) {
10729				return Math.min(
10730					hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
10731					hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
10732			}
10733	
10734			return 'none';
10735		}
10736		// returns Number or undefined if no constraint
10737		helpers$1.getConstraintWidth = function(domNode) {
10738			return getConstraintDimension(domNode, 'max-width', 'clientWidth');
10739		};
10740		// returns Number or undefined if no constraint
10741		helpers$1.getConstraintHeight = function(domNode) {
10742			return getConstraintDimension(domNode, 'max-height', 'clientHeight');
10743		};
10744		/**
10745		 * @private
10746			*/
10747		helpers$1._calculatePadding = function(container, padding, parentDimension) {
10748			padding = helpers$1.getStyle(container, padding);
10749	
10750			return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10);
10751		};
10752		/**
10753		 * @private
10754		 */
10755		helpers$1._getParentNode = function(domNode) {
10756			var parent = domNode.parentNode;
10757			if (parent && parent.toString() === '[object ShadowRoot]') {
10758				parent = parent.host;
10759			}
10760			return parent;
10761		};
10762		helpers$1.getMaximumWidth = function(domNode) {
10763			var container = helpers$1._getParentNode(domNode);
10764			if (!container) {
10765				return domNode.clientWidth;
10766			}
10767	
10768			var clientWidth = container.clientWidth;
10769			var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth);
10770			var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth);
10771	
10772			var w = clientWidth - paddingLeft - paddingRight;
10773			var cw = helpers$1.getConstraintWidth(domNode);
10774			return isNaN(cw) ? w : Math.min(w, cw);
10775		};
10776		helpers$1.getMaximumHeight = function(domNode) {
10777			var container = helpers$1._getParentNode(domNode);
10778			if (!container) {
10779				return domNode.clientHeight;
10780			}
10781	
10782			var clientHeight = container.clientHeight;
10783			var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight);
10784			var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight);
10785	
10786			var h = clientHeight - paddingTop - paddingBottom;
10787			var ch = helpers$1.getConstraintHeight(domNode);
10788			return isNaN(ch) ? h : Math.min(h, ch);
10789		};
10790		helpers$1.getStyle = function(el, property) {
10791			return el.currentStyle ?
10792				el.currentStyle[property] :
10793				document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
10794		};
10795		helpers$1.retinaScale = function(chart, forceRatio) {
10796			var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
10797			if (pixelRatio === 1) {
10798				return;
10799			}
10800	
10801			var canvas = chart.canvas;
10802			var height = chart.height;
10803			var width = chart.width;
10804	
10805			canvas.height = height * pixelRatio;
10806			canvas.width = width * pixelRatio;
10807			chart.ctx.scale(pixelRatio, pixelRatio);
10808	
10809			// If no style has been set on the canvas, the render size is used as display size,
10810			// making the chart visually bigger, so let's enforce it to the "correct" values.
10811			// See https://github.com/chartjs/Chart.js/issues/3575
10812			if (!canvas.style.height && !canvas.style.width) {
10813				canvas.style.height = height + 'px';
10814				canvas.style.width = width + 'px';
10815			}
10816		};
10817		// -- Canvas methods
10818		helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) {
10819			return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
10820		};
10821		helpers$1.longestText = function(ctx, font, arrayOfThings, cache) {
10822			cache = cache || {};
10823			var data = cache.data = cache.data || {};
10824			var gc = cache.garbageCollect = cache.garbageCollect || [];
10825	
10826			if (cache.font !== font) {
10827				data = cache.data = {};
10828				gc = cache.garbageCollect = [];
10829				cache.font = font;
10830			}
10831	
10832			ctx.font = font;
10833			var longest = 0;
10834			var ilen = arrayOfThings.length;
10835			var i, j, jlen, thing, nestedThing;
10836			for (i = 0; i < ilen; i++) {
10837				thing = arrayOfThings[i];
10838	
10839				// Undefined strings and arrays should not be measured
10840				if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) {
10841					longest = helpers$1.measureText(ctx, data, gc, longest, thing);
10842				} else if (helpers$1.isArray(thing)) {
10843					// if it is an array lets measure each element
10844					// to do maybe simplify this function a bit so we can do this more recursively?
10845					for (j = 0, jlen = thing.length; j < jlen; j++) {
10846						nestedThing = thing[j];
10847						// Undefined strings and arrays should not be measured
10848						if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) {
10849							longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing);
10850						}
10851					}
10852				}
10853			}
10854	
10855			var gcLen = gc.length / 2;
10856			if (gcLen > arrayOfThings.length) {
10857				for (i = 0; i < gcLen; i++) {
10858					delete data[gc[i]];
10859				}
10860				gc.splice(0, gcLen);
10861			}
10862			return longest;
10863		};
10864		helpers$1.measureText = function(ctx, data, gc, longest, string) {
10865			var textWidth = data[string];
10866			if (!textWidth) {
10867				textWidth = data[string] = ctx.measureText(string).width;
10868				gc.push(string);
10869			}
10870			if (textWidth > longest) {
10871				longest = textWidth;
10872			}
10873			return longest;
10874		};
10875	
10876		/**
10877		 * @deprecated
10878		 */
10879		helpers$1.numberOfLabelLines = function(arrayOfThings) {
10880			var numberOfLines = 1;
10881			helpers$1.each(arrayOfThings, function(thing) {
10882				if (helpers$1.isArray(thing)) {
10883					if (thing.length > numberOfLines) {
10884						numberOfLines = thing.length;
10885					}
10886				}
10887			});
10888			return numberOfLines;
10889		};
10890	
10891		helpers$1.color = !chartjsColor ?
10892			function(value) {
10893				console.error('Color.js not found!');
10894				return value;
10895			} :
10896			function(value) {
10897				/* global CanvasGradient */
10898				if (value instanceof CanvasGradient) {
10899					value = core_defaults.global.defaultColor;
10900				}
10901	
10902				return chartjsColor(value);
10903			};
10904	
10905		helpers$1.getHoverColor = function(colorValue) {
10906			/* global CanvasPattern */
10907			return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ?
10908				colorValue :
10909				helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString();
10910		};
10911	};
10912	
10913	function abstract() {
10914		throw new Error(
10915			'This method is not implemented: either no adapter can ' +
10916			'be found or an incomplete integration was provided.'
10917		);
10918	}
10919	
10920	/**
10921	 * Date adapter (current used by the time scale)
10922	 * @namespace Chart._adapters._date
10923	 * @memberof Chart._adapters
10924	 * @private
10925	 */
10926	
10927	/**
10928	 * Currently supported unit string values.
10929	 * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')}
10930	 * @memberof Chart._adapters._date
10931	 * @name Unit
10932	 */
10933	
10934	/**
10935	 * @class
10936	 */
10937	function DateAdapter(options) {
10938		this.options = options || {};
10939	}
10940	
10941	helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ {
10942		/**
10943		 * Returns a map of time formats for the supported formatting units defined
10944		 * in Unit as well as 'datetime' representing a detailed date/time string.
10945		 * @returns {{string: string}}
10946		 */
10947		formats: abstract,
10948	
10949		/**
10950		 * Parses the given `value` and return the associated timestamp.
10951		 * @param {any} value - the value to parse (usually comes from the data)
10952		 * @param {string} [format] - the expected data format
10953		 * @returns {(number|null)}
10954		 * @function
10955		 */
10956		parse: abstract,
10957	
10958		/**
10959		 * Returns the formatted date in the specified `format` for a given `timestamp`.
10960		 * @param {number} timestamp - the timestamp to format
10961		 * @param {string} format - the date/time token
10962		 * @return {string}
10963		 * @function
10964		 */
10965		format: abstract,
10966	
10967		/**
10968		 * Adds the specified `amount` of `unit` to the given `timestamp`.
10969		 * @param {number} timestamp - the input timestamp
10970		 * @param {number} amount - the amount to add
10971		 * @param {Unit} unit - the unit as string
10972		 * @return {number}
10973		 * @function
10974		 */
10975		add: abstract,
10976	
10977		/**
10978		 * Returns the number of `unit` between the given timestamps.
10979		 * @param {number} max - the input timestamp (reference)
10980		 * @param {number} min - the timestamp to substract
10981		 * @param {Unit} unit - the unit as string
10982		 * @return {number}
10983		 * @function
10984		 */
10985		diff: abstract,
10986	
10987		/**
10988		 * Returns start of `unit` for the given `timestamp`.
10989		 * @param {number} timestamp - the input timestamp
10990		 * @param {Unit} unit - the unit as string
10991		 * @param {number} [weekday] - the ISO day of the week with 1 being Monday
10992		 * and 7 being Sunday (only needed if param *unit* is `isoWeek`).
10993		 * @function
10994		 */
10995		startOf: abstract,
10996	
10997		/**
10998		 * Returns end of `unit` for the given `timestamp`.
10999		 * @param {number} timestamp - the input timestamp
11000		 * @param {Unit} unit - the unit as string
11001		 * @function
11002		 */
11003		endOf: abstract,
11004	
11005		// DEPRECATIONS
11006	
11007		/**
11008		 * Provided for backward compatibility for scale.getValueForPixel(),
11009		 * this method should be overridden only by the moment adapter.
11010		 * @deprecated since version 2.8.0
11011		 * @todo remove at version 3
11012		 * @private
11013		 */
11014		_create: function(value) {
11015			return value;
11016		}
11017	});
11018	
11019	DateAdapter.override = function(members) {
11020		helpers$1.extend(DateAdapter.prototype, members);
11021	};
11022	
11023	var _date = DateAdapter;
11024	
11025	var core_adapters = {
11026		_date: _date
11027	};
11028	
11029	/**
11030	 * Namespace to hold static tick generation functions
11031	 * @namespace Chart.Ticks
11032	 */
11033	var core_ticks = {
11034		/**
11035		 * Namespace to hold formatters for different types of ticks
11036		 * @namespace Chart.Ticks.formatters
11037		 */
11038		formatters: {
11039			/**
11040			 * Formatter for value labels
11041			 * @method Chart.Ticks.formatters.values
11042			 * @param value the value to display
11043			 * @return {string|string[]} the label to display
11044			 */
11045			values: function(value) {
11046				return helpers$1.isArray(value) ? value : '' + value;
11047			},
11048	
11049			/**
11050			 * Formatter for linear numeric ticks
11051			 * @method Chart.Ticks.formatters.linear
11052			 * @param tickValue {number} the value to be formatted
11053			 * @param index {number} the position of the tickValue parameter in the ticks array
11054			 * @param ticks {number[]} the list of ticks being converted
11055			 * @return {string} string representation of the tickValue parameter
11056			 */
11057			linear: function(tickValue, index, ticks) {
11058				// If we have lots of ticks, don't use the ones
11059				var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
11060	
11061				// If we have a number like 2.5 as the delta, figure out how many decimal places we need
11062				if (Math.abs(delta) > 1) {
11063					if (tickValue !== Math.floor(tickValue)) {
11064						// not an integer
11065						delta = tickValue - Math.floor(tickValue);
11066					}
11067				}
11068	
11069				var logDelta = helpers$1.log10(Math.abs(delta));
11070				var tickString = '';
11071	
11072				if (tickValue !== 0) {
11073					var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
11074					if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
11075						var logTick = helpers$1.log10(Math.abs(tickValue));
11076						var numExponential = Math.floor(logTick) - Math.floor(logDelta);
11077						numExponential = Math.max(Math.min(numExponential, 20), 0);
11078						tickString = tickValue.toExponential(numExponential);
11079					} else {
11080						var numDecimal = -1 * Math.floor(logDelta);
11081						numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
11082						tickString = tickValue.toFixed(numDecimal);
11083					}
11084				} else {
11085					tickString = '0'; // never show decimal places for 0
11086				}
11087	
11088				return tickString;
11089			},
11090	
11091			logarithmic: function(tickValue, index, ticks) {
11092				var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue))));
11093	
11094				if (tickValue === 0) {
11095					return '0';
11096				} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
11097					return tickValue.toExponential();
11098				}
11099				return '';
11100			}
11101		}
11102	};
11103	
11104	var isArray = helpers$1.isArray;
11105	var isNullOrUndef = helpers$1.isNullOrUndef;
11106	var valueOrDefault$a = helpers$1.valueOrDefault;
11107	var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault;
11108	
11109	core_defaults._set('scale', {
11110		display: true,
11111		position: 'left',
11112		offset: false,
11113	
11114		// grid line settings
11115		gridLines: {
11116			display: true,
11117			color: 'rgba(0,0,0,0.1)',
11118			lineWidth: 1,
11119			drawBorder: true,
11120			drawOnChartArea: true,
11121			drawTicks: true,
11122			tickMarkLength: 10,
11123			zeroLineWidth: 1,
11124			zeroLineColor: 'rgba(0,0,0,0.25)',
11125			zeroLineBorderDash: [],
11126			zeroLineBorderDashOffset: 0.0,
11127			offsetGridLines: false,
11128			borderDash: [],
11129			borderDashOffset: 0.0
11130		},
11131	
11132		// scale label
11133		scaleLabel: {
11134			// display property
11135			display: false,
11136	
11137			// actual label
11138			labelString: '',
11139	
11140			// top/bottom padding
11141			padding: {
11142				top: 4,
11143				bottom: 4
11144			}
11145		},
11146	
11147		// label settings
11148		ticks: {
11149			beginAtZero: false,
11150			minRotation: 0,
11151			maxRotation: 50,
11152			mirror: false,
11153			padding: 0,
11154			reverse: false,
11155			display: true,
11156			autoSkip: true,
11157			autoSkipPadding: 0,
11158			labelOffset: 0,
11159			// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
11160			callback: core_ticks.formatters.values,
11161			minor: {},
11162			major: {}
11163		}
11164	});
11165	
11166	/** Returns a new array containing numItems from arr */
11167	function sample(arr, numItems) {
11168		var result = [];
11169		var increment = arr.length / numItems;
11170		var i = 0;
11171		var len = arr.length;
11172	
11173		for (; i < len; i += increment) {
11174			result.push(arr[Math.floor(i)]);
11175		}
11176		return result;
11177	}
11178	
11179	function getPixelForGridLine(scale, index, offsetGridLines) {
11180		var length = scale.getTicks().length;
11181		var validIndex = Math.min(index, length - 1);
11182		var lineValue = scale.getPixelForTick(validIndex);
11183		var start = scale._startPixel;
11184		var end = scale._endPixel;
11185		var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
11186		var offset;
11187	
11188		if (offsetGridLines) {
11189			if (length === 1) {
11190				offset = Math.max(lineValue - start, end - lineValue);
11191			} else if (index === 0) {
11192				offset = (scale.getPixelForTick(1) - lineValue) / 2;
11193			} else {
11194				offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;
11195			}
11196			lineValue += validIndex < index ? offset : -offset;
11197	
11198			// Return undefined if the pixel is out of the range
11199			if (lineValue < start - epsilon || lineValue > end + epsilon) {
11200				return;
11201			}
11202		}
11203		return lineValue;
11204	}
11205	
11206	function garbageCollect(caches, length) {
11207		helpers$1.each(caches, function(cache) {
11208			var gc = cache.gc;
11209			var gcLen = gc.length / 2;
11210			var i;
11211			if (gcLen > length) {
11212				for (i = 0; i < gcLen; ++i) {
11213					delete cache.data[gc[i]];
11214				}
11215				gc.splice(0, gcLen);
11216			}
11217		});
11218	}
11219	
11220	/**
11221	 * Returns {width, height, offset} objects for the first, last, widest, highest tick
11222	 * labels where offset indicates the anchor point offset from the top in pixels.
11223	 */
11224	function computeLabelSizes(ctx, tickFonts, ticks, caches) {
11225		var length = ticks.length;
11226		var widths = [];
11227		var heights = [];
11228		var offsets = [];
11229		var widestLabelSize = 0;
11230		var highestLabelSize = 0;
11231		var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
11232	
11233		for (i = 0; i < length; ++i) {
11234			label = ticks[i].label;
11235			tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor;
11236			ctx.font = fontString = tickFont.string;
11237			cache = caches[fontString] = caches[fontString] || {data: {}, gc: []};
11238			lineHeight = tickFont.lineHeight;
11239			width = height = 0;
11240			// Undefined labels and arrays should not be measured
11241			if (!isNullOrUndef(label) && !isArray(label)) {
11242				width = helpers$1.measureText(ctx, cache.data, cache.gc, width, label);
11243				height = lineHeight;
11244			} else if (isArray(label)) {
11245				// if it is an array let's measure each element
11246				for (j = 0, jlen = label.length; j < jlen; ++j) {
11247					nestedLabel = label[j];
11248					// Undefined labels and arrays should not be measured
11249					if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
11250						width = helpers$1.measureText(ctx, cache.data, cache.gc, width, nestedLabel);
11251						height += lineHeight;
11252					}
11253				}
11254			}
11255			widths.push(width);
11256			heights.push(height);
11257			offsets.push(lineHeight / 2);
11258			widestLabelSize = Math.max(width, widestLabelSize);
11259			highestLabelSize = Math.max(height, highestLabelSize);
11260		}
11261		garbageCollect(caches, length);
11262	
11263		widest = widths.indexOf(widestLabelSize);
11264		highest = heights.indexOf(highestLabelSize);
11265	
11266		function valueAt(idx) {
11267			return {
11268				width: widths[idx] || 0,
11269				height: heights[idx] || 0,
11270				offset: offsets[idx] || 0
11271			};
11272		}
11273	
11274		return {
11275			first: valueAt(0),
11276			last: valueAt(length - 1),
11277			widest: valueAt(widest),
11278			highest: valueAt(highest)
11279		};
11280	}
11281	
11282	function getTickMarkLength(options) {
11283		return options.drawTicks ? options.tickMarkLength : 0;
11284	}
11285	
11286	function getScaleLabelHeight(options) {
11287		var font, padding;
11288	
11289		if (!options.display) {
11290			return 0;
11291		}
11292	
11293		font = helpers$1.options._parseFont(options);
11294		padding = helpers$1.options.toPadding(options.padding);
11295	
11296		return font.lineHeight + padding.height;
11297	}
11298	
11299	function parseFontOptions(options, nestedOpts) {
11300		return helpers$1.extend(helpers$1.options._parseFont({
11301			fontFamily: valueOrDefault$a(nestedOpts.fontFamily, options.fontFamily),
11302			fontSize: valueOrDefault$a(nestedOpts.fontSize, options.fontSize),
11303			fontStyle: valueOrDefault$a(nestedOpts.fontStyle, options.fontStyle),
11304			lineHeight: valueOrDefault$a(nestedOpts.lineHeight, options.lineHeight)
11305		}), {
11306			color: helpers$1.options.resolve([nestedOpts.fontColor, options.fontColor, core_defaults.global.defaultFontColor])
11307		});
11308	}
11309	
11310	function parseTickFontOptions(options) {
11311		var minor = parseFontOptions(options, options.minor);
11312		var major = options.major.enabled ? parseFontOptions(options, options.major) : minor;
11313	
11314		return {minor: minor, major: major};
11315	}
11316	
11317	function nonSkipped(ticksToFilter) {
11318		var filtered = [];
11319		var item, index, len;
11320		for (index = 0, len = ticksToFilter.length; index < len; ++index) {
11321			item = ticksToFilter[index];
11322			if (typeof item._index !== 'undefined') {
11323				filtered.push(item);
11324			}
11325		}
11326		return filtered;
11327	}
11328	
11329	function getEvenSpacing(arr) {
11330		var len = arr.length;
11331		var i, diff;
11332	
11333		if (len < 2) {
11334			return false;
11335		}
11336	
11337		for (diff = arr[0], i = 1; i < len; ++i) {
11338			if (arr[i] - arr[i - 1] !== diff) {
11339				return false;
11340			}
11341		}
11342		return diff;
11343	}
11344	
11345	function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) {
11346		var evenMajorSpacing = getEvenSpacing(majorIndices);
11347		var spacing = (ticks.length - 1) / ticksLimit;
11348		var factors, factor, i, ilen;
11349	
11350		// If the major ticks are evenly spaced apart, place the minor ticks
11351		// so that they divide the major ticks into even chunks
11352		if (!evenMajorSpacing) {
11353			return Math.max(spacing, 1);
11354		}
11355	
11356		factors = helpers$1.math._factorize(evenMajorSpacing);
11357		for (i = 0, ilen = factors.length - 1; i < ilen; i++) {
11358			factor = factors[i];
11359			if (factor > spacing) {
11360				return factor;
11361			}
11362		}
11363		return Math.max(spacing, 1);
11364	}
11365	
11366	function getMajorIndices(ticks) {
11367		var result = [];
11368		var i, ilen;
11369		for (i = 0, ilen = ticks.length; i < ilen; i++) {
11370			if (ticks[i].major) {
11371				result.push(i);
11372			}
11373		}
11374		return result;
11375	}
11376	
11377	function skipMajors(ticks, majorIndices, spacing) {
11378		var count = 0;
11379		var next = majorIndices[0];
11380		var i, tick;
11381	
11382		spacing = Math.ceil(spacing);
11383		for (i = 0; i < ticks.length; i++) {
11384			tick = ticks[i];
11385			if (i === next) {
11386				tick._index = i;
11387				count++;
11388				next = majorIndices[count * spacing];
11389			} else {
11390				delete tick.label;
11391			}
11392		}
11393	}
11394	
11395	function skip(ticks, spacing, majorStart, majorEnd) {
11396		var start = valueOrDefault$a(majorStart, 0);
11397		var end = Math.min(valueOrDefault$a(majorEnd, ticks.length), ticks.length);
11398		var count = 0;
11399		var length, i, tick, next;
11400	
11401		spacing = Math.ceil(spacing);
11402		if (majorEnd) {
11403			length = majorEnd - majorStart;
11404			spacing = length / Math.floor(length / spacing);
11405		}
11406	
11407		next = start;
11408	
11409		while (next < 0) {
11410			count++;
11411			next = Math.round(start + count * spacing);
11412		}
11413	
11414		for (i = Math.max(start, 0); i < end; i++) {
11415			tick = ticks[i];
11416			if (i === next) {
11417				tick._index = i;
11418				count++;
11419				next = Math.round(start + count * spacing);
11420			} else {
11421				delete tick.label;
11422			}
11423		}
11424	}
11425	
11426	var Scale = core_element.extend({
11427	
11428		zeroLineIndex: 0,
11429	
11430		/**
11431		 * Get the padding needed for the scale
11432		 * @method getPadding
11433		 * @private
11434		 * @returns {Padding} the necessary padding
11435		 */
11436		getPadding: function() {
11437			var me = this;
11438			return {
11439				left: me.paddingLeft || 0,
11440				top: me.paddingTop || 0,
11441				right: me.paddingRight || 0,
11442				bottom: me.paddingBottom || 0
11443			};
11444		},
11445	
11446		/**
11447		 * Returns the scale tick objects ({label, major})
11448		 * @since 2.7
11449		 */
11450		getTicks: function() {
11451			return this._ticks;
11452		},
11453	
11454		/**
11455		* @private
11456		*/
11457		_getLabels: function() {
11458			var data = this.chart.data;
11459			return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];
11460		},
11461	
11462		// These methods are ordered by lifecyle. Utilities then follow.
11463		// Any function defined here is inherited by all scale types.
11464		// Any function can be extended by the scale type
11465	
11466		/**
11467		 * Provided for backward compatibility, not available anymore
11468		 * @function Chart.Scale.mergeTicksOptions
11469		 * @deprecated since version 2.8.0
11470		 * @todo remove at version 3
11471		 */
11472		mergeTicksOptions: function() {
11473			// noop
11474		},
11475	
11476		beforeUpdate: function() {
11477			helpers$1.callback(this.options.beforeUpdate, [this]);
11478		},
11479	
11480		/**
11481		 * @param {number} maxWidth - the max width in pixels
11482		 * @param {number} maxHeight - the max height in pixels
11483		 * @param {object} margins - the space between the edge of the other scales and edge of the chart
11484		 *   This space comes from two sources:
11485		 *     - padding - space that's required to show the labels at the edges of the scale
11486		 *     - thickness of scales or legends in another orientation
11487		 */
11488		update: function(maxWidth, maxHeight, margins) {
11489			var me = this;
11490			var tickOpts = me.options.ticks;
11491			var sampleSize = tickOpts.sampleSize;
11492			var i, ilen, labels, ticks, samplingEnabled;
11493	
11494			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
11495			me.beforeUpdate();
11496	
11497			// Absorb the master measurements
11498			me.maxWidth = maxWidth;
11499			me.maxHeight = maxHeight;
11500			me.margins = helpers$1.extend({
11501				left: 0,
11502				right: 0,
11503				top: 0,
11504				bottom: 0
11505			}, margins);
11506	
11507			me._ticks = null;
11508			me.ticks = null;
11509			me._labelSizes = null;
11510			me._maxLabelLines = 0;
11511			me.longestLabelWidth = 0;
11512			me.longestTextCache = me.longestTextCache || {};
11513			me._gridLineItems = null;
11514			me._labelItems = null;
11515	
11516			// Dimensions
11517			me.beforeSetDimensions();
11518			me.setDimensions();
11519			me.afterSetDimensions();
11520	
11521			// Data min/max
11522			me.beforeDataLimits();
11523			me.determineDataLimits();
11524			me.afterDataLimits();
11525	
11526			// Ticks - `this.ticks` is now DEPRECATED!
11527			// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
11528			// and must not be accessed directly from outside this class. `this.ticks` being
11529			// around for long time and not marked as private, we can't change its structure
11530			// without unexpected breaking changes. If you need to access the scale ticks,
11531			// use scale.getTicks() instead.
11532	
11533			me.beforeBuildTicks();
11534	
11535			// New implementations should return an array of objects but for BACKWARD COMPAT,
11536			// we still support no return (`this.ticks` internally set by calling this method).
11537			ticks = me.buildTicks() || [];
11538	
11539			// Allow modification of ticks in callback.
11540			ticks = me.afterBuildTicks(ticks) || ticks;
11541	
11542			// Ensure ticks contains ticks in new tick format
11543			if ((!ticks || !ticks.length) && me.ticks) {
11544				ticks = [];
11545				for (i = 0, ilen = me.ticks.length; i < ilen; ++i) {
11546					ticks.push({
11547						value: me.ticks[i],
11548						major: false
11549					});
11550				}
11551			}
11552	
11553			me._ticks = ticks;
11554	
11555			// Compute tick rotation and fit using a sampled subset of labels
11556			// We generally don't need to compute the size of every single label for determining scale size
11557			samplingEnabled = sampleSize < ticks.length;
11558			labels = me._convertTicksToLabels(samplingEnabled ? sample(ticks, sampleSize) : ticks);
11559	
11560			// _configure is called twice, once here, once from core.controller.updateLayout.
11561			// Here we haven't been positioned yet, but dimensions are correct.
11562			// Variables set in _configure are needed for calculateTickRotation, and
11563			// it's ok that coordinates are not correct there, only dimensions matter.
11564			me._configure();
11565	
11566			// Tick Rotation
11567			me.beforeCalculateTickRotation();
11568			me.calculateTickRotation();
11569			me.afterCalculateTickRotation();
11570	
11571			me.beforeFit();
11572			me.fit();
11573			me.afterFit();
11574	
11575			// Auto-skip
11576			me._ticksToDraw = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(ticks) : ticks;
11577	
11578			if (samplingEnabled) {
11579				// Generate labels using all non-skipped ticks
11580				labels = me._convertTicksToLabels(me._ticksToDraw);
11581			}
11582	
11583			me.ticks = labels;   // BACKWARD COMPATIBILITY
11584	
11585			// IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!
11586	
11587			me.afterUpdate();
11588	
11589			// TODO(v3): remove minSize as a public property and return value from all layout boxes. It is unused
11590			// make maxWidth and maxHeight private
11591			return me.minSize;
11592		},
11593	
11594		/**
11595		 * @private
11596		 */
11597		_configure: function() {
11598			var me = this;
11599			var reversePixels = me.options.ticks.reverse;
11600			var startPixel, endPixel;
11601	
11602			if (me.isHorizontal()) {
11603				startPixel = me.left;
11604				endPixel = me.right;
11605			} else {
11606				startPixel = me.top;
11607				endPixel = me.bottom;
11608				// by default vertical scales are from bottom to top, so pixels are reversed
11609				reversePixels = !reversePixels;
11610			}
11611			me._startPixel = startPixel;
11612			me._endPixel = endPixel;
11613			me._reversePixels = reversePixels;
11614			me._length = endPixel - startPixel;
11615		},
11616	
11617		afterUpdate: function() {
11618			helpers$1.callback(this.options.afterUpdate, [this]);
11619		},
11620	
11621		//
11622	
11623		beforeSetDimensions: function() {
11624			helpers$1.callback(this.options.beforeSetDimensions, [this]);
11625		},
11626		setDimensions: function() {
11627			var me = this;
11628			// Set the unconstrained dimension before label rotation
11629			if (me.isHorizontal()) {
11630				// Reset position before calculating rotation
11631				me.width = me.maxWidth;
11632				me.left = 0;
11633				me.right = me.width;
11634			} else {
11635				me.height = me.maxHeight;
11636	
11637				// Reset position before calculating rotation
11638				me.top = 0;
11639				me.bottom = me.height;
11640			}
11641	
11642			// Reset padding
11643			me.paddingLeft = 0;
11644			me.paddingTop = 0;
11645			me.paddingRight = 0;
11646			me.paddingBottom = 0;
11647		},
11648		afterSetDimensions: function() {
11649			helpers$1.callback(this.options.afterSetDimensions, [this]);
11650		},
11651	
11652		// Data limits
11653		beforeDataLimits: function() {
11654			helpers$1.callback(this.options.beforeDataLimits, [this]);
11655		},
11656		determineDataLimits: helpers$1.noop,
11657		afterDataLimits: function() {
11658			helpers$1.callback(this.options.afterDataLimits, [this]);
11659		},
11660	
11661		//
11662		beforeBuildTicks: function() {
11663			helpers$1.callback(this.options.beforeBuildTicks, [this]);
11664		},
11665		buildTicks: helpers$1.noop,
11666		afterBuildTicks: function(ticks) {
11667			var me = this;
11668			// ticks is empty for old axis implementations here
11669			if (isArray(ticks) && ticks.length) {
11670				return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]);
11671			}
11672			// Support old implementations (that modified `this.ticks` directly in buildTicks)
11673			me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks;
11674			return ticks;
11675		},
11676	
11677		beforeTickToLabelConversion: function() {
11678			helpers$1.callback(this.options.beforeTickToLabelConversion, [this]);
11679		},
11680		convertTicksToLabels: function() {
11681			var me = this;
11682			// Convert ticks to strings
11683			var tickOpts = me.options.ticks;
11684			me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
11685		},
11686		afterTickToLabelConversion: function() {
11687			helpers$1.callback(this.options.afterTickToLabelConversion, [this]);
11688		},
11689	
11690		//
11691	
11692		beforeCalculateTickRotation: function() {
11693			helpers$1.callback(this.options.beforeCalculateTickRotation, [this]);
11694		},
11695		calculateTickRotation: function() {
11696			var me = this;
11697			var options = me.options;
11698			var tickOpts = options.ticks;
11699			var numTicks = me.getTicks().length;
11700			var minRotation = tickOpts.minRotation || 0;
11701			var maxRotation = tickOpts.maxRotation;
11702			var labelRotation = minRotation;
11703			var labelSizes, maxLabelWidth, maxLabelHeight, maxWidth, tickWidth, maxHeight, maxLabelDiagonal;
11704	
11705			if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) {
11706				me.labelRotation = minRotation;
11707				return;
11708			}
11709	
11710			labelSizes = me._getLabelSizes();
11711			maxLabelWidth = labelSizes.widest.width;
11712			maxLabelHeight = labelSizes.highest.height - labelSizes.highest.offset;
11713	
11714			// Estimate the width of each grid based on the canvas width, the maximum
11715			// label width and the number of tick intervals
11716			maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth);
11717			tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1);
11718	
11719			// Allow 3 pixels x2 padding either side for label readability
11720			if (maxLabelWidth + 6 > tickWidth) {
11721				tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
11722				maxHeight = me.maxHeight - getTickMarkLength(options.gridLines)
11723					- tickOpts.padding - getScaleLabelHeight(options.scaleLabel);
11724				maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
11725				labelRotation = helpers$1.toDegrees(Math.min(
11726					Math.asin(Math.min((labelSizes.highest.height + 6) / tickWidth, 1)),
11727					Math.asin(Math.min(maxHeight / maxLabelDiagonal, 1)) - Math.asin(maxLabelHeight / maxLabelDiagonal)
11728				));
11729				labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
11730			}
11731	
11732			me.labelRotation = labelRotation;
11733		},
11734		afterCalculateTickRotation: function() {
11735			helpers$1.callback(this.options.afterCalculateTickRotation, [this]);
11736		},
11737	
11738		//
11739	
11740		beforeFit: function() {
11741			helpers$1.callback(this.options.beforeFit, [this]);
11742		},
11743		fit: function() {
11744			var me = this;
11745			// Reset
11746			var minSize = me.minSize = {
11747				width: 0,
11748				height: 0
11749			};
11750	
11751			var chart = me.chart;
11752			var opts = me.options;
11753			var tickOpts = opts.ticks;
11754			var scaleLabelOpts = opts.scaleLabel;
11755			var gridLineOpts = opts.gridLines;
11756			var display = me._isVisible();
11757			var isBottom = opts.position === 'bottom';
11758			var isHorizontal = me.isHorizontal();
11759	
11760			// Width
11761			if (isHorizontal) {
11762				minSize.width = me.maxWidth;
11763			} else if (display) {
11764				minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts);
11765			}
11766	
11767			// height
11768			if (!isHorizontal) {
11769				minSize.height = me.maxHeight; // fill all the height
11770			} else if (display) {
11771				minSize.height = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts);
11772			}
11773	
11774			// Don't bother fitting the ticks if we are not showing the labels
11775			if (tickOpts.display && display) {
11776				var tickFonts = parseTickFontOptions(tickOpts);
11777				var labelSizes = me._getLabelSizes();
11778				var firstLabelSize = labelSizes.first;
11779				var lastLabelSize = labelSizes.last;
11780				var widestLabelSize = labelSizes.widest;
11781				var highestLabelSize = labelSizes.highest;
11782				var lineSpace = tickFonts.minor.lineHeight * 0.4;
11783				var tickPadding = tickOpts.padding;
11784	
11785				if (isHorizontal) {
11786					// A horizontal axis is more constrained by the height.
11787					var isRotated = me.labelRotation !== 0;
11788					var angleRadians = helpers$1.toRadians(me.labelRotation);
11789					var cosRotation = Math.cos(angleRadians);
11790					var sinRotation = Math.sin(angleRadians);
11791	
11792					var labelHeight = sinRotation * widestLabelSize.width
11793						+ cosRotation * (highestLabelSize.height - (isRotated ? highestLabelSize.offset : 0))
11794						+ (isRotated ? 0 : lineSpace); // padding
11795	
11796					minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
11797	
11798					var offsetLeft = me.getPixelForTick(0) - me.left;
11799					var offsetRight = me.right - me.getPixelForTick(me.getTicks().length - 1);
11800					var paddingLeft, paddingRight;
11801	
11802					// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
11803					// which means that the right padding is dominated by the font height
11804					if (isRotated) {
11805						paddingLeft = isBottom ?
11806							cosRotation * firstLabelSize.width + sinRotation * firstLabelSize.offset :
11807							sinRotation * (firstLabelSize.height - firstLabelSize.offset);
11808						paddingRight = isBottom ?
11809							sinRotation * (lastLabelSize.height - lastLabelSize.offset) :
11810							cosRotation * lastLabelSize.width + sinRotation * lastLabelSize.offset;
11811					} else {
11812						paddingLeft = firstLabelSize.width / 2;
11813						paddingRight = lastLabelSize.width / 2;
11814					}
11815	
11816					// Adjust padding taking into account changes in offsets
11817					// and add 3 px to move away from canvas edges
11818					me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3;
11819					me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3;
11820				} else {
11821					// A vertical axis is more constrained by the width. Labels are the
11822					// dominant factor here, so get that length first and account for padding
11823					var labelWidth = tickOpts.mirror ? 0 :
11824						// use lineSpace for consistency with horizontal axis
11825						// tickPadding is not implemented for horizontal
11826						widestLabelSize.width + tickPadding + lineSpace;
11827	
11828					minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth);
11829	
11830					me.paddingTop = firstLabelSize.height / 2;
11831					me.paddingBottom = lastLabelSize.height / 2;
11832				}
11833			}
11834	
11835			me.handleMargins();
11836	
11837			if (isHorizontal) {
11838				me.width = me._length = chart.width - me.margins.left - me.margins.right;
11839				me.height = minSize.height;
11840			} else {
11841				me.width = minSize.width;
11842				me.height = me._length = chart.height - me.margins.top - me.margins.bottom;
11843			}
11844		},
11845	
11846		/**
11847		 * Handle margins and padding interactions
11848		 * @private
11849		 */
11850		handleMargins: function() {
11851			var me = this;
11852			if (me.margins) {
11853				me.margins.left = Math.max(me.paddingLeft, me.margins.left);
11854				me.margins.top = Math.max(me.paddingTop, me.margins.top);
11855				me.margins.right = Math.max(me.paddingRight, me.margins.right);
11856				me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom);
11857			}
11858		},
11859	
11860		afterFit: function() {
11861			helpers$1.callback(this.options.afterFit, [this]);
11862		},
11863	
11864		// Shared Methods
11865		isHorizontal: function() {
11866			var pos = this.options.position;
11867			return pos === 'top' || pos === 'bottom';
11868		},
11869		isFullWidth: function() {
11870			return this.options.fullWidth;
11871		},
11872	
11873		// 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
11874		getRightValue: function(rawValue) {
11875			// Null and undefined values first
11876			if (isNullOrUndef(rawValue)) {
11877				return NaN;
11878			}
11879			// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
11880			if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) {
11881				return NaN;
11882			}
11883	
11884			// If it is in fact an object, dive in one more level
11885			if (rawValue) {
11886				if (this.isHorizontal()) {
11887					if (rawValue.x !== undefined) {
11888						return this.getRightValue(rawValue.x);
11889					}
11890				} else if (rawValue.y !== undefined) {
11891					return this.getRightValue(rawValue.y);
11892				}
11893			}
11894	
11895			// Value is good, return it
11896			return rawValue;
11897		},
11898	
11899		_convertTicksToLabels: function(ticks) {
11900			var me = this;
11901			var labels, i, ilen;
11902	
11903			me.ticks = ticks.map(function(tick) {
11904				return tick.value;
11905			});
11906	
11907			me.beforeTickToLabelConversion();
11908	
11909			// New implementations should return the formatted tick labels but for BACKWARD
11910			// COMPAT, we still support no return (`this.ticks` internally changed by calling
11911			// this method and supposed to contain only string values).
11912			labels = me.convertTicksToLabels(ticks) || me.ticks;
11913	
11914			me.afterTickToLabelConversion();
11915	
11916			// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
11917			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
11918				ticks[i].label = labels[i];
11919			}
11920	
11921			return labels;
11922		},
11923	
11924		/**
11925		 * @private
11926		 */
11927		_getLabelSizes: function() {
11928			var me = this;
11929			var labelSizes = me._labelSizes;
11930	
11931			if (!labelSizes) {
11932				me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.getTicks(), me.longestTextCache);
11933				me.longestLabelWidth = labelSizes.widest.width;
11934			}
11935	
11936			return labelSizes;
11937		},
11938	
11939		/**
11940		 * @private
11941		 */
11942		_parseValue: function(value) {
11943			var start, end, min, max;
11944	
11945			if (isArray(value)) {
11946				start = +this.getRightValue(value[0]);
11947				end = +this.getRightValue(value[1]);
11948				min = Math.min(start, end);
11949				max = Math.max(start, end);
11950			} else {
11951				value = +this.getRightValue(value);
11952				start = undefined;
11953				end = value;
11954				min = value;
11955				max = value;
11956			}
11957	
11958			return {
11959				min: min,
11960				max: max,
11961				start: start,
11962				end: end
11963			};
11964		},
11965	
11966		/**
11967		* @private
11968		*/
11969		_getScaleLabel: function(rawValue) {
11970			var v = this._parseValue(rawValue);
11971			if (v.start !== undefined) {
11972				return '[' + v.start + ', ' + v.end + ']';
11973			}
11974	
11975			return +this.getRightValue(rawValue);
11976		},
11977	
11978		/**
11979		 * Used to get the value to display in the tooltip for the data at the given index
11980		 * @param index
11981		 * @param datasetIndex
11982		 */
11983		getLabelForIndex: helpers$1.noop,
11984	
11985		/**
11986		 * Returns the location of the given data point. Value can either be an index or a numerical value
11987		 * The coordinate (0, 0) is at the upper-left corner of the canvas
11988		 * @param value
11989		 * @param index
11990		 * @param datasetIndex
11991		 */
11992		getPixelForValue: helpers$1.noop,
11993	
11994		/**
11995		 * Used to get the data value from a given pixel. This is the inverse of getPixelForValue
11996		 * The coordinate (0, 0) is at the upper-left corner of the canvas
11997		 * @param pixel
11998		 */
11999		getValueForPixel: helpers$1.noop,
12000	
12001		/**
12002		 * Returns the location of the tick at the given index
12003		 * The coordinate (0, 0) is at the upper-left corner of the canvas
12004		 */
12005		getPixelForTick: function(index) {
12006			var me = this;
12007			var offset = me.options.offset;
12008			var numTicks = me._ticks.length;
12009			var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1);
12010	
12011			return index < 0 || index > numTicks - 1
12012				? null
12013				: me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0));
12014		},
12015	
12016		/**
12017		 * Utility for getting the pixel location of a percentage of scale
12018		 * The coordinate (0, 0) is at the upper-left corner of the canvas
12019		 */
12020		getPixelForDecimal: function(decimal) {
12021			var me = this;
12022	
12023			if (me._reversePixels) {
12024				decimal = 1 - decimal;
12025			}
12026	
12027			return me._startPixel + decimal * me._length;
12028		},
12029	
12030		getDecimalForPixel: function(pixel) {
12031			var decimal = (pixel - this._startPixel) / this._length;
12032			return this._reversePixels ? 1 - decimal : decimal;
12033		},
12034	
12035		/**
12036		 * Returns the pixel for the minimum chart value
12037		 * The coordinate (0, 0) is at the upper-left corner of the canvas
12038		 */
12039		getBasePixel: function() {
12040			return this.getPixelForValue(this.getBaseValue());
12041		},
12042	
12043		getBaseValue: function() {
12044			var me = this;
12045			var min = me.min;
12046			var max = me.max;
12047	
12048			return me.beginAtZero ? 0 :
12049				min < 0 && max < 0 ? max :
12050				min > 0 && max > 0 ? min :
12051				0;
12052		},
12053	
12054		/**
12055		 * Returns a subset of ticks to be plotted to avoid overlapping labels.
12056		 * @private
12057		 */
12058		_autoSkip: function(ticks) {
12059			var me = this;
12060			var tickOpts = me.options.ticks;
12061			var axisLength = me._length;
12062			var ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1;
12063			var majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
12064			var numMajorIndices = majorIndices.length;
12065			var first = majorIndices[0];
12066			var last = majorIndices[numMajorIndices - 1];
12067			var i, ilen, spacing, avgMajorSpacing;
12068	
12069			// If there are too many major ticks to display them all
12070			if (numMajorIndices > ticksLimit) {
12071				skipMajors(ticks, majorIndices, numMajorIndices / ticksLimit);
12072				return nonSkipped(ticks);
12073			}
12074	
12075			spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit);
12076	
12077			if (numMajorIndices > 0) {
12078				for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
12079					skip(ticks, spacing, majorIndices[i], majorIndices[i + 1]);
12080				}
12081				avgMajorSpacing = numMajorIndices > 1 ? (last - first) / (numMajorIndices - 1) : null;
12082				skip(ticks, spacing, helpers$1.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
12083				skip(ticks, spacing, last, helpers$1.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
12084				return nonSkipped(ticks);
12085			}
12086			skip(ticks, spacing);
12087			return nonSkipped(ticks);
12088		},
12089	
12090		/**
12091		 * @private
12092		 */
12093		_tickSize: function() {
12094			var me = this;
12095			var optionTicks = me.options.ticks;
12096	
12097			// Calculate space needed by label in axis direction.
12098			var rot = helpers$1.toRadians(me.labelRotation);
12099			var cos = Math.abs(Math.cos(rot));
12100			var sin = Math.abs(Math.sin(rot));
12101	
12102			var labelSizes = me._getLabelSizes();
12103			var padding = optionTicks.autoSkipPadding || 0;
12104			var w = labelSizes ? labelSizes.widest.width + padding : 0;
12105			var h = labelSizes ? labelSizes.highest.height + padding : 0;
12106	
12107			// Calculate space needed for 1 tick in axis direction.
12108			return me.isHorizontal()
12109				? h * cos > w * sin ? w / cos : h / sin
12110				: h * sin < w * cos ? h / cos : w / sin;
12111		},
12112	
12113		/**
12114		 * @private
12115		 */
12116		_isVisible: function() {
12117			var me = this;
12118			var chart = me.chart;
12119			var display = me.options.display;
12120			var i, ilen, meta;
12121	
12122			if (display !== 'auto') {
12123				return !!display;
12124			}
12125	
12126			// When 'auto', the scale is visible if at least one associated dataset is visible.
12127			for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
12128				if (chart.isDatasetVisible(i)) {
12129					meta = chart.getDatasetMeta(i);
12130					if (meta.xAxisID === me.id || meta.yAxisID === me.id) {
12131						return true;
12132					}
12133				}
12134			}
12135	
12136			return false;
12137		},
12138	
12139		/**
12140		 * @private
12141		 */
12142		_computeGridLineItems: function(chartArea) {
12143			var me = this;
12144			var chart = me.chart;
12145			var options = me.options;
12146			var gridLines = options.gridLines;
12147			var position = options.position;
12148			var offsetGridLines = gridLines.offsetGridLines;
12149			var isHorizontal = me.isHorizontal();
12150			var ticks = me._ticksToDraw;
12151			var ticksLength = ticks.length + (offsetGridLines ? 1 : 0);
12152	
12153			var tl = getTickMarkLength(gridLines);
12154			var items = [];
12155			var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
12156			var axisHalfWidth = axisWidth / 2;
12157			var alignPixel = helpers$1._alignPixel;
12158			var alignBorderValue = function(pixel) {
12159				return alignPixel(chart, pixel, axisWidth);
12160			};
12161			var borderValue, i, tick, lineValue, alignedLineValue;
12162			var tx1, ty1, tx2, ty2, x1, y1, x2, y2, lineWidth, lineColor, borderDash, borderDashOffset;
12163	
12164			if (position === 'top') {
12165				borderValue = alignBorderValue(me.bottom);
12166				ty1 = me.bottom - tl;
12167				ty2 = borderValue - axisHalfWidth;
12168				y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
12169				y2 = chartArea.bottom;
12170			} else if (position === 'bottom') {
12171				borderValue = alignBorderValue(me.top);
12172				y1 = chartArea.top;
12173				y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
12174				ty1 = borderValue + axisHalfWidth;
12175				ty2 = me.top + tl;
12176			} else if (position === 'left') {
12177				borderValue = alignBorderValue(me.right);
12178				tx1 = me.right - tl;
12179				tx2 = borderValue - axisHalfWidth;
12180				x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
12181				x2 = chartArea.right;
12182			} else {
12183				borderValue = alignBorderValue(me.left);
12184				x1 = chartArea.left;
12185				x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
12186				tx1 = borderValue + axisHalfWidth;
12187				tx2 = me.left + tl;
12188			}
12189	
12190			for (i = 0; i < ticksLength; ++i) {
12191				tick = ticks[i] || {};
12192	
12193				// autoskipper skipped this tick (#4635)
12194				if (isNullOrUndef(tick.label) && i < ticks.length) {
12195					continue;
12196				}
12197	
12198				if (i === me.zeroLineIndex && options.offset === offsetGridLines) {
12199					// Draw the first index specially
12200					lineWidth = gridLines.zeroLineWidth;
12201					lineColor = gridLines.zeroLineColor;
12202					borderDash = gridLines.zeroLineBorderDash || [];
12203					borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0;
12204				} else {
12205					lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, i, 1);
12206					lineColor = valueAtIndexOrDefault(gridLines.color, i, 'rgba(0,0,0,0.1)');
12207					borderDash = gridLines.borderDash || [];
12208					borderDashOffset = gridLines.borderDashOffset || 0.0;
12209				}
12210	
12211				lineValue = getPixelForGridLine(me, tick._index || i, offsetGridLines);
12212	
12213				// Skip if the pixel is out of the range
12214				if (lineValue === undefined) {
12215					continue;
12216				}
12217	
12218				alignedLineValue = alignPixel(chart, lineValue, lineWidth);
12219	
12220				if (isHorizontal) {
12221					tx1 = tx2 = x1 = x2 = alignedLineValue;
12222				} else {
12223					ty1 = ty2 = y1 = y2 = alignedLineValue;
12224				}
12225	
12226				items.push({
12227					tx1: tx1,
12228					ty1: ty1,
12229					tx2: tx2,
12230					ty2: ty2,
12231					x1: x1,
12232					y1: y1,
12233					x2: x2,
12234					y2: y2,
12235					width: lineWidth,
12236					color: lineColor,
12237					borderDash: borderDash,
12238					borderDashOffset: borderDashOffset,
12239				});
12240			}
12241	
12242			items.ticksLength = ticksLength;
12243			items.borderValue = borderValue;
12244	
12245			return items;
12246		},
12247	
12248		/**
12249		 * @private
12250		 */
12251		_computeLabelItems: function() {
12252			var me = this;
12253			var options = me.options;
12254			var optionTicks = options.ticks;
12255			var position = options.position;
12256			var isMirrored = optionTicks.mirror;
12257			var isHorizontal = me.isHorizontal();
12258			var ticks = me._ticksToDraw;
12259			var fonts = parseTickFontOptions(optionTicks);
12260			var tickPadding = optionTicks.padding;
12261			var tl = getTickMarkLength(options.gridLines);
12262			var rotation = -helpers$1.toRadians(me.labelRotation);
12263			var items = [];
12264			var i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
12265	
12266			if (position === 'top') {
12267				y = me.bottom - tl - tickPadding;
12268				textAlign = !rotation ? 'center' : 'left';
12269			} else if (position === 'bottom') {
12270				y = me.top + tl + tickPadding;
12271				textAlign = !rotation ? 'center' : 'right';
12272			} else if (position === 'left') {
12273				x = me.right - (isMirrored ? 0 : tl) - tickPadding;
12274				textAlign = isMirrored ? 'left' : 'right';
12275			} else {
12276				x = me.left + (isMirrored ? 0 : tl) + tickPadding;
12277				textAlign = isMirrored ? 'right' : 'left';
12278			}
12279	
12280			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
12281				tick = ticks[i];
12282				label = tick.label;
12283	
12284				// autoskipper skipped this tick (#4635)
12285				if (isNullOrUndef(label)) {
12286					continue;
12287				}
12288	
12289				pixel = me.getPixelForTick(tick._index || i) + optionTicks.labelOffset;
12290				font = tick.major ? fonts.major : fonts.minor;
12291				lineHeight = font.lineHeight;
12292				lineCount = isArray(label) ? label.length : 1;
12293	
12294				if (isHorizontal) {
12295					x = pixel;
12296					textOffset = position === 'top'
12297						? ((!rotation ? 0.5 : 1) - lineCount) * lineHeight
12298						: (!rotation ? 0.5 : 0) * lineHeight;
12299				} else {
12300					y = pixel;
12301					textOffset = (1 - lineCount) * lineHeight / 2;
12302				}
12303	
12304				items.push({
12305					x: x,
12306					y: y,
12307					rotation: rotation,
12308					label: label,
12309					font: font,
12310					textOffset: textOffset,
12311					textAlign: textAlign
12312				});
12313			}
12314	
12315			return items;
12316		},
12317	
12318		/**
12319		 * @private
12320		 */
12321		_drawGrid: function(chartArea) {
12322			var me = this;
12323			var gridLines = me.options.gridLines;
12324	
12325			if (!gridLines.display) {
12326				return;
12327			}
12328	
12329			var ctx = me.ctx;
12330			var chart = me.chart;
12331			var alignPixel = helpers$1._alignPixel;
12332			var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
12333			var items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea));
12334			var width, color, i, ilen, item;
12335	
12336			for (i = 0, ilen = items.length; i < ilen; ++i) {
12337				item = items[i];
12338				width = item.width;
12339				color = item.color;
12340	
12341				if (width && color) {
12342					ctx.save();
12343					ctx.lineWidth = width;
12344					ctx.strokeStyle = color;
12345					if (ctx.setLineDash) {
12346						ctx.setLineDash(item.borderDash);
12347						ctx.lineDashOffset = item.borderDashOffset;
12348					}
12349	
12350					ctx.beginPath();
12351	
12352					if (gridLines.drawTicks) {
12353						ctx.moveTo(item.tx1, item.ty1);
12354						ctx.lineTo(item.tx2, item.ty2);
12355					}
12356	
12357					if (gridLines.drawOnChartArea) {
12358						ctx.moveTo(item.x1, item.y1);
12359						ctx.lineTo(item.x2, item.y2);
12360					}
12361	
12362					ctx.stroke();
12363					ctx.restore();
12364				}
12365			}
12366	
12367			if (axisWidth) {
12368				// Draw the line at the edge of the axis
12369				var firstLineWidth = axisWidth;
12370				var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 1);
12371				var borderValue = items.borderValue;
12372				var x1, x2, y1, y2;
12373	
12374				if (me.isHorizontal()) {
12375					x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2;
12376					x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2;
12377					y1 = y2 = borderValue;
12378				} else {
12379					y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2;
12380					y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2;
12381					x1 = x2 = borderValue;
12382				}
12383	
12384				ctx.lineWidth = axisWidth;
12385				ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0);
12386				ctx.beginPath();
12387				ctx.moveTo(x1, y1);
12388				ctx.lineTo(x2, y2);
12389				ctx.stroke();
12390			}
12391		},
12392	
12393		/**
12394		 * @private
12395		 */
12396		_drawLabels: function() {
12397			var me = this;
12398			var optionTicks = me.options.ticks;
12399	
12400			if (!optionTicks.display) {
12401				return;
12402			}
12403	
12404			var ctx = me.ctx;
12405			var items = me._labelItems || (me._labelItems = me._computeLabelItems());
12406			var i, j, ilen, jlen, item, tickFont, label, y;
12407	
12408			for (i = 0, ilen = items.length; i < ilen; ++i) {
12409				item = items[i];
12410				tickFont = item.font;
12411	
12412				// Make sure we draw text in the correct color and font
12413				ctx.save();
12414				ctx.translate(item.x, item.y);
12415				ctx.rotate(item.rotation);
12416				ctx.font = tickFont.string;
12417				ctx.fillStyle = tickFont.color;
12418				ctx.textBaseline = 'middle';
12419				ctx.textAlign = item.textAlign;
12420	
12421				label = item.label;
12422				y = item.textOffset;
12423				if (isArray(label)) {
12424					for (j = 0, jlen = label.length; j < jlen; ++j) {
12425						// We just make sure the multiline element is a string here..
12426						ctx.fillText('' + label[j], 0, y);
12427						y += tickFont.lineHeight;
12428					}
12429				} else {
12430					ctx.fillText(label, 0, y);
12431				}
12432				ctx.restore();
12433			}
12434		},
12435	
12436		/**
12437		 * @private
12438		 */
12439		_drawTitle: function() {
12440			var me = this;
12441			var ctx = me.ctx;
12442			var options = me.options;
12443			var scaleLabel = options.scaleLabel;
12444	
12445			if (!scaleLabel.display) {
12446				return;
12447			}
12448	
12449			var scaleLabelFontColor = valueOrDefault$a(scaleLabel.fontColor, core_defaults.global.defaultFontColor);
12450			var scaleLabelFont = helpers$1.options._parseFont(scaleLabel);
12451			var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding);
12452			var halfLineHeight = scaleLabelFont.lineHeight / 2;
12453			var position = options.position;
12454			var rotation = 0;
12455			var scaleLabelX, scaleLabelY;
12456	
12457			if (me.isHorizontal()) {
12458				scaleLabelX = me.left + me.width / 2; // midpoint of the width
12459				scaleLabelY = position === 'bottom'
12460					? me.bottom - halfLineHeight - scaleLabelPadding.bottom
12461					: me.top + halfLineHeight + scaleLabelPadding.top;
12462			} else {
12463				var isLeft = position === 'left';
12464				scaleLabelX = isLeft
12465					? me.left + halfLineHeight + scaleLabelPadding.top
12466					: me.right - halfLineHeight - scaleLabelPadding.top;
12467				scaleLabelY = me.top + me.height / 2;
12468				rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
12469			}
12470	
12471			ctx.save();
12472			ctx.translate(scaleLabelX, scaleLabelY);
12473			ctx.rotate(rotation);
12474			ctx.textAlign = 'center';
12475			ctx.textBaseline = 'middle';
12476			ctx.fillStyle = scaleLabelFontColor; // render in correct colour
12477			ctx.font = scaleLabelFont.string;
12478			ctx.fillText(scaleLabel.labelString, 0, 0);
12479			ctx.restore();
12480		},
12481	
12482		draw: function(chartArea) {
12483			var me = this;
12484	
12485			if (!me._isVisible()) {
12486				return;
12487			}
12488	
12489			me._drawGrid(chartArea);
12490			me._drawTitle();
12491			me._drawLabels();
12492		},
12493	
12494		/**
12495		 * @private
12496		 */
12497		_layers: function() {
12498			var me = this;
12499			var opts = me.options;
12500			var tz = opts.ticks && opts.ticks.z || 0;
12501			var gz = opts.gridLines && opts.gridLines.z || 0;
12502	
12503			if (!me._isVisible() || tz === gz || me.draw !== me._draw) {
12504				// backward compatibility: draw has been overridden by custom scale
12505				return [{
12506					z: tz,
12507					draw: function() {
12508						me.draw.apply(me, arguments);
12509					}
12510				}];
12511			}
12512	
12513			return [{
12514				z: gz,
12515				draw: function() {
12516					me._drawGrid.apply(me, arguments);
12517					me._drawTitle.apply(me, arguments);
12518				}
12519			}, {
12520				z: tz,
12521				draw: function() {
12522					me._drawLabels.apply(me, arguments);
12523				}
12524			}];
12525		},
12526	
12527		/**
12528		 * @private
12529		 */
12530		_getMatchingVisibleMetas: function(type) {
12531			var me = this;
12532			var isHorizontal = me.isHorizontal();
12533			return me.chart._getSortedVisibleDatasetMetas()
12534				.filter(function(meta) {
12535					return (!type || meta.type === type)
12536						&& (isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id);
12537				});
12538		}
12539	});
12540	
12541	Scale.prototype._draw = Scale.prototype.draw;
12542	
12543	var core_scale = Scale;
12544	
12545	var isNullOrUndef$1 = helpers$1.isNullOrUndef;
12546	
12547	var defaultConfig = {
12548		position: 'bottom'
12549	};
12550	
12551	var scale_category = core_scale.extend({
12552		determineDataLimits: function() {
12553			var me = this;
12554			var labels = me._getLabels();
12555			var ticksOpts = me.options.ticks;
12556			var min = ticksOpts.min;
12557			var max = ticksOpts.max;
12558			var minIndex = 0;
12559			var maxIndex = labels.length - 1;
12560			var findIndex;
12561	
12562			if (min !== undefined) {
12563				// user specified min value
12564				findIndex = labels.indexOf(min);
12565				if (findIndex >= 0) {
12566					minIndex = findIndex;
12567				}
12568			}
12569	
12570			if (max !== undefined) {
12571				// user specified max value
12572				findIndex = labels.indexOf(max);
12573				if (findIndex >= 0) {
12574					maxIndex = findIndex;
12575				}
12576			}
12577	
12578			me.minIndex = minIndex;
12579			me.maxIndex = maxIndex;
12580			me.min = labels[minIndex];
12581			me.max = labels[maxIndex];
12582		},
12583	
12584		buildTicks: function() {
12585			var me = this;
12586			var labels = me._getLabels();
12587			var minIndex = me.minIndex;
12588			var maxIndex = me.maxIndex;
12589	
12590			// If we are viewing some subset of labels, slice the original array
12591			me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1);
12592		},
12593	
12594		getLabelForIndex: function(index, datasetIndex) {
12595			var me = this;
12596			var chart = me.chart;
12597	
12598			if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) {
12599				return me.getRightValue(chart.data.datasets[datasetIndex].data[index]);
12600			}
12601	
12602			return me._getLabels()[index];
12603		},
12604	
12605		_configure: function() {
12606			var me = this;
12607			var offset = me.options.offset;
12608			var ticks = me.ticks;
12609	
12610			core_scale.prototype._configure.call(me);
12611	
12612			if (!me.isHorizontal()) {
12613				// For backward compatibility, vertical category scale reverse is inverted.
12614				me._reversePixels = !me._reversePixels;
12615			}
12616	
12617			if (!ticks) {
12618				return;
12619			}
12620	
12621			me._startValue = me.minIndex - (offset ? 0.5 : 0);
12622			me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1);
12623		},
12624	
12625		// Used to get data value locations.  Value can either be an index or a numerical value
12626		getPixelForValue: function(value, index, datasetIndex) {
12627			var me = this;
12628			var valueCategory, labels, idx;
12629	
12630			if (!isNullOrUndef$1(index) && !isNullOrUndef$1(datasetIndex)) {
12631				value = me.chart.data.datasets[datasetIndex].data[index];
12632			}
12633	
12634			// If value is a data object, then index is the index in the data array,
12635			// not the index of the scale. We need to change that.
12636			if (!isNullOrUndef$1(value)) {
12637				valueCategory = me.isHorizontal() ? value.x : value.y;
12638			}
12639			if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
12640				labels = me._getLabels();
12641				value = helpers$1.valueOrDefault(valueCategory, value);
12642				idx = labels.indexOf(value);
12643				index = idx !== -1 ? idx : index;
12644				if (isNaN(index)) {
12645					index = value;
12646				}
12647			}
12648			return me.getPixelForDecimal((index - me._startValue) / me._valueRange);
12649		},
12650	
12651		getPixelForTick: function(index) {
12652			var ticks = this.ticks;
12653			return index < 0 || index > ticks.length - 1
12654				? null
12655				: this.getPixelForValue(ticks[index], index + this.minIndex);
12656		},
12657	
12658		getValueForPixel: function(pixel) {
12659			var me = this;
12660			var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
12661			return Math.min(Math.max(value, 0), me.ticks.length - 1);
12662		},
12663	
12664		getBasePixel: function() {
12665			return this.bottom;
12666		}
12667	});
12668	
12669	// INTERNAL: static default options, registered in src/index.js
12670	var _defaults = defaultConfig;
12671	scale_category._defaults = _defaults;
12672	
12673	var noop = helpers$1.noop;
12674	var isNullOrUndef$2 = helpers$1.isNullOrUndef;
12675	
12676	/**
12677	 * Generate a set of linear ticks
12678	 * @param generationOptions the options used to generate the ticks
12679	 * @param dataRange the range of the data
12680	 * @returns {number[]} array of tick values
12681	 */
12682	function generateTicks(generationOptions, dataRange) {
12683		var ticks = [];
12684		// To get a "nice" value for the tick spacing, we will use the appropriately named
12685		// "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
12686		// for details.
12687	
12688		var MIN_SPACING = 1e-14;
12689		var stepSize = generationOptions.stepSize;
12690		var unit = stepSize || 1;
12691		var maxNumSpaces = generationOptions.maxTicks - 1;
12692		var min = generationOptions.min;
12693		var max = generationOptions.max;
12694		var precision = generationOptions.precision;
12695		var rmin = dataRange.min;
12696		var rmax = dataRange.max;
12697		var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit;
12698		var factor, niceMin, niceMax, numSpaces;
12699	
12700		// Beyond MIN_SPACING floating point numbers being to lose precision
12701		// such that we can't do the math necessary to generate ticks
12702		if (spacing < MIN_SPACING && isNullOrUndef$2(min) && isNullOrUndef$2(max)) {
12703			return [rmin, rmax];
12704		}
12705	
12706		numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
12707		if (numSpaces > maxNumSpaces) {
12708			// If the calculated num of spaces exceeds maxNumSpaces, recalculate it
12709			spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit;
12710		}
12711	
12712		if (stepSize || isNullOrUndef$2(precision)) {
12713			// If a precision is not specified, calculate factor based on spacing
12714			factor = Math.pow(10, helpers$1._decimalPlaces(spacing));
12715		} else {
12716			// If the user specified a precision, round to that number of decimal places
12717			factor = Math.pow(10, precision);
12718			spacing = Math.ceil(spacing * factor) / factor;
12719		}
12720	
12721		niceMin = Math.floor(rmin / spacing) * spacing;
12722		niceMax = Math.ceil(rmax / spacing) * spacing;
12723	
12724		// If min, max and stepSize is set and they make an evenly spaced scale use it.
12725		if (stepSize) {
12726			// If very close to our whole number, use it.
12727			if (!isNullOrUndef$2(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) {
12728				niceMin = min;
12729			}
12730			if (!isNullOrUndef$2(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) {
12731				niceMax = max;
12732			}
12733		}
12734	
12735		numSpaces = (niceMax - niceMin) / spacing;
12736		// If very close to our rounded value, use it.
12737		if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
12738			numSpaces = Math.round(numSpaces);
12739		} else {
12740			numSpaces = Math.ceil(numSpaces);
12741		}
12742	
12743		niceMin = Math.round(niceMin * factor) / factor;
12744		niceMax = Math.round(niceMax * factor) / factor;
12745		ticks.push(isNullOrUndef$2(min) ? niceMin : min);
12746		for (var j = 1; j < numSpaces; ++j) {
12747			ticks.push(Math.round((niceMin + j * spacing) * factor) / factor);
12748		}
12749		ticks.push(isNullOrUndef$2(max) ? niceMax : max);
12750	
12751		return ticks;
12752	}
12753	
12754	var scale_linearbase = core_scale.extend({
12755		getRightValue: function(value) {
12756			if (typeof value === 'string') {
12757				return +value;
12758			}
12759			return core_scale.prototype.getRightValue.call(this, value);
12760		},
12761	
12762		handleTickRangeOptions: function() {
12763			var me = this;
12764			var opts = me.options;
12765			var tickOpts = opts.ticks;
12766	
12767			// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
12768			// do nothing since that would make the chart weird. If the user really wants a weird chart
12769			// axis, they can manually override it
12770			if (tickOpts.beginAtZero) {
12771				var minSign = helpers$1.sign(me.min);
12772				var maxSign = helpers$1.sign(me.max);
12773	
12774				if (minSign < 0 && maxSign < 0) {
12775					// move the top up to 0
12776					me.max = 0;
12777				} else if (minSign > 0 && maxSign > 0) {
12778					// move the bottom down to 0
12779					me.min = 0;
12780				}
12781			}
12782	
12783			var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
12784			var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
12785	
12786			if (tickOpts.min !== undefined) {
12787				me.min = tickOpts.min;
12788			} else if (tickOpts.suggestedMin !== undefined) {
12789				if (me.min === null) {
12790					me.min = tickOpts.suggestedMin;
12791				} else {
12792					me.min = Math.min(me.min, tickOpts.suggestedMin);
12793				}
12794			}
12795	
12796			if (tickOpts.max !== undefined) {
12797				me.max = tickOpts.max;
12798			} else if (tickOpts.suggestedMax !== undefined) {
12799				if (me.max === null) {
12800					me.max = tickOpts.suggestedMax;
12801				} else {
12802					me.max = Math.max(me.max, tickOpts.suggestedMax);
12803				}
12804			}
12805	
12806			if (setMin !== setMax) {
12807				// We set the min or the max but not both.
12808				// So ensure that our range is good
12809				// Inverted or 0 length range can happen when
12810				// ticks.min is set, and no datasets are visible
12811				if (me.min >= me.max) {
12812					if (setMin) {
12813						me.max = me.min + 1;
12814					} else {
12815						me.min = me.max - 1;
12816					}
12817				}
12818			}
12819	
12820			if (me.min === me.max) {
12821				me.max++;
12822	
12823				if (!tickOpts.beginAtZero) {
12824					me.min--;
12825				}
12826			}
12827		},
12828	
12829		getTickLimit: function() {
12830			var me = this;
12831			var tickOpts = me.options.ticks;
12832			var stepSize = tickOpts.stepSize;
12833			var maxTicksLimit = tickOpts.maxTicksLimit;
12834			var maxTicks;
12835	
12836			if (stepSize) {
12837				maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
12838			} else {
12839				maxTicks = me._computeTickLimit();
12840				maxTicksLimit = maxTicksLimit || 11;
12841			}
12842	
12843			if (maxTicksLimit) {
12844				maxTicks = Math.min(maxTicksLimit, maxTicks);
12845			}
12846	
12847			return maxTicks;
12848		},
12849	
12850		_computeTickLimit: function() {
12851			return Number.POSITIVE_INFINITY;
12852		},
12853	
12854		handleDirectionalChanges: noop,
12855	
12856		buildTicks: function() {
12857			var me = this;
12858			var opts = me.options;
12859			var tickOpts = opts.ticks;
12860	
12861			// Figure out what the max number of ticks we can support it is based on the size of
12862			// the axis area. For now, we say that the minimum tick spacing in pixels must be 40
12863			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12864			// the graph. Make sure we always have at least 2 ticks
12865			var maxTicks = me.getTickLimit();
12866			maxTicks = Math.max(2, maxTicks);
12867	
12868			var numericGeneratorOptions = {
12869				maxTicks: maxTicks,
12870				min: tickOpts.min,
12871				max: tickOpts.max,
12872				precision: tickOpts.precision,
12873				stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
12874			};
12875			var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
12876	
12877			me.handleDirectionalChanges();
12878	
12879			// At this point, we need to update our max and min given the tick values since we have expanded the
12880			// range of the scale
12881			me.max = helpers$1.max(ticks);
12882			me.min = helpers$1.min(ticks);
12883	
12884			if (tickOpts.reverse) {
12885				ticks.reverse();
12886	
12887				me.start = me.max;
12888				me.end = me.min;
12889			} else {
12890				me.start = me.min;
12891				me.end = me.max;
12892			}
12893		},
12894	
12895		convertTicksToLabels: function() {
12896			var me = this;
12897			me.ticksAsNumbers = me.ticks.slice();
12898			me.zeroLineIndex = me.ticks.indexOf(0);
12899	
12900			core_scale.prototype.convertTicksToLabels.call(me);
12901		},
12902	
12903		_configure: function() {
12904			var me = this;
12905			var ticks = me.getTicks();
12906			var start = me.min;
12907			var end = me.max;
12908			var offset;
12909	
12910			core_scale.prototype._configure.call(me);
12911	
12912			if (me.options.offset && ticks.length) {
12913				offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
12914				start -= offset;
12915				end += offset;
12916			}
12917			me._startValue = start;
12918			me._endValue = end;
12919			me._valueRange = end - start;
12920		}
12921	});
12922	
12923	var defaultConfig$1 = {
12924		position: 'left',
12925		ticks: {
12926			callback: core_ticks.formatters.linear
12927		}
12928	};
12929	
12930	var DEFAULT_MIN = 0;
12931	var DEFAULT_MAX = 1;
12932	
12933	function getOrCreateStack(stacks, stacked, meta) {
12934		var key = [
12935			meta.type,
12936			// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12937			stacked === undefined && meta.stack === undefined ? meta.index : '',
12938			meta.stack
12939		].join('.');
12940	
12941		if (stacks[key] === undefined) {
12942			stacks[key] = {
12943				pos: [],
12944				neg: []
12945			};
12946		}
12947	
12948		return stacks[key];
12949	}
12950	
12951	function stackData(scale, stacks, meta, data) {
12952		var opts = scale.options;
12953		var stacked = opts.stacked;
12954		var stack = getOrCreateStack(stacks, stacked, meta);
12955		var pos = stack.pos;
12956		var neg = stack.neg;
12957		var ilen = data.length;
12958		var i, value;
12959	
12960		for (i = 0; i < ilen; ++i) {
12961			value = scale._parseValue(data[i]);
12962			if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) {
12963				continue;
12964			}
12965	
12966			pos[i] = pos[i] || 0;
12967			neg[i] = neg[i] || 0;
12968	
12969			if (opts.relativePoints) {
12970				pos[i] = 100;
12971			} else if (value.min < 0 || value.max < 0) {
12972				neg[i] += value.min;
12973			} else {
12974				pos[i] += value.max;
12975			}
12976		}
12977	}
12978	
12979	function updateMinMax(scale, meta, data) {
12980		var ilen = data.length;
12981		var i, value;
12982	
12983		for (i = 0; i < ilen; ++i) {
12984			value = scale._parseValue(data[i]);
12985			if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) {
12986				continue;
12987			}
12988	
12989			scale.min = Math.min(scale.min, value.min);
12990			scale.max = Math.max(scale.max, value.max);
12991		}
12992	}
12993	
12994	var scale_linear = scale_linearbase.extend({
12995		determineDataLimits: function() {
12996			var me = this;
12997			var opts = me.options;
12998			var chart = me.chart;
12999			var datasets = chart.data.datasets;
13000			var metasets = me._getMatchingVisibleMetas();
13001			var hasStacks = opts.stacked;
13002			var stacks = {};
13003			var ilen = metasets.length;
13004			var i, meta, data, values;
13005	
13006			me.min = Number.POSITIVE_INFINITY;
13007			me.max = Number.NEGATIVE_INFINITY;
13008	
13009			if (hasStacks === undefined) {
13010				for (i = 0; !hasStacks && i < ilen; ++i) {
13011					meta = metasets[i];
13012					hasStacks = meta.stack !== undefined;
13013				}
13014			}
13015	
13016			for (i = 0; i < ilen; ++i) {
13017				meta = metasets[i];
13018				data = datasets[meta.index].data;
13019				if (hasStacks) {
13020					stackData(me, stacks, meta, data);
13021				} else {
13022					updateMinMax(me, meta, data);
13023				}
13024			}
13025	
13026			helpers$1.each(stacks, function(stackValues) {
13027				values = stackValues.pos.concat(stackValues.neg);
13028				me.min = Math.min(me.min, helpers$1.min(values));
13029				me.max = Math.max(me.max, helpers$1.max(values));
13030			});
13031	
13032			me.min = helpers$1.isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
13033			me.max = helpers$1.isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
13034	
13035			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
13036			me.handleTickRangeOptions();
13037		},
13038	
13039		// Returns the maximum number of ticks based on the scale dimension
13040		_computeTickLimit: function() {
13041			var me = this;
13042			var tickFont;
13043	
13044			if (me.isHorizontal()) {
13045				return Math.ceil(me.width / 40);
13046			}
13047			tickFont = helpers$1.options._parseFont(me.options.ticks);
13048			return Math.ceil(me.height / tickFont.lineHeight);
13049		},
13050	
13051		// Called after the ticks are built. We need
13052		handleDirectionalChanges: function() {
13053			if (!this.isHorizontal()) {
13054				// We are in a vertical orientation. The top value is the highest. So reverse the array
13055				this.ticks.reverse();
13056			}
13057		},
13058	
13059		getLabelForIndex: function(index, datasetIndex) {
13060			return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
13061		},
13062	
13063		// Utils
13064		getPixelForValue: function(value) {
13065			var me = this;
13066			return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange);
13067		},
13068	
13069		getValueForPixel: function(pixel) {
13070			return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
13071		},
13072	
13073		getPixelForTick: function(index) {
13074			var ticks = this.ticksAsNumbers;
13075			if (index < 0 || index > ticks.length - 1) {
13076				return null;
13077			}
13078			return this.getPixelForValue(ticks[index]);
13079		}
13080	});
13081	
13082	// INTERNAL: static default options, registered in src/index.js
13083	var _defaults$1 = defaultConfig$1;
13084	scale_linear._defaults = _defaults$1;
13085	
13086	var valueOrDefault$b = helpers$1.valueOrDefault;
13087	var log10 = helpers$1.math.log10;
13088	
13089	/**
13090	 * Generate a set of logarithmic ticks
13091	 * @param generationOptions the options used to generate the ticks
13092	 * @param dataRange the range of the data
13093	 * @returns {number[]} array of tick values
13094	 */
13095	function generateTicks$1(generationOptions, dataRange) {
13096		var ticks = [];
13097	
13098		var tickVal = valueOrDefault$b(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min))));
13099	
13100		var endExp = Math.floor(log10(dataRange.max));
13101		var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
13102		var exp, significand;
13103	
13104		if (tickVal === 0) {
13105			exp = Math.floor(log10(dataRange.minNotZero));
13106			significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
13107	
13108			ticks.push(tickVal);
13109			tickVal = significand * Math.pow(10, exp);
13110		} else {
13111			exp = Math.floor(log10(tickVal));
13112			significand = Math.floor(tickVal / Math.pow(10, exp));
13113		}
13114		var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
13115	
13116		do {
13117			ticks.push(tickVal);
13118	
13119			++significand;
13120			if (significand === 10) {
13121				significand = 1;
13122				++exp;
13123				precision = exp >= 0 ? 1 : precision;
13124			}
13125	
13126			tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
13127		} while (exp < endExp || (exp === endExp && significand < endSignificand));
13128	
13129		var lastTick = valueOrDefault$b(generationOptions.max, tickVal);
13130		ticks.push(lastTick);
13131	
13132		return ticks;
13133	}
13134	
13135	var defaultConfig$2 = {
13136		position: 'left',
13137	
13138		// label settings
13139		ticks: {
13140			callback: core_ticks.formatters.logarithmic
13141		}
13142	};
13143	
13144	// TODO(v3): change this to positiveOrDefault
13145	function nonNegativeOrDefault(value, defaultValue) {
13146		return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue;
13147	}
13148	
13149	var scale_logarithmic = core_scale.extend({
13150		determineDataLimits: function() {
13151			var me = this;
13152			var opts = me.options;
13153			var chart = me.chart;
13154			var datasets = chart.data.datasets;
13155			var isHorizontal = me.isHorizontal();
13156			function IDMatches(meta) {
13157				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
13158			}
13159			var datasetIndex, meta, value, data, i, ilen;
13160	
13161			// Calculate Range
13162			me.min = Number.POSITIVE_INFINITY;
13163			me.max = Number.NEGATIVE_INFINITY;
13164			me.minNotZero = Number.POSITIVE_INFINITY;
13165	
13166			var hasStacks = opts.stacked;
13167			if (hasStacks === undefined) {
13168				for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
13169					meta = chart.getDatasetMeta(datasetIndex);
13170					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
13171						meta.stack !== undefined) {
13172						hasStacks = true;
13173						break;
13174					}
13175				}
13176			}
13177	
13178			if (opts.stacked || hasStacks) {
13179				var valuesPerStack = {};
13180	
13181				for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
13182					meta = chart.getDatasetMeta(datasetIndex);
13183					var key = [
13184						meta.type,
13185						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
13186						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
13187						meta.stack
13188					].join('.');
13189	
13190					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
13191						if (valuesPerStack[key] === undefined) {
13192							valuesPerStack[key] = [];
13193						}
13194	
13195						data = datasets[datasetIndex].data;
13196						for (i = 0, ilen = data.length; i < ilen; i++) {
13197							var values = valuesPerStack[key];
13198							value = me._parseValue(data[i]);
13199							// invalid, hidden and negative values are ignored
13200							if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) {
13201								continue;
13202							}
13203							values[i] = values[i] || 0;
13204							values[i] += value.max;
13205						}
13206					}
13207				}
13208	
13209				helpers$1.each(valuesPerStack, function(valuesForType) {
13210					if (valuesForType.length > 0) {
13211						var minVal = helpers$1.min(valuesForType);
13212						var maxVal = helpers$1.max(valuesForType);
13213						me.min = Math.min(me.min, minVal);
13214						me.max = Math.max(me.max, maxVal);
13215					}
13216				});
13217	
13218			} else {
13219				for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
13220					meta = chart.getDatasetMeta(datasetIndex);
13221					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
13222						data = datasets[datasetIndex].data;
13223						for (i = 0, ilen = data.length; i < ilen; i++) {
13224							value = me._parseValue(data[i]);
13225							// invalid, hidden and negative values are ignored
13226							if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) {
13227								continue;
13228							}
13229	
13230							me.min = Math.min(value.min, me.min);
13231							me.max = Math.max(value.max, me.max);
13232	
13233							if (value.min !== 0) {
13234								me.minNotZero = Math.min(value.min, me.minNotZero);
13235							}
13236						}
13237					}
13238				}
13239			}
13240	
13241			me.min = helpers$1.isFinite(me.min) ? me.min : null;
13242			me.max = helpers$1.isFinite(me.max) ? me.max : null;
13243			me.minNotZero = helpers$1.isFinite(me.minNotZero) ? me.minNotZero : null;
13244	
13245			// Common base implementation to handle ticks.min, ticks.max
13246			this.handleTickRangeOptions();
13247		},
13248	
13249		handleTickRangeOptions: function() {
13250			var me = this;
13251			var tickOpts = me.options.ticks;
13252			var DEFAULT_MIN = 1;
13253			var DEFAULT_MAX = 10;
13254	
13255			me.min = nonNegativeOrDefault(tickOpts.min, me.min);
13256			me.max = nonNegativeOrDefault(tickOpts.max, me.max);
13257	
13258			if (me.min === me.max) {
13259				if (me.min !== 0 && me.min !== null) {
13260					me.min = Math.pow(10, Math.floor(log10(me.min)) - 1);
13261					me.max = Math.pow(10, Math.floor(log10(me.max)) + 1);
13262				} else {
13263					me.min = DEFAULT_MIN;
13264					me.max = DEFAULT_MAX;
13265				}
13266			}
13267			if (me.min === null) {
13268				me.min = Math.pow(10, Math.floor(log10(me.max)) - 1);
13269			}
13270			if (me.max === null) {
13271				me.max = me.min !== 0
13272					? Math.pow(10, Math.floor(log10(me.min)) + 1)
13273					: DEFAULT_MAX;
13274			}
13275			if (me.minNotZero === null) {
13276				if (me.min > 0) {
13277					me.minNotZero = me.min;
13278				} else if (me.max < 1) {
13279					me.minNotZero = Math.pow(10, Math.floor(log10(me.max)));
13280				} else {
13281					me.minNotZero = DEFAULT_MIN;
13282				}
13283			}
13284		},
13285	
13286		buildTicks: function() {
13287			var me = this;
13288			var tickOpts = me.options.ticks;
13289			var reverse = !me.isHorizontal();
13290	
13291			var generationOptions = {
13292				min: nonNegativeOrDefault(tickOpts.min),
13293				max: nonNegativeOrDefault(tickOpts.max)
13294			};
13295			var ticks = me.ticks = generateTicks$1(generationOptions, me);
13296	
13297			// At this point, we need to update our max and min given the tick values since we have expanded the
13298			// range of the scale
13299			me.max = helpers$1.max(ticks);
13300			me.min = helpers$1.min(ticks);
13301	
13302			if (tickOpts.reverse) {
13303				reverse = !reverse;
13304				me.start = me.max;
13305				me.end = me.min;
13306			} else {
13307				me.start = me.min;
13308				me.end = me.max;
13309			}
13310			if (reverse) {
13311				ticks.reverse();
13312			}
13313		},
13314	
13315		convertTicksToLabels: function() {
13316			this.tickValues = this.ticks.slice();
13317	
13318			core_scale.prototype.convertTicksToLabels.call(this);
13319		},
13320	
13321		// Get the correct tooltip label
13322		getLabelForIndex: function(index, datasetIndex) {
13323			return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
13324		},
13325	
13326		getPixelForTick: function(index) {
13327			var ticks = this.tickValues;
13328			if (index < 0 || index > ticks.length - 1) {
13329				return null;
13330			}
13331			return this.getPixelForValue(ticks[index]);
13332		},
13333	
13334		/**
13335		 * Returns the value of the first tick.
13336		 * @param {number} value - The minimum not zero value.
13337		 * @return {number} The first tick value.
13338		 * @private
13339		 */
13340		_getFirstTickValue: function(value) {
13341			var exp = Math.floor(log10(value));
13342			var significand = Math.floor(value / Math.pow(10, exp));
13343	
13344			return significand * Math.pow(10, exp);
13345		},
13346	
13347		_configure: function() {
13348			var me = this;
13349			var start = me.min;
13350			var offset = 0;
13351	
13352			core_scale.prototype._configure.call(me);
13353	
13354			if (start === 0) {
13355				start = me._getFirstTickValue(me.minNotZero);
13356				offset = valueOrDefault$b(me.options.ticks.fontSize, core_defaults.global.defaultFontSize) / me._length;
13357			}
13358	
13359			me._startValue = log10(start);
13360			me._valueOffset = offset;
13361			me._valueRange = (log10(me.max) - log10(start)) / (1 - offset);
13362		},
13363	
13364		getPixelForValue: function(value) {
13365			var me = this;
13366			var decimal = 0;
13367	
13368			value = +me.getRightValue(value);
13369	
13370			if (value > me.min && value > 0) {
13371				decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset;
13372			}
13373			return me.getPixelForDecimal(decimal);
13374		},
13375	
13376		getValueForPixel: function(pixel) {
13377			var me = this;
13378			var decimal = me.getDecimalForPixel(pixel);
13379			return decimal === 0 && me.min === 0
13380				? 0
13381				: Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange);
13382		}
13383	});
13384	
13385	// INTERNAL: static default options, registered in src/index.js
13386	var _defaults$2 = defaultConfig$2;
13387	scale_logarithmic._defaults = _defaults$2;
13388	
13389	var valueOrDefault$c = helpers$1.valueOrDefault;
13390	var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault;
13391	var resolve$4 = helpers$1.options.resolve;
13392	
13393	var defaultConfig$3 = {
13394		display: true,
13395	
13396		// Boolean - Whether to animate scaling the chart from the centre
13397		animate: true,
13398		position: 'chartArea',
13399	
13400		angleLines: {
13401			display: true,
13402			color: 'rgba(0,0,0,0.1)',
13403			lineWidth: 1,
13404			borderDash: [],
13405			borderDashOffset: 0.0
13406		},
13407	
13408		gridLines: {
13409			circular: false
13410		},
13411	
13412		// label settings
13413		ticks: {
13414			// Boolean - Show a backdrop to the scale label
13415			showLabelBackdrop: true,
13416	
13417			// String - The colour of the label backdrop
13418			backdropColor: 'rgba(255,255,255,0.75)',
13419	
13420			// Number - The backdrop padding above & below the label in pixels
13421			backdropPaddingY: 2,
13422	
13423			// Number - The backdrop padding to the side of the label in pixels
13424			backdropPaddingX: 2,
13425	
13426			callback: core_ticks.formatters.linear
13427		},
13428	
13429		pointLabels: {
13430			// Boolean - if true, show point labels
13431			display: true,
13432	
13433			// Number - Point label font size in pixels
13434			fontSize: 10,
13435	
13436			// Function - Used to convert point labels
13437			callback: function(label) {
13438				return label;
13439			}
13440		}
13441	};
13442	
13443	function getTickBackdropHeight(opts) {
13444		var tickOpts = opts.ticks;
13445	
13446		if (tickOpts.display && opts.display) {
13447			return valueOrDefault$c(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2;
13448		}
13449		return 0;
13450	}
13451	
13452	function measureLabelSize(ctx, lineHeight, label) {
13453		if (helpers$1.isArray(label)) {
13454			return {
13455				w: helpers$1.longestText(ctx, ctx.font, label),
13456				h: label.length * lineHeight
13457			};
13458		}
13459	
13460		return {
13461			w: ctx.measureText(label).width,
13462			h: lineHeight
13463		};
13464	}
13465	
13466	function determineLimits(angle, pos, size, min, max) {
13467		if (angle === min || angle === max) {
13468			return {
13469				start: pos - (size / 2),
13470				end: pos + (size / 2)
13471			};
13472		} else if (angle < min || angle > max) {
13473			return {
13474				start: pos - size,
13475				end: pos
13476			};
13477		}
13478	
13479		return {
13480			start: pos,
13481			end: pos + size
13482		};
13483	}
13484	
13485	/**
13486	 * Helper function to fit a radial linear scale with point labels
13487	 */
13488	function fitWithPointLabels(scale) {
13489	
13490		// Right, this is really confusing and there is a lot of maths going on here
13491		// The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
13492		//
13493		// Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
13494		//
13495		// Solution:
13496		//
13497		// We assume the radius of the polygon is half the size of the canvas at first
13498		// at each index we check if the text overlaps.
13499		//
13500		// Where it does, we store that angle and that index.
13501		//
13502		// After finding the largest index and angle we calculate how much we need to remove
13503		// from the shape radius to move the point inwards by that x.
13504		//
13505		// We average the left and right distances to get the maximum shape radius that can fit in the box
13506		// along with labels.
13507		//
13508		// Once we have that, we can find the centre point for the chart, by taking the x text protrusion
13509		// on each side, removing that from the size, halving it and adding the left x protrusion width.
13510		//
13511		// This will mean we have a shape fitted to the canvas, as large as it can be with the labels
13512		// and position it in the most space efficient manner
13513		//
13514		// https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
13515	
13516		var plFont = helpers$1.options._parseFont(scale.options.pointLabels);
13517	
13518		// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
13519		// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
13520		var furthestLimits = {
13521			l: 0,
13522			r: scale.width,
13523			t: 0,
13524			b: scale.height - scale.paddingTop
13525		};
13526		var furthestAngles = {};
13527		var i, textSize, pointPosition;
13528	
13529		scale.ctx.font = plFont.string;
13530		scale._pointLabelSizes = [];
13531	
13532		var valueCount = scale.chart.data.labels.length;
13533		for (i = 0; i < valueCount; i++) {
13534			pointPosition = scale.getPointPosition(i, scale.drawingArea + 5);
13535			textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]);
13536			scale._pointLabelSizes[i] = textSize;
13537	
13538			// Add quarter circle to make degree 0 mean top of circle
13539			var angleRadians = scale.getIndexAngle(i);
13540			var angle = helpers$1.toDegrees(angleRadians) % 360;
13541			var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
13542			var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
13543	
13544			if (hLimits.start < furthestLimits.l) {
13545				furthestLimits.l = hLimits.start;
13546				furthestAngles.l = angleRadians;
13547			}
13548	
13549			if (hLimits.end > furthestLimits.r) {
13550				furthestLimits.r = hLimits.end;
13551				furthestAngles.r = angleRadians;
13552			}
13553	
13554			if (vLimits.start < furthestLimits.t) {
13555				furthestLimits.t = vLimits.start;
13556				furthestAngles.t = angleRadians;
13557			}
13558	
13559			if (vLimits.end > furthestLimits.b) {
13560				furthestLimits.b = vLimits.end;
13561				furthestAngles.b = angleRadians;
13562			}
13563		}
13564	
13565		scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles);
13566	}
13567	
13568	function getTextAlignForAngle(angle) {
13569		if (angle === 0 || angle === 180) {
13570			return 'center';
13571		} else if (angle < 180) {
13572			return 'left';
13573		}
13574	
13575		return 'right';
13576	}
13577	
13578	function fillText(ctx, text, position, lineHeight) {
13579		var y = position.y + lineHeight / 2;
13580		var i, ilen;
13581	
13582		if (helpers$1.isArray(text)) {
13583			for (i = 0, ilen = text.length; i < ilen; ++i) {
13584				ctx.fillText(text[i], position.x, y);
13585				y += lineHeight;
13586			}
13587		} else {
13588			ctx.fillText(text, position.x, y);
13589		}
13590	}
13591	
13592	function adjustPointPositionForLabelHeight(angle, textSize, position) {
13593		if (angle === 90 || angle === 270) {
13594			position.y -= (textSize.h / 2);
13595		} else if (angle > 270 || angle < 90) {
13596			position.y -= textSize.h;
13597		}
13598	}
13599	
13600	function drawPointLabels(scale) {
13601		var ctx = scale.ctx;
13602		var opts = scale.options;
13603		var pointLabelOpts = opts.pointLabels;
13604		var tickBackdropHeight = getTickBackdropHeight(opts);
13605		var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
13606		var plFont = helpers$1.options._parseFont(pointLabelOpts);
13607	
13608		ctx.save();
13609	
13610		ctx.font = plFont.string;
13611		ctx.textBaseline = 'middle';
13612	
13613		for (var i = scale.chart.data.labels.length - 1; i >= 0; i--) {
13614			// Extra pixels out for some label spacing
13615			var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
13616			var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
13617	
13618			// Keep this in loop since we may support array properties here
13619			var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor);
13620			ctx.fillStyle = pointLabelFontColor;
13621	
13622			var angleRadians = scale.getIndexAngle(i);
13623			var angle = helpers$1.toDegrees(angleRadians);
13624			ctx.textAlign = getTextAlignForAngle(angle);
13625			adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
13626			fillText(ctx, scale.pointLabels[i], pointLabelPosition, plFont.lineHeight);
13627		}
13628		ctx.restore();
13629	}
13630	
13631	function drawRadiusLine(scale, gridLineOpts, radius, index) {
13632		var ctx = scale.ctx;
13633		var circular = gridLineOpts.circular;
13634		var valueCount = scale.chart.data.labels.length;
13635		var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1);
13636		var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1);
13637		var pointPosition;
13638	
13639		if ((!circular && !valueCount) || !lineColor || !lineWidth) {
13640			return;
13641		}
13642	
13643		ctx.save();
13644		ctx.strokeStyle = lineColor;
13645		ctx.lineWidth = lineWidth;
13646		if (ctx.setLineDash) {
13647			ctx.setLineDash(gridLineOpts.borderDash || []);
13648			ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0;
13649		}
13650	
13651		ctx.beginPath();
13652		if (circular) {
13653			// Draw circular arcs between the points
13654			ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
13655		} else {
13656			// Draw straight lines connecting each index
13657			pointPosition = scale.getPointPosition(0, radius);
13658			ctx.moveTo(pointPosition.x, pointPosition.y);
13659	
13660			for (var i = 1; i < valueCount; i++) {
13661				pointPosition = scale.getPointPosition(i, radius);
13662				ctx.lineTo(pointPosition.x, pointPosition.y);
13663			}
13664		}
13665		ctx.closePath();
13666		ctx.stroke();
13667		ctx.restore();
13668	}
13669	
13670	function numberOrZero(param) {
13671		return helpers$1.isNumber(param) ? param : 0;
13672	}
13673	
13674	var scale_radialLinear = scale_linearbase.extend({
13675		setDimensions: function() {
13676			var me = this;
13677	
13678			// Set the unconstrained dimension before label rotation
13679			me.width = me.maxWidth;
13680			me.height = me.maxHeight;
13681			me.paddingTop = getTickBackdropHeight(me.options) / 2;
13682			me.xCenter = Math.floor(me.width / 2);
13683			me.yCenter = Math.floor((me.height - me.paddingTop) / 2);
13684			me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2;
13685		},
13686	
13687		determineDataLimits: function() {
13688			var me = this;
13689			var chart = me.chart;
13690			var min = Number.POSITIVE_INFINITY;
13691			var max = Number.NEGATIVE_INFINITY;
13692	
13693			helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) {
13694				if (chart.isDatasetVisible(datasetIndex)) {
13695					var meta = chart.getDatasetMeta(datasetIndex);
13696	
13697					helpers$1.each(dataset.data, function(rawValue, index) {
13698						var value = +me.getRightValue(rawValue);
13699						if (isNaN(value) || meta.data[index].hidden) {
13700							return;
13701						}
13702	
13703						min = Math.min(value, min);
13704						max = Math.max(value, max);
13705					});
13706				}
13707			});
13708	
13709			me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
13710			me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
13711	
13712			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
13713			me.handleTickRangeOptions();
13714		},
13715	
13716		// Returns the maximum number of ticks based on the scale dimension
13717		_computeTickLimit: function() {
13718			return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
13719		},
13720	
13721		convertTicksToLabels: function() {
13722			var me = this;
13723	
13724			scale_linearbase.prototype.convertTicksToLabels.call(me);
13725	
13726			// Point labels
13727			me.pointLabels = me.chart.data.labels.map(function() {
13728				var label = helpers$1.callback(me.options.pointLabels.callback, arguments, me);
13729				return label || label === 0 ? label : '';
13730			});
13731		},
13732	
13733		getLabelForIndex: function(index, datasetIndex) {
13734			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13735		},
13736	
13737		fit: function() {
13738			var me = this;
13739			var opts = me.options;
13740	
13741			if (opts.display && opts.pointLabels.display) {
13742				fitWithPointLabels(me);
13743			} else {
13744				me.setCenterPoint(0, 0, 0, 0);
13745			}
13746		},
13747	
13748		/**
13749		 * Set radius reductions and determine new radius and center point
13750		 * @private
13751		 */
13752		setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
13753			var me = this;
13754			var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
13755			var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
13756			var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
13757			var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);
13758	
13759			radiusReductionLeft = numberOrZero(radiusReductionLeft);
13760			radiusReductionRight = numberOrZero(radiusReductionRight);
13761			radiusReductionTop = numberOrZero(radiusReductionTop);
13762			radiusReductionBottom = numberOrZero(radiusReductionBottom);
13763	
13764			me.drawingArea = Math.min(
13765				Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
13766				Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
13767			me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
13768		},
13769	
13770		setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
13771			var me = this;
13772			var maxRight = me.width - rightMovement - me.drawingArea;
13773			var maxLeft = leftMovement + me.drawingArea;
13774			var maxTop = topMovement + me.drawingArea;
13775			var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;
13776	
13777			me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
13778			me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
13779		},
13780	
13781		getIndexAngle: function(index) {
13782			var chart = this.chart;
13783			var angleMultiplier = 360 / chart.data.labels.length;
13784			var options = chart.options || {};
13785			var startAngle = options.startAngle || 0;
13786	
13787			// Start from the top instead of right, so remove a quarter of the circle
13788			var angle = (index * angleMultiplier + startAngle) % 360;
13789	
13790			return (angle < 0 ? angle + 360 : angle) * Math.PI * 2 / 360;
13791		},
13792	
13793		getDistanceFromCenterForValue: function(value) {
13794			var me = this;
13795	
13796			if (helpers$1.isNullOrUndef(value)) {
13797				return NaN;
13798			}
13799	
13800			// Take into account half font size + the yPadding of the top value
13801			var scalingFactor = me.drawingArea / (me.max - me.min);
13802			if (me.options.ticks.reverse) {
13803				return (me.max - value) * scalingFactor;
13804			}
13805			return (value - me.min) * scalingFactor;
13806		},
13807	
13808		getPointPosition: function(index, distanceFromCenter) {
13809			var me = this;
13810			var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
13811			return {
13812				x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter,
13813				y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter
13814			};
13815		},
13816	
13817		getPointPositionForValue: function(index, value) {
13818			return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
13819		},
13820	
13821		getBasePosition: function(index) {
13822			var me = this;
13823			var min = me.min;
13824			var max = me.max;
13825	
13826			return me.getPointPositionForValue(index || 0,
13827				me.beginAtZero ? 0 :
13828				min < 0 && max < 0 ? max :
13829				min > 0 && max > 0 ? min :
13830				0);
13831		},
13832	
13833		/**
13834		 * @private
13835		 */
13836		_drawGrid: function() {
13837			var me = this;
13838			var ctx = me.ctx;
13839			var opts = me.options;
13840			var gridLineOpts = opts.gridLines;
13841			var angleLineOpts = opts.angleLines;
13842			var lineWidth = valueOrDefault$c(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
13843			var lineColor = valueOrDefault$c(angleLineOpts.color, gridLineOpts.color);
13844			var i, offset, position;
13845	
13846			if (opts.pointLabels.display) {
13847				drawPointLabels(me);
13848			}
13849	
13850			if (gridLineOpts.display) {
13851				helpers$1.each(me.ticks, function(label, index) {
13852					if (index !== 0) {
13853						offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
13854						drawRadiusLine(me, gridLineOpts, offset, index);
13855					}
13856				});
13857			}
13858	
13859			if (angleLineOpts.display && lineWidth && lineColor) {
13860				ctx.save();
13861				ctx.lineWidth = lineWidth;
13862				ctx.strokeStyle = lineColor;
13863				if (ctx.setLineDash) {
13864					ctx.setLineDash(resolve$4([angleLineOpts.borderDash, gridLineOpts.borderDash, []]));
13865					ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]);
13866				}
13867	
13868				for (i = me.chart.data.labels.length - 1; i >= 0; i--) {
13869					offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max);
13870					position = me.getPointPosition(i, offset);
13871					ctx.beginPath();
13872					ctx.moveTo(me.xCenter, me.yCenter);
13873					ctx.lineTo(position.x, position.y);
13874					ctx.stroke();
13875				}
13876	
13877				ctx.restore();
13878			}
13879		},
13880	
13881		/**
13882		 * @private
13883		 */
13884		_drawLabels: function() {
13885			var me = this;
13886			var ctx = me.ctx;
13887			var opts = me.options;
13888			var tickOpts = opts.ticks;
13889	
13890			if (!tickOpts.display) {
13891				return;
13892			}
13893	
13894			var startAngle = me.getIndexAngle(0);
13895			var tickFont = helpers$1.options._parseFont(tickOpts);
13896			var tickFontColor = valueOrDefault$c(tickOpts.fontColor, core_defaults.global.defaultFontColor);
13897			var offset, width;
13898	
13899			ctx.save();
13900			ctx.font = tickFont.string;
13901			ctx.translate(me.xCenter, me.yCenter);
13902			ctx.rotate(startAngle);
13903			ctx.textAlign = 'center';
13904			ctx.textBaseline = 'middle';
13905	
13906			helpers$1.each(me.ticks, function(label, index) {
13907				if (index === 0 && !tickOpts.reverse) {
13908					return;
13909				}
13910	
13911				offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
13912	
13913				if (tickOpts.showLabelBackdrop) {
13914					width = ctx.measureText(label).width;
13915					ctx.fillStyle = tickOpts.backdropColor;
13916	
13917					ctx.fillRect(
13918						-width / 2 - tickOpts.backdropPaddingX,
13919						-offset - tickFont.size / 2 - tickOpts.backdropPaddingY,
13920						width + tickOpts.backdropPaddingX * 2,
13921						tickFont.size + tickOpts.backdropPaddingY * 2
13922					);
13923				}
13924	
13925				ctx.fillStyle = tickFontColor;
13926				ctx.fillText(label, 0, -offset);
13927			});
13928	
13929			ctx.restore();
13930		},
13931	
13932		/**
13933		 * @private
13934		 */
13935		_drawTitle: helpers$1.noop
13936	});
13937	
13938	// INTERNAL: static default options, registered in src/index.js
13939	var _defaults$3 = defaultConfig$3;
13940	scale_radialLinear._defaults = _defaults$3;
13941	
13942	var deprecated$1 = helpers$1._deprecated;
13943	var resolve$5 = helpers$1.options.resolve;
13944	var valueOrDefault$d = helpers$1.valueOrDefault;
13945	
13946	// Integer constants are from the ES6 spec.
13947	var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
13948	var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
13949	
13950	var INTERVALS = {
13951		millisecond: {
13952			common: true,
13953			size: 1,
13954			steps: 1000
13955		},
13956		second: {
13957			common: true,
13958			size: 1000,
13959			steps: 60
13960		},
13961		minute: {
13962			common: true,
13963			size: 60000,
13964			steps: 60
13965		},
13966		hour: {
13967			common: true,
13968			size: 3600000,
13969			steps: 24
13970		},
13971		day: {
13972			common: true,
13973			size: 86400000,
13974			steps: 30
13975		},
13976		week: {
13977			common: false,
13978			size: 604800000,
13979			steps: 4
13980		},
13981		month: {
13982			common: true,
13983			size: 2.628e9,
13984			steps: 12
13985		},
13986		quarter: {
13987			common: false,
13988			size: 7.884e9,
13989			steps: 4
13990		},
13991		year: {
13992			common: true,
13993			size: 3.154e10
13994		}
13995	};
13996	
13997	var UNITS = Object.keys(INTERVALS);
13998	
13999	function sorter(a, b) {
14000		return a - b;
14001	}
14002	
14003	function arrayUnique(items) {
14004		var hash = {};
14005		var out = [];
14006		var i, ilen, item;
14007	
14008		for (i = 0, ilen = items.length; i < ilen; ++i) {
14009			item = items[i];
14010			if (!hash[item]) {
14011				hash[item] = true;
14012				out.push(item);
14013			}
14014		}
14015	
14016		return out;
14017	}
14018	
14019	function getMin(options) {
14020		return helpers$1.valueOrDefault(options.time.min, options.ticks.min);
14021	}
14022	
14023	function getMax(options) {
14024		return helpers$1.valueOrDefault(options.time.max, options.ticks.max);
14025	}
14026	
14027	/**
14028	 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
14029	 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
14030	 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
14031	 * extremity (left + width or top + height). Note that it would be more optimized to directly
14032	 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
14033	 * to create the lookup table. The table ALWAYS contains at least two items: min and max.
14034	 *
14035	 * @param {number[]} timestamps - timestamps sorted from lowest to highest.
14036	 * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min
14037	 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
14038	 * If 'series', timestamps will be positioned at the same distance from each other. In this
14039	 * case, only timestamps that break the time linearity are registered, meaning that in the
14040	 * best case, all timestamps are linear, the table contains only min and max.
14041	 */
14042	function buildLookupTable(timestamps, min, max, distribution) {
14043		if (distribution === 'linear' || !timestamps.length) {
14044			return [
14045				{time: min, pos: 0},
14046				{time: max, pos: 1}
14047			];
14048		}
14049	
14050		var table = [];
14051		var items = [min];
14052		var i, ilen, prev, curr, next;
14053	
14054		for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
14055			curr = timestamps[i];
14056			if (curr > min && curr < max) {
14057				items.push(curr);
14058			}
14059		}
14060	
14061		items.push(max);
14062	
14063		for (i = 0, ilen = items.length; i < ilen; ++i) {
14064			next = items[i + 1];
14065			prev = items[i - 1];
14066			curr = items[i];
14067	
14068			// only add points that breaks the scale linearity
14069			if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
14070				table.push({time: curr, pos: i / (ilen - 1)});
14071			}
14072		}
14073	
14074		return table;
14075	}
14076	
14077	// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
14078	function lookup(table, key, value) {
14079		var lo = 0;
14080		var hi = table.length - 1;
14081		var mid, i0, i1;
14082	
14083		while (lo >= 0 && lo <= hi) {
14084			mid = (lo + hi) >> 1;
14085			i0 = table[mid - 1] || null;
14086			i1 = table[mid];
14087	
14088			if (!i0) {
14089				// given value is outside table (before first item)
14090				return {lo: null, hi: i1};
14091			} else if (i1[key] < value) {
14092				lo = mid + 1;
14093			} else if (i0[key] > value) {
14094				hi = mid - 1;
14095			} else {
14096				return {lo: i0, hi: i1};
14097			}
14098		}
14099	
14100		// given value is outside table (after last item)
14101		return {lo: i1, hi: null};
14102	}
14103	
14104	/**
14105	 * Linearly interpolates the given source `value` using the table items `skey` values and
14106	 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
14107	 * returns the position for a timestamp equal to 42. If value is out of bounds, values at
14108	 * index [0, 1] or [n - 1, n] are used for the interpolation.
14109	 */
14110	function interpolate$1(table, skey, sval, tkey) {
14111		var range = lookup(table, skey, sval);
14112	
14113		// Note: the lookup table ALWAYS contains at least 2 items (min and max)
14114		var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
14115		var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
14116	
14117		var span = next[skey] - prev[skey];
14118		var ratio = span ? (sval - prev[skey]) / span : 0;
14119		var offset = (next[tkey] - prev[tkey]) * ratio;
14120	
14121		return prev[tkey] + offset;
14122	}
14123	
14124	function toTimestamp(scale, input) {
14125		var adapter = scale._adapter;
14126		var options = scale.options.time;
14127		var parser = options.parser;
14128		var format = parser || options.format;
14129		var value = input;
14130	
14131		if (typeof parser === 'function') {
14132			value = parser(value);
14133		}
14134	
14135		// Only parse if its not a timestamp already
14136		if (!helpers$1.isFinite(value)) {
14137			value = typeof format === 'string'
14138				? adapter.parse(value, format)
14139				: adapter.parse(value);
14140		}
14141	
14142		if (value !== null) {
14143			return +value;
14144		}
14145	
14146		// Labels are in an incompatible format and no `parser` has been provided.
14147		// The user might still use the deprecated `format` option for parsing.
14148		if (!parser && typeof format === 'function') {
14149			value = format(input);
14150	
14151			// `format` could return something else than a timestamp, if so, parse it
14152			if (!helpers$1.isFinite(value)) {
14153				value = adapter.parse(value);
14154			}
14155		}
14156	
14157		return value;
14158	}
14159	
14160	function parse(scale, input) {
14161		if (helpers$1.isNullOrUndef(input)) {
14162			return null;
14163		}
14164	
14165		var options = scale.options.time;
14166		var value = toTimestamp(scale, scale.getRightValue(input));
14167		if (value === null) {
14168			return value;
14169		}
14170	
14171		if (options.round) {
14172			value = +scale._adapter.startOf(value, options.round);
14173		}
14174	
14175		return value;
14176	}
14177	
14178	/**
14179	 * Figures out what unit results in an appropriate number of auto-generated ticks
14180	 */
14181	function determineUnitForAutoTicks(minUnit, min, max, capacity) {
14182		var ilen = UNITS.length;
14183		var i, interval, factor;
14184	
14185		for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
14186			interval = INTERVALS[UNITS[i]];
14187			factor = interval.steps ? interval.steps : MAX_INTEGER;
14188	
14189			if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
14190				return UNITS[i];
14191			}
14192		}
14193	
14194		return UNITS[ilen - 1];
14195	}
14196	
14197	/**
14198	 * Figures out what unit to format a set of ticks with
14199	 */
14200	function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {
14201		var i, unit;
14202	
14203		for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) {
14204			unit = UNITS[i];
14205			if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {
14206				return unit;
14207			}
14208		}
14209	
14210		return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
14211	}
14212	
14213	function determineMajorUnit(unit) {
14214		for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
14215			if (INTERVALS[UNITS[i]].common) {
14216				return UNITS[i];
14217			}
14218		}
14219	}
14220	
14221	/**
14222	 * Generates a maximum of `capacity` timestamps between min and max, rounded to the
14223	 * `minor` unit using the given scale time `options`.
14224	 * Important: this method can return ticks outside the min and max range, it's the
14225	 * responsibility of the calling code to clamp values if needed.
14226	 */
14227	function generate(scale, min, max, capacity) {
14228		var adapter = scale._adapter;
14229		var options = scale.options;
14230		var timeOpts = options.time;
14231		var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
14232		var stepSize = resolve$5([timeOpts.stepSize, timeOpts.unitStepSize, 1]);
14233		var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
14234		var first = min;
14235		var ticks = [];
14236		var time;
14237	
14238		// For 'week' unit, handle the first day of week option
14239		if (weekday) {
14240			first = +adapter.startOf(first, 'isoWeek', weekday);
14241		}
14242	
14243		// Align first ticks on unit
14244		first = +adapter.startOf(first, weekday ? 'day' : minor);
14245	
14246		// Prevent browser from freezing in case user options request millions of milliseconds
14247		if (adapter.diff(max, min, minor) > 100000 * stepSize) {
14248			throw min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor;
14249		}
14250	
14251		for (time = first; time < max; time = +adapter.add(time, stepSize, minor)) {
14252			ticks.push(time);
14253		}
14254	
14255		if (time === max || options.bounds === 'ticks') {
14256			ticks.push(time);
14257		}
14258	
14259		return ticks;
14260	}
14261	
14262	/**
14263	 * Returns the start and end offsets from edges in the form of {start, end}
14264	 * where each value is a relative width to the scale and ranges between 0 and 1.
14265	 * They add extra margins on the both sides by scaling down the original scale.
14266	 * Offsets are added when the `offset` option is true.
14267	 */
14268	function computeOffsets(table, ticks, min, max, options) {
14269		var start = 0;
14270		var end = 0;
14271		var first, last;
14272	
14273		if (options.offset && ticks.length) {
14274			first = interpolate$1(table, 'time', ticks[0], 'pos');
14275			if (ticks.length === 1) {
14276				start = 1 - first;
14277			} else {
14278				start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2;
14279			}
14280			last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos');
14281			if (ticks.length === 1) {
14282				end = last;
14283			} else {
14284				end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2;
14285			}
14286		}
14287	
14288		return {start: start, end: end, factor: 1 / (start + 1 + end)};
14289	}
14290	
14291	function setMajorTicks(scale, ticks, map, majorUnit) {
14292		var adapter = scale._adapter;
14293		var first = +adapter.startOf(ticks[0].value, majorUnit);
14294		var last = ticks[ticks.length - 1].value;
14295		var major, index;
14296	
14297		for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) {
14298			index = map[major];
14299			if (index >= 0) {
14300				ticks[index].major = true;
14301			}
14302		}
14303		return ticks;
14304	}
14305	
14306	function ticksFromTimestamps(scale, values, majorUnit) {
14307		var ticks = [];
14308		var map = {};
14309		var ilen = values.length;
14310		var i, value;
14311	
14312		for (i = 0; i < ilen; ++i) {
14313			value = values[i];
14314			map[value] = i;
14315	
14316			ticks.push({
14317				value: value,
14318				major: false
14319			});
14320		}
14321	
14322		// We set the major ticks separately from the above loop because calling startOf for every tick
14323		// is expensive when there is a large number of ticks
14324		return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit);
14325	}
14326	
14327	var defaultConfig$4 = {
14328		position: 'bottom',
14329	
14330		/**
14331		 * Data distribution along the scale:
14332		 * - 'linear': data are spread according to their time (distances can vary),
14333		 * - 'series': data are spread at the same distance from each other.
14334		 * @see https://github.com/chartjs/Chart.js/pull/4507
14335		 * @since 2.7.0
14336		 */
14337		distribution: 'linear',
14338	
14339		/**
14340		 * Scale boundary strategy (bypassed by min/max time options)
14341		 * - `data`: make sure data are fully visible, ticks outside are removed
14342		 * - `ticks`: make sure ticks are fully visible, data outside are truncated
14343		 * @see https://github.com/chartjs/Chart.js/pull/4556
14344		 * @since 2.7.0
14345		 */
14346		bounds: 'data',
14347	
14348		adapters: {},
14349		time: {
14350			parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
14351			unit: false, // false == automatic or override with week, month, year, etc.
14352			round: false, // none, or override with week, month, year, etc.
14353			displayFormat: false, // DEPRECATED
14354			isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/
14355			minUnit: 'millisecond',
14356			displayFormats: {}
14357		},
14358		ticks: {
14359			autoSkip: false,
14360	
14361			/**
14362			 * Ticks generation input values:
14363			 * - 'auto': generates "optimal" ticks based on scale size and time options.
14364			 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
14365			 * - 'labels': generates ticks from user given `data.labels` values ONLY.
14366			 * @see https://github.com/chartjs/Chart.js/pull/4507
14367			 * @since 2.7.0
14368			 */
14369			source: 'auto',
14370	
14371			major: {
14372				enabled: false
14373			}
14374		}
14375	};
14376	
14377	var scale_time = core_scale.extend({
14378		initialize: function() {
14379			this.mergeTicksOptions();
14380			core_scale.prototype.initialize.call(this);
14381		},
14382	
14383		update: function() {
14384			var me = this;
14385			var options = me.options;
14386			var time = options.time || (options.time = {});
14387			var adapter = me._adapter = new core_adapters._date(options.adapters.date);
14388	
14389			// DEPRECATIONS: output a message only one time per update
14390			deprecated$1('time scale', time.format, 'time.format', 'time.parser');
14391			deprecated$1('time scale', time.min, 'time.min', 'ticks.min');
14392			deprecated$1('time scale', time.max, 'time.max', 'ticks.max');
14393	
14394			// Backward compatibility: before introducing adapter, `displayFormats` was
14395			// supposed to contain *all* unit/string pairs but this can't be resolved
14396			// when loading the scale (adapters are loaded afterward), so let's populate
14397			// missing formats on update
14398			helpers$1.mergeIf(time.displayFormats, adapter.formats());
14399	
14400			return core_scale.prototype.update.apply(me, arguments);
14401		},
14402	
14403		/**
14404		 * Allows data to be referenced via 't' attribute
14405		 */
14406		getRightValue: function(rawValue) {
14407			if (rawValue && rawValue.t !== undefined) {
14408				rawValue = rawValue.t;
14409			}
14410			return core_scale.prototype.getRightValue.call(this, rawValue);
14411		},
14412	
14413		determineDataLimits: function() {
14414			var me = this;
14415			var chart = me.chart;
14416			var adapter = me._adapter;
14417			var options = me.options;
14418			var unit = options.time.unit || 'day';
14419			var min = MAX_INTEGER;
14420			var max = MIN_INTEGER;
14421			var timestamps = [];
14422			var datasets = [];
14423			var labels = [];
14424			var i, j, ilen, jlen, data, timestamp, labelsAdded;
14425			var dataLabels = me._getLabels();
14426	
14427			for (i = 0, ilen = dataLabels.length; i < ilen; ++i) {
14428				labels.push(parse(me, dataLabels[i]));
14429			}
14430	
14431			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
14432				if (chart.isDatasetVisible(i)) {
14433					data = chart.data.datasets[i].data;
14434	
14435					// Let's consider that all data have the same format.
14436					if (helpers$1.isObject(data[0])) {
14437						datasets[i] = [];
14438	
14439						for (j = 0, jlen = data.length; j < jlen; ++j) {
14440							timestamp = parse(me, data[j]);
14441							timestamps.push(timestamp);
14442							datasets[i][j] = timestamp;
14443						}
14444					} else {
14445						datasets[i] = labels.slice(0);
14446						if (!labelsAdded) {
14447							timestamps = timestamps.concat(labels);
14448							labelsAdded = true;
14449						}
14450					}
14451				} else {
14452					datasets[i] = [];
14453				}
14454			}
14455	
14456			if (labels.length) {
14457				min = Math.min(min, labels[0]);
14458				max = Math.max(max, labels[labels.length - 1]);
14459			}
14460	
14461			if (timestamps.length) {
14462				timestamps = ilen > 1 ? arrayUnique(timestamps).sort(sorter) : timestamps.sort(sorter);
14463				min = Math.min(min, timestamps[0]);
14464				max = Math.max(max, timestamps[timestamps.length - 1]);
14465			}
14466	
14467			min = parse(me, getMin(options)) || min;
14468			max = parse(me, getMax(options)) || max;
14469	
14470			// In case there is no valid min/max, set limits based on unit time option
14471			min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min;
14472			max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max;
14473	
14474			// Make sure that max is strictly higher than min (required by the lookup table)
14475			me.min = Math.min(min, max);
14476			me.max = Math.max(min + 1, max);
14477	
14478			// PRIVATE
14479			me._table = [];
14480			me._timestamps = {
14481				data: timestamps,
14482				datasets: datasets,
14483				labels: labels
14484			};
14485		},
14486	
14487		buildTicks: function() {
14488			var me = this;
14489			var min = me.min;
14490			var max = me.max;
14491			var options = me.options;
14492			var tickOpts = options.ticks;
14493			var timeOpts = options.time;
14494			var timestamps = me._timestamps;
14495			var ticks = [];
14496			var capacity = me.getLabelCapacity(min);
14497			var source = tickOpts.source;
14498			var distribution = options.distribution;
14499			var i, ilen, timestamp;
14500	
14501			if (source === 'data' || (source === 'auto' && distribution === 'series')) {
14502				timestamps = timestamps.data;
14503			} else if (source === 'labels') {
14504				timestamps = timestamps.labels;
14505			} else {
14506				timestamps = generate(me, min, max, capacity);
14507			}
14508	
14509			if (options.bounds === 'ticks' && timestamps.length) {
14510				min = timestamps[0];
14511				max = timestamps[timestamps.length - 1];
14512			}
14513	
14514			// Enforce limits with user min/max options
14515			min = parse(me, getMin(options)) || min;
14516			max = parse(me, getMax(options)) || max;
14517	
14518			// Remove ticks outside the min/max range
14519			for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
14520				timestamp = timestamps[i];
14521				if (timestamp >= min && timestamp <= max) {
14522					ticks.push(timestamp);
14523				}
14524			}
14525	
14526			me.min = min;
14527			me.max = max;
14528	
14529			// PRIVATE
14530			// determineUnitForFormatting relies on the number of ticks so we don't use it when
14531			// autoSkip is enabled because we don't yet know what the final number of ticks will be
14532			me._unit = timeOpts.unit || (tickOpts.autoSkip
14533				? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, capacity)
14534				: determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max));
14535			me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined
14536				: determineMajorUnit(me._unit);
14537			me._table = buildLookupTable(me._timestamps.data, min, max, distribution);
14538			me._offsets = computeOffsets(me._table, ticks, min, max, options);
14539	
14540			if (tickOpts.reverse) {
14541				ticks.reverse();
14542			}
14543	
14544			return ticksFromTimestamps(me, ticks, me._majorUnit);
14545		},
14546	
14547		getLabelForIndex: function(index, datasetIndex) {
14548			var me = this;
14549			var adapter = me._adapter;
14550			var data = me.chart.data;
14551			var timeOpts = me.options.time;
14552			var label = data.labels && index < data.labels.length ? data.labels[index] : '';
14553			var value = data.datasets[datasetIndex].data[index];
14554	
14555			if (helpers$1.isObject(value)) {
14556				label = me.getRightValue(value);
14557			}
14558			if (timeOpts.tooltipFormat) {
14559				return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat);
14560			}
14561			if (typeof label === 'string') {
14562				return label;
14563			}
14564			return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime);
14565		},
14566	
14567		/**
14568		 * Function to format an individual tick mark
14569		 * @private
14570		 */
14571		tickFormatFunction: function(time, index, ticks, format) {
14572			var me = this;
14573			var adapter = me._adapter;
14574			var options = me.options;
14575			var formats = options.time.displayFormats;
14576			var minorFormat = formats[me._unit];
14577			var majorUnit = me._majorUnit;
14578			var majorFormat = formats[majorUnit];
14579			var tick = ticks[index];
14580			var tickOpts = options.ticks;
14581			var major = majorUnit && majorFormat && tick && tick.major;
14582			var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat);
14583			var nestedTickOpts = major ? tickOpts.major : tickOpts.minor;
14584			var formatter = resolve$5([
14585				nestedTickOpts.callback,
14586				nestedTickOpts.userCallback,
14587				tickOpts.callback,
14588				tickOpts.userCallback
14589			]);
14590	
14591			return formatter ? formatter(label, index, ticks) : label;
14592		},
14593	
14594		convertTicksToLabels: function(ticks) {
14595			var labels = [];
14596			var i, ilen;
14597	
14598			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
14599				labels.push(this.tickFormatFunction(ticks[i].value, i, ticks));
14600			}
14601	
14602			return labels;
14603		},
14604	
14605		/**
14606		 * @private
14607		 */
14608		getPixelForOffset: function(time) {
14609			var me = this;
14610			var offsets = me._offsets;
14611			var pos = interpolate$1(me._table, 'time', time, 'pos');
14612			return me.getPixelForDecimal((offsets.start + pos) * offsets.factor);
14613		},
14614	
14615		getPixelForValue: function(value, index, datasetIndex) {
14616			var me = this;
14617			var time = null;
14618	
14619			if (index !== undefined && datasetIndex !== undefined) {
14620				time = me._timestamps.datasets[datasetIndex][index];
14621			}
14622	
14623			if (time === null) {
14624				time = parse(me, value);
14625			}
14626	
14627			if (time !== null) {
14628				return me.getPixelForOffset(time);
14629			}
14630		},
14631	
14632		getPixelForTick: function(index) {
14633			var ticks = this.getTicks();
14634			return index >= 0 && index < ticks.length ?
14635				this.getPixelForOffset(ticks[index].value) :
14636				null;
14637		},
14638	
14639		getValueForPixel: function(pixel) {
14640			var me = this;
14641			var offsets = me._offsets;
14642			var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
14643			var time = interpolate$1(me._table, 'pos', pos, 'time');
14644	
14645			// DEPRECATION, we should return time directly
14646			return me._adapter._create(time);
14647		},
14648	
14649		/**
14650		 * @private
14651		 */
14652		_getLabelSize: function(label) {
14653			var me = this;
14654			var ticksOpts = me.options.ticks;
14655			var tickLabelWidth = me.ctx.measureText(label).width;
14656			var angle = helpers$1.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
14657			var cosRotation = Math.cos(angle);
14658			var sinRotation = Math.sin(angle);
14659			var tickFontSize = valueOrDefault$d(ticksOpts.fontSize, core_defaults.global.defaultFontSize);
14660	
14661			return {
14662				w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation),
14663				h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation)
14664			};
14665		},
14666	
14667		/**
14668		 * Crude approximation of what the label width might be
14669		 * @private
14670		 */
14671		getLabelWidth: function(label) {
14672			return this._getLabelSize(label).w;
14673		},
14674	
14675		/**
14676		 * @private
14677		 */
14678		getLabelCapacity: function(exampleTime) {
14679			var me = this;
14680			var timeOpts = me.options.time;
14681			var displayFormats = timeOpts.displayFormats;
14682	
14683			// pick the longest format (milliseconds) for guestimation
14684			var format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
14685			var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format);
14686			var size = me._getLabelSize(exampleLabel);
14687			var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h);
14688	
14689			if (me.options.offset) {
14690				capacity--;
14691			}
14692	
14693			return capacity > 0 ? capacity : 1;
14694		}
14695	});
14696	
14697	// INTERNAL: static default options, registered in src/index.js
14698	var _defaults$4 = defaultConfig$4;
14699	scale_time._defaults = _defaults$4;
14700	
14701	var scales = {
14702		category: scale_category,
14703		linear: scale_linear,
14704		logarithmic: scale_logarithmic,
14705		radialLinear: scale_radialLinear,
14706		time: scale_time
14707	};
14708	
14709	var FORMATS = {
14710		datetime: 'MMM D, YYYY, h:mm:ss a',
14711		millisecond: 'h:mm:ss.SSS a',
14712		second: 'h:mm:ss a',
14713		minute: 'h:mm a',
14714		hour: 'hA',
14715		day: 'MMM D',
14716		week: 'll',
14717		month: 'MMM YYYY',
14718		quarter: '[Q]Q - YYYY',
14719		year: 'YYYY'
14720	};
14721	
14722	core_adapters._date.override(typeof moment === 'function' ? {
14723		_id: 'moment', // DEBUG ONLY
14724	
14725		formats: function() {
14726			return FORMATS;
14727		},
14728	
14729		parse: function(value, format) {
14730			if (typeof value === 'string' && typeof format === 'string') {
14731				value = moment(value, format);
14732			} else if (!(value instanceof moment)) {
14733				value = moment(value);
14734			}
14735			return value.isValid() ? value.valueOf() : null;
14736		},
14737	
14738		format: function(time, format) {
14739			return moment(time).format(format);
14740		},
14741	
14742		add: function(time, amount, unit) {
14743			return moment(time).add(amount, unit).valueOf();
14744		},
14745	
14746		diff: function(max, min, unit) {
14747			return moment(max).diff(moment(min), unit);
14748		},
14749	
14750		startOf: function(time, unit, weekday) {
14751			time = moment(time);
14752			if (unit === 'isoWeek') {
14753				return time.isoWeekday(weekday).valueOf();
14754			}
14755			return time.startOf(unit).valueOf();
14756		},
14757	
14758		endOf: function(time, unit) {
14759			return moment(time).endOf(unit).valueOf();
14760		},
14761	
14762		// DEPRECATIONS
14763	
14764		/**
14765		 * Provided for backward compatibility with scale.getValueForPixel().
14766		 * @deprecated since version 2.8.0
14767		 * @todo remove at version 3
14768		 * @private
14769		 */
14770		_create: function(time) {
14771			return moment(time);
14772		},
14773	} : {});
14774	
14775	core_defaults._set('global', {
14776		plugins: {
14777			filler: {
14778				propagate: true
14779			}
14780		}
14781	});
14782	
14783	var mappers = {
14784		dataset: function(source) {
14785			var index = source.fill;
14786			var chart = source.chart;
14787			var meta = chart.getDatasetMeta(index);
14788			var visible = meta && chart.isDatasetVisible(index);
14789			var points = (visible && meta.dataset._children) || [];
14790			var length = points.length || 0;
14791	
14792			return !length ? null : function(point, i) {
14793				return (i < length && points[i]._view) || null;
14794			};
14795		},
14796	
14797		boundary: function(source) {
14798			var boundary = source.boundary;
14799			var x = boundary ? boundary.x : null;
14800			var y = boundary ? boundary.y : null;
14801	
14802			if (helpers$1.isArray(boundary)) {
14803				return function(point, i) {
14804					return boundary[i];
14805				};
14806			}
14807	
14808			return function(point) {
14809				return {
14810					x: x === null ? point.x : x,
14811					y: y === null ? point.y : y,
14812				};
14813			};
14814		}
14815	};
14816	
14817	// @todo if (fill[0] === '#')
14818	function decodeFill(el, index, count) {
14819		var model = el._model || {};
14820		var fill = model.fill;
14821		var target;
14822	
14823		if (fill === undefined) {
14824			fill = !!model.backgroundColor;
14825		}
14826	
14827		if (fill === false || fill === null) {
14828			return false;
14829		}
14830	
14831		if (fill === true) {
14832			return 'origin';
14833		}
14834	
14835		target = parseFloat(fill, 10);
14836		if (isFinite(target) && Math.floor(target) === target) {
14837			if (fill[0] === '-' || fill[0] === '+') {
14838				target = index + target;
14839			}
14840	
14841			if (target === index || target < 0 || target >= count) {
14842				return false;
14843			}
14844	
14845			return target;
14846		}
14847	
14848		switch (fill) {
14849		// compatibility
14850		case 'bottom':
14851			return 'start';
14852		case 'top':
14853			return 'end';
14854		case 'zero':
14855			return 'origin';
14856		// supported boundaries
14857		case 'origin':
14858		case 'start':
14859		case 'end':
14860			return fill;
14861		// invalid fill values
14862		default:
14863			return false;
14864		}
14865	}
14866	
14867	function computeLinearBoundary(source) {
14868		var model = source.el._model || {};
14869		var scale = source.el._scale || {};
14870		var fill = source.fill;
14871		var target = null;
14872		var horizontal;
14873	
14874		if (isFinite(fill)) {
14875			return null;
14876		}
14877	
14878		// Backward compatibility: until v3, we still need to support boundary values set on
14879		// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
14880		// controllers might still use it (e.g. the Smith chart).
14881	
14882		if (fill === 'start') {
14883			target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
14884		} else if (fill === 'end') {
14885			target = model.scaleTop === undefined ? scale.top : model.scaleTop;
14886		} else if (model.scaleZero !== undefined) {
14887			target = model.scaleZero;
14888		} else if (scale.getBasePixel) {
14889			target = scale.getBasePixel();
14890		}
14891	
14892		if (target !== undefined && target !== null) {
14893			if (target.x !== undefined && target.y !== undefined) {
14894				return target;
14895			}
14896	
14897			if (helpers$1.isFinite(target)) {
14898				horizontal = scale.isHorizontal();
14899				return {
14900					x: horizontal ? target : null,
14901					y: horizontal ? null : target
14902				};
14903			}
14904		}
14905	
14906		return null;
14907	}
14908	
14909	function computeCircularBoundary(source) {
14910		var scale = source.el._scale;
14911		var options = scale.options;
14912		var length = scale.chart.data.labels.length;
14913		var fill = source.fill;
14914		var target = [];
14915		var start, end, center, i, point;
14916	
14917		if (!length) {
14918			return null;
14919		}
14920	
14921		start = options.ticks.reverse ? scale.max : scale.min;
14922		end = options.ticks.reverse ? scale.min : scale.max;
14923		center = scale.getPointPositionForValue(0, start);
14924		for (i = 0; i < length; ++i) {
14925			point = fill === 'start' || fill === 'end'
14926				? scale.getPointPositionForValue(i, fill === 'start' ? start : end)
14927				: scale.getBasePosition(i);
14928			if (options.gridLines.circular) {
14929				point.cx = center.x;
14930				point.cy = center.y;
14931				point.angle = scale.getIndexAngle(i) - Math.PI / 2;
14932			}
14933			target.push(point);
14934		}
14935		return target;
14936	}
14937	
14938	function computeBoundary(source) {
14939		var scale = source.el._scale || {};
14940	
14941		if (scale.getPointPositionForValue) {
14942			return computeCircularBoundary(source);
14943		}
14944		return computeLinearBoundary(source);
14945	}
14946	
14947	function resolveTarget(sources, index, propagate) {
14948		var source = sources[index];
14949		var fill = source.fill;
14950		var visited = [index];
14951		var target;
14952	
14953		if (!propagate) {
14954			return fill;
14955		}
14956	
14957		while (fill !== false && visited.indexOf(fill) === -1) {
14958			if (!isFinite(fill)) {
14959				return fill;
14960			}
14961	
14962			target = sources[fill];
14963			if (!target) {
14964				return false;
14965			}
14966	
14967			if (target.visible) {
14968				return fill;
14969			}
14970	
14971			visited.push(fill);
14972			fill = target.fill;
14973		}
14974	
14975		return false;
14976	}
14977	
14978	function createMapper(source) {
14979		var fill = source.fill;
14980		var type = 'dataset';
14981	
14982		if (fill === false) {
14983			return null;
14984		}
14985	
14986		if (!isFinite(fill)) {
14987			type = 'boundary';
14988		}
14989	
14990		return mappers[type](source);
14991	}
14992	
14993	function isDrawable(point) {
14994		return point && !point.skip;
14995	}
14996	
14997	function drawArea(ctx, curve0, curve1, len0, len1) {
14998		var i, cx, cy, r;
14999	
15000		if (!len0 || !len1) {
15001			return;
15002		}
15003	
15004		// building first area curve (normal)
15005		ctx.moveTo(curve0[0].x, curve0[0].y);
15006		for (i = 1; i < len0; ++i) {
15007			helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
15008		}
15009	
15010		if (curve1[0].angle !== undefined) {
15011			cx = curve1[0].cx;
15012			cy = curve1[0].cy;
15013			r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2));
15014			for (i = len1 - 1; i > 0; --i) {
15015				ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true);
15016			}
15017			return;
15018		}
15019	
15020		// joining the two area curves
15021		ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
15022	
15023		// building opposite area curve (reverse)
15024		for (i = len1 - 1; i > 0; --i) {
15025			helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
15026		}
15027	}
15028	
15029	function doFill(ctx, points, mapper, view, color, loop) {
15030		var count = points.length;
15031		var span = view.spanGaps;
15032		var curve0 = [];
15033		var curve1 = [];
15034		var len0 = 0;
15035		var len1 = 0;
15036		var i, ilen, index, p0, p1, d0, d1, loopOffset;
15037	
15038		ctx.beginPath();
15039	
15040		for (i = 0, ilen = count; i < ilen; ++i) {
15041			index = i % count;
15042			p0 = points[index]._view;
15043			p1 = mapper(p0, index, view);
15044			d0 = isDrawable(p0);
15045			d1 = isDrawable(p1);
15046	
15047			if (loop && loopOffset === undefined && d0) {
15048				loopOffset = i + 1;
15049				ilen = count + loopOffset;
15050			}
15051	
15052			if (d0 && d1) {
15053				len0 = curve0.push(p0);
15054				len1 = curve1.push(p1);
15055			} else if (len0 && len1) {
15056				if (!span) {
15057					drawArea(ctx, curve0, curve1, len0, len1);
15058					len0 = len1 = 0;
15059					curve0 = [];
15060					curve1 = [];
15061				} else {
15062					if (d0) {
15063						curve0.push(p0);
15064					}
15065					if (d1) {
15066						curve1.push(p1);
15067					}
15068				}
15069			}
15070		}
15071	
15072		drawArea(ctx, curve0, curve1, len0, len1);
15073	
15074		ctx.closePath();
15075		ctx.fillStyle = color;
15076		ctx.fill();
15077	}
15078	
15079	var plugin_filler = {
15080		id: 'filler',
15081	
15082		afterDatasetsUpdate: function(chart, options) {
15083			var count = (chart.data.datasets || []).length;
15084			var propagate = options.propagate;
15085			var sources = [];
15086			var meta, i, el, source;
15087	
15088			for (i = 0; i < count; ++i) {
15089				meta = chart.getDatasetMeta(i);
15090				el = meta.dataset;
15091				source = null;
15092	
15093				if (el && el._model && el instanceof elements.Line) {
15094					source = {
15095						visible: chart.isDatasetVisible(i),
15096						fill: decodeFill(el, i, count),
15097						chart: chart,
15098						el: el
15099					};
15100				}
15101	
15102				meta.$filler = source;
15103				sources.push(source);
15104			}
15105	
15106			for (i = 0; i < count; ++i) {
15107				source = sources[i];
15108				if (!source) {
15109					continue;
15110				}
15111	
15112				source.fill = resolveTarget(sources, i, propagate);
15113				source.boundary = computeBoundary(source);
15114				source.mapper = createMapper(source);
15115			}
15116		},
15117	
15118		beforeDatasetsDraw: function(chart) {
15119			var metasets = chart._getSortedVisibleDatasetMetas();
15120			var ctx = chart.ctx;
15121			var meta, i, el, view, points, mapper, color;
15122	
15123			for (i = metasets.length - 1; i >= 0; --i) {
15124				meta = metasets[i].$filler;
15125	
15126				if (!meta || !meta.visible) {
15127					continue;
15128				}
15129	
15130				el = meta.el;
15131				view = el._view;
15132				points = el._children || [];
15133				mapper = meta.mapper;
15134				color = view.backgroundColor || core_defaults.global.defaultColor;
15135	
15136				if (mapper && color && points.length) {
15137					helpers$1.canvas.clipArea(ctx, chart.chartArea);
15138					doFill(ctx, points, mapper, view, color, el._loop);
15139					helpers$1.canvas.unclipArea(ctx);
15140				}
15141			}
15142		}
15143	};
15144	
15145	var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter;
15146	var noop$1 = helpers$1.noop;
15147	var valueOrDefault$e = helpers$1.valueOrDefault;
15148	
15149	core_defaults._set('global', {
15150		legend: {
15151			display: true,
15152			position: 'top',
15153			align: 'center',
15154			fullWidth: true,
15155			reverse: false,
15156			weight: 1000,
15157	
15158			// a callback that will handle
15159			onClick: function(e, legendItem) {
15160				var index = legendItem.datasetIndex;
15161				var ci = this.chart;
15162				var meta = ci.getDatasetMeta(index);
15163	
15164				// See controller.isDatasetVisible comment
15165				meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
15166	
15167				// We hid a dataset ... rerender the chart
15168				ci.update();
15169			},
15170	
15171			onHover: null,
15172			onLeave: null,
15173	
15174			labels: {
15175				boxWidth: 40,
15176				padding: 10,
15177				// Generates labels shown in the legend
15178				// Valid properties to return:
15179				// text : text to display
15180				// fillStyle : fill of coloured box
15181				// strokeStyle: stroke of coloured box
15182				// hidden : if this legend item refers to a hidden item
15183				// lineCap : cap style for line
15184				// lineDash
15185				// lineDashOffset :
15186				// lineJoin :
15187				// lineWidth :
15188				generateLabels: function(chart) {
15189					var datasets = chart.data.datasets;
15190					var options = chart.options.legend || {};
15191					var usePointStyle = options.labels && options.labels.usePointStyle;
15192	
15193					return chart._getSortedDatasetMetas().map(function(meta) {
15194						var style = meta.controller.getStyle(usePointStyle ? 0 : undefined);
15195	
15196						return {
15197							text: datasets[meta.index].label,
15198							fillStyle: style.backgroundColor,
15199							hidden: !chart.isDatasetVisible(meta.index),
15200							lineCap: style.borderCapStyle,
15201							lineDash: style.borderDash,
15202							lineDashOffset: style.borderDashOffset,
15203							lineJoin: style.borderJoinStyle,
15204							lineWidth: style.borderWidth,
15205							strokeStyle: style.borderColor,
15206							pointStyle: style.pointStyle,
15207							rotation: style.rotation,
15208	
15209							// Below is extra data used for toggling the datasets
15210							datasetIndex: meta.index
15211						};
15212					}, this);
15213				}
15214			}
15215		},
15216	
15217		legendCallback: function(chart) {
15218			var list = document.createElement('ul');
15219			var datasets = chart.data.datasets;
15220			var i, ilen, listItem, listItemSpan;
15221	
15222			list.setAttribute('class', chart.id + '-legend');
15223	
15224			for (i = 0, ilen = datasets.length; i < ilen; i++) {
15225				listItem = list.appendChild(document.createElement('li'));
15226				listItemSpan = listItem.appendChild(document.createElement('span'));
15227				listItemSpan.style.backgroundColor = datasets[i].backgroundColor;
15228				if (datasets[i].label) {
15229					listItem.appendChild(document.createTextNode(datasets[i].label));
15230				}
15231			}
15232	
15233			return list.outerHTML;
15234		}
15235	});
15236	
15237	/**
15238	 * Helper function to get the box width based on the usePointStyle option
15239	 * @param {object} labelopts - the label options on the legend
15240	 * @param {number} fontSize - the label font size
15241	 * @return {number} width of the color box area
15242	 */
15243	function getBoxWidth(labelOpts, fontSize) {
15244		return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ?
15245			fontSize :
15246			labelOpts.boxWidth;
15247	}
15248	
15249	/**
15250	 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
15251	 */
15252	var Legend = core_element.extend({
15253	
15254		initialize: function(config) {
15255			var me = this;
15256			helpers$1.extend(me, config);
15257	
15258			// Contains hit boxes for each dataset (in dataset order)
15259			me.legendHitBoxes = [];
15260	
15261			/**
15262				* @private
15263				*/
15264			me._hoveredItem = null;
15265	
15266			// Are we in doughnut mode which has a different data type
15267			me.doughnutMode = false;
15268		},
15269	
15270		// These methods are ordered by lifecycle. Utilities then follow.
15271		// Any function defined here is inherited by all legend types.
15272		// Any function can be extended by the legend type
15273	
15274		beforeUpdate: noop$1,
15275		update: function(maxWidth, maxHeight, margins) {
15276			var me = this;
15277	
15278			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
15279			me.beforeUpdate();
15280	
15281			// Absorb the master measurements
15282			me.maxWidth = maxWidth;
15283			me.maxHeight = maxHeight;
15284			me.margins = margins;
15285	
15286			// Dimensions
15287			me.beforeSetDimensions();
15288			me.setDimensions();
15289			me.afterSetDimensions();
15290			// Labels
15291			me.beforeBuildLabels();
15292			me.buildLabels();
15293			me.afterBuildLabels();
15294	
15295			// Fit
15296			me.beforeFit();
15297			me.fit();
15298			me.afterFit();
15299			//
15300			me.afterUpdate();
15301	
15302			return me.minSize;
15303		},
15304		afterUpdate: noop$1,
15305	
15306		//
15307	
15308		beforeSetDimensions: noop$1,
15309		setDimensions: function() {
15310			var me = this;
15311			// Set the unconstrained dimension before label rotation
15312			if (me.isHorizontal()) {
15313				// Reset position before calculating rotation
15314				me.width = me.maxWidth;
15315				me.left = 0;
15316				me.right = me.width;
15317			} else {
15318				me.height = me.maxHeight;
15319	
15320				// Reset position before calculating rotation
15321				me.top = 0;
15322				me.bottom = me.height;
15323			}
15324	
15325			// Reset padding
15326			me.paddingLeft = 0;
15327			me.paddingTop = 0;
15328			me.paddingRight = 0;
15329			me.paddingBottom = 0;
15330	
15331			// Reset minSize
15332			me.minSize = {
15333				width: 0,
15334				height: 0
15335			};
15336		},
15337		afterSetDimensions: noop$1,
15338	
15339		//
15340	
15341		beforeBuildLabels: noop$1,
15342		buildLabels: function() {
15343			var me = this;
15344			var labelOpts = me.options.labels || {};
15345			var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || [];
15346	
15347			if (labelOpts.filter) {
15348				legendItems = legendItems.filter(function(item) {
15349					return labelOpts.filter(item, me.chart.data);
15350				});
15351			}
15352	
15353			if (me.options.reverse) {
15354				legendItems.reverse();
15355			}
15356	
15357			me.legendItems = legendItems;
15358		},
15359		afterBuildLabels: noop$1,
15360	
15361		//
15362	
15363		beforeFit: noop$1,
15364		fit: function() {
15365			var me = this;
15366			var opts = me.options;
15367			var labelOpts = opts.labels;
15368			var display = opts.display;
15369	
15370			var ctx = me.ctx;
15371	
15372			var labelFont = helpers$1.options._parseFont(labelOpts);
15373			var fontSize = labelFont.size;
15374	
15375			// Reset hit boxes
15376			var hitboxes = me.legendHitBoxes = [];
15377	
15378			var minSize = me.minSize;
15379			var isHorizontal = me.isHorizontal();
15380	
15381			if (isHorizontal) {
15382				minSize.width = me.maxWidth; // fill all the width
15383				minSize.height = display ? 10 : 0;
15384			} else {
15385				minSize.width = display ? 10 : 0;
15386				minSize.height = me.maxHeight; // fill all the height
15387			}
15388	
15389			// Increase sizes here
15390			if (!display) {
15391				me.width = minSize.width = me.height = minSize.height = 0;
15392				return;
15393			}
15394			ctx.font = labelFont.string;
15395	
15396			if (isHorizontal) {
15397				// Labels
15398	
15399				// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
15400				var lineWidths = me.lineWidths = [0];
15401				var totalHeight = 0;
15402	
15403				ctx.textAlign = 'left';
15404				ctx.textBaseline = 'middle';
15405	
15406				helpers$1.each(me.legendItems, function(legendItem, i) {
15407					var boxWidth = getBoxWidth(labelOpts, fontSize);
15408					var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
15409	
15410					if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) {
15411						totalHeight += fontSize + labelOpts.padding;
15412						lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
15413					}
15414	
15415					// Store the hitbox width and height here. Final position will be updated in `draw`
15416					hitboxes[i] = {
15417						left: 0,
15418						top: 0,
15419						width: width,
15420						height: fontSize
15421					};
15422	
15423					lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
15424				});
15425	
15426				minSize.height += totalHeight;
15427	
15428			} else {
15429				var vPadding = labelOpts.padding;
15430				var columnWidths = me.columnWidths = [];
15431				var columnHeights = me.columnHeights = [];
15432				var totalWidth = labelOpts.padding;
15433				var currentColWidth = 0;
15434				var currentColHeight = 0;
15435	
15436				helpers$1.each(me.legendItems, function(legendItem, i) {
15437					var boxWidth = getBoxWidth(labelOpts, fontSize);
15438					var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
15439	
15440					// If too tall, go to new column
15441					if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) {
15442						totalWidth += currentColWidth + labelOpts.padding;
15443						columnWidths.push(currentColWidth); // previous column width
15444						columnHeights.push(currentColHeight);
15445						currentColWidth = 0;
15446						currentColHeight = 0;
15447					}
15448	
15449					// Get max width
15450					currentColWidth = Math.max(currentColWidth, itemWidth);
15451					currentColHeight += fontSize + vPadding;
15452	
15453					// Store the hitbox width and height here. Final position will be updated in `draw`
15454					hitboxes[i] = {
15455						left: 0,
15456						top: 0,
15457						width: itemWidth,
15458						height: fontSize
15459					};
15460				});
15461	
15462				totalWidth += currentColWidth;
15463				columnWidths.push(currentColWidth);
15464				columnHeights.push(currentColHeight);
15465				minSize.width += totalWidth;
15466			}
15467	
15468			me.width = minSize.width;
15469			me.height = minSize.height;
15470		},
15471		afterFit: noop$1,
15472	
15473		// Shared Methods
15474		isHorizontal: function() {
15475			return this.options.position === 'top' || this.options.position === 'bottom';
15476		},
15477	
15478		// Actually draw the legend on the canvas
15479		draw: function() {
15480			var me = this;
15481			var opts = me.options;
15482			var labelOpts = opts.labels;
15483			var globalDefaults = core_defaults.global;
15484			var defaultColor = globalDefaults.defaultColor;
15485			var lineDefault = globalDefaults.elements.line;
15486			var legendHeight = me.height;
15487			var columnHeights = me.columnHeights;
15488			var legendWidth = me.width;
15489			var lineWidths = me.lineWidths;
15490	
15491			if (!opts.display) {
15492				return;
15493			}
15494	
15495			var rtlHelper = getRtlHelper$1(opts.rtl, me.left, me.minSize.width);
15496			var ctx = me.ctx;
15497			var fontColor = valueOrDefault$e(labelOpts.fontColor, globalDefaults.defaultFontColor);
15498			var labelFont = helpers$1.options._parseFont(labelOpts);
15499			var fontSize = labelFont.size;
15500			var cursor;
15501	
15502			// Canvas setup
15503			ctx.textAlign = rtlHelper.textAlign('left');
15504			ctx.textBaseline = 'middle';
15505			ctx.lineWidth = 0.5;
15506			ctx.strokeStyle = fontColor; // for strikethrough effect
15507			ctx.fillStyle = fontColor; // render in correct colour
15508			ctx.font = labelFont.string;
15509	
15510			var boxWidth = getBoxWidth(labelOpts, fontSize);
15511			var hitboxes = me.legendHitBoxes;
15512	
15513			// current position
15514			var drawLegendBox = function(x, y, legendItem) {
15515				if (isNaN(boxWidth) || boxWidth <= 0) {
15516					return;
15517				}
15518	
15519				// Set the ctx for the box
15520				ctx.save();
15521	
15522				var lineWidth = valueOrDefault$e(legendItem.lineWidth, lineDefault.borderWidth);
15523				ctx.fillStyle = valueOrDefault$e(legendItem.fillStyle, defaultColor);
15524				ctx.lineCap = valueOrDefault$e(legendItem.lineCap, lineDefault.borderCapStyle);
15525				ctx.lineDashOffset = valueOrDefault$e(legendItem.lineDashOffset, lineDefault.borderDashOffset);
15526				ctx.lineJoin = valueOrDefault$e(legendItem.lineJoin, lineDefault.borderJoinStyle);
15527				ctx.lineWidth = lineWidth;
15528				ctx.strokeStyle = valueOrDefault$e(legendItem.strokeStyle, defaultColor);
15529	
15530				if (ctx.setLineDash) {
15531					// IE 9 and 10 do not support line dash
15532					ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash));
15533				}
15534	
15535				if (labelOpts && labelOpts.usePointStyle) {
15536					// Recalculate x and y for drawPoint() because its expecting
15537					// x and y to be center of figure (instead of top left)
15538					var radius = boxWidth * Math.SQRT2 / 2;
15539					var centerX = rtlHelper.xPlus(x, boxWidth / 2);
15540					var centerY = y + fontSize / 2;
15541	
15542					// Draw pointStyle as legend symbol
15543					helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation);
15544				} else {
15545					// Draw box as legend symbol
15546					ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize);
15547					if (lineWidth !== 0) {
15548						ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize);
15549					}
15550				}
15551	
15552				ctx.restore();
15553			};
15554	
15555			var fillText = function(x, y, legendItem, textWidth) {
15556				var halfFontSize = fontSize / 2;
15557				var xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize);
15558				var yMiddle = y + halfFontSize;
15559	
15560				ctx.fillText(legendItem.text, xLeft, yMiddle);
15561	
15562				if (legendItem.hidden) {
15563					// Strikethrough the text if hidden
15564					ctx.beginPath();
15565					ctx.lineWidth = 2;
15566					ctx.moveTo(xLeft, yMiddle);
15567					ctx.lineTo(rtlHelper.xPlus(xLeft, textWidth), yMiddle);
15568					ctx.stroke();
15569				}
15570			};
15571	
15572			var alignmentOffset = function(dimension, blockSize) {
15573				switch (opts.align) {
15574				case 'start':
15575					return labelOpts.padding;
15576				case 'end':
15577					return dimension - blockSize;
15578				default: // center
15579					return (dimension - blockSize + labelOpts.padding) / 2;
15580				}
15581			};
15582	
15583			// Horizontal
15584			var isHorizontal = me.isHorizontal();
15585			if (isHorizontal) {
15586				cursor = {
15587					x: me.left + alignmentOffset(legendWidth, lineWidths[0]),
15588					y: me.top + labelOpts.padding,
15589					line: 0
15590				};
15591			} else {
15592				cursor = {
15593					x: me.left + labelOpts.padding,
15594					y: me.top + alignmentOffset(legendHeight, columnHeights[0]),
15595					line: 0
15596				};
15597			}
15598	
15599			helpers$1.rtl.overrideTextDirection(me.ctx, opts.textDirection);
15600	
15601			var itemHeight = fontSize + labelOpts.padding;
15602			helpers$1.each(me.legendItems, function(legendItem, i) {
15603				var textWidth = ctx.measureText(legendItem.text).width;
15604				var width = boxWidth + (fontSize / 2) + textWidth;
15605				var x = cursor.x;
15606				var y = cursor.y;
15607	
15608				rtlHelper.setWidth(me.minSize.width);
15609	
15610				// Use (me.left + me.minSize.width) and (me.top + me.minSize.height)
15611				// instead of me.right and me.bottom because me.width and me.height
15612				// may have been changed since me.minSize was calculated
15613				if (isHorizontal) {
15614					if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) {
15615						y = cursor.y += itemHeight;
15616						cursor.line++;
15617						x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]);
15618					}
15619				} else if (i > 0 && y + itemHeight > me.top + me.minSize.height) {
15620					x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
15621					cursor.line++;
15622					y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]);
15623				}
15624	
15625				var realX = rtlHelper.x(x);
15626	
15627				drawLegendBox(realX, y, legendItem);
15628	
15629				hitboxes[i].left = rtlHelper.leftForLtr(realX, hitboxes[i].width);
15630				hitboxes[i].top = y;
15631	
15632				// Fill the actual label
15633				fillText(realX, y, legendItem, textWidth);
15634	
15635				if (isHorizontal) {
15636					cursor.x += width + labelOpts.padding;
15637				} else {
15638					cursor.y += itemHeight;
15639				}
15640			});
15641	
15642			helpers$1.rtl.restoreTextDirection(me.ctx, opts.textDirection);
15643		},
15644	
15645		/**
15646		 * @private
15647		 */
15648		_getLegendItemAt: function(x, y) {
15649			var me = this;
15650			var i, hitBox, lh;
15651	
15652			if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
15653				// See if we are touching one of the dataset boxes
15654				lh = me.legendHitBoxes;
15655				for (i = 0; i < lh.length; ++i) {
15656					hitBox = lh[i];
15657	
15658					if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
15659						// Touching an element
15660						return me.legendItems[i];
15661					}
15662				}
15663			}
15664	
15665			return null;
15666		},
15667	
15668		/**
15669		 * Handle an event
15670		 * @private
15671		 * @param {IEvent} event - The event to handle
15672		 */
15673		handleEvent: function(e) {
15674			var me = this;
15675			var opts = me.options;
15676			var type = e.type === 'mouseup' ? 'click' : e.type;
15677			var hoveredItem;
15678	
15679			if (type === 'mousemove') {
15680				if (!opts.onHover && !opts.onLeave) {
15681					return;
15682				}
15683			} else if (type === 'click') {
15684				if (!opts.onClick) {
15685					return;
15686				}
15687			} else {
15688				return;
15689			}
15690	
15691			// Chart event already has relative position in it
15692			hoveredItem = me._getLegendItemAt(e.x, e.y);
15693	
15694			if (type === 'click') {
15695				if (hoveredItem && opts.onClick) {
15696					// use e.native for backwards compatibility
15697					opts.onClick.call(me, e.native, hoveredItem);
15698				}
15699			} else {
15700				if (opts.onLeave && hoveredItem !== me._hoveredItem) {
15701					if (me._hoveredItem) {
15702						opts.onLeave.call(me, e.native, me._hoveredItem);
15703					}
15704					me._hoveredItem = hoveredItem;
15705				}
15706	
15707				if (opts.onHover && hoveredItem) {
15708					// use e.native for backwards compatibility
15709					opts.onHover.call(me, e.native, hoveredItem);
15710				}
15711			}
15712		}
15713	});
15714	
15715	function createNewLegendAndAttach(chart, legendOpts) {
15716		var legend = new Legend({
15717			ctx: chart.ctx,
15718			options: legendOpts,
15719			chart: chart
15720		});
15721	
15722		core_layouts.configure(chart, legend, legendOpts);
15723		core_layouts.addBox(chart, legend);
15724		chart.legend = legend;
15725	}
15726	
15727	var plugin_legend = {
15728		id: 'legend',
15729	
15730		/**
15731		 * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
15732		 * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
15733		 * the plugin, which one will be re-exposed in the chart.js file.
15734		 * https://github.com/chartjs/Chart.js/pull/2640
15735		 * @private
15736		 */
15737		_element: Legend,
15738	
15739		beforeInit: function(chart) {
15740			var legendOpts = chart.options.legend;
15741	
15742			if (legendOpts) {
15743				createNewLegendAndAttach(chart, legendOpts);
15744			}
15745		},
15746	
15747		beforeUpdate: function(chart) {
15748			var legendOpts = chart.options.legend;
15749			var legend = chart.legend;
15750	
15751			if (legendOpts) {
15752				helpers$1.mergeIf(legendOpts, core_defaults.global.legend);
15753	
15754				if (legend) {
15755					core_layouts.configure(chart, legend, legendOpts);
15756					legend.options = legendOpts;
15757				} else {
15758					createNewLegendAndAttach(chart, legendOpts);
15759				}
15760			} else if (legend) {
15761				core_layouts.removeBox(chart, legend);
15762				delete chart.legend;
15763			}
15764		},
15765	
15766		afterEvent: function(chart, e) {
15767			var legend = chart.legend;
15768			if (legend) {
15769				legend.handleEvent(e);
15770			}
15771		}
15772	};
15773	
15774	var noop$2 = helpers$1.noop;
15775	
15776	core_defaults._set('global', {
15777		title: {
15778			display: false,
15779			fontStyle: 'bold',
15780			fullWidth: true,
15781			padding: 10,
15782			position: 'top',
15783			text: '',
15784			weight: 2000         // by default greater than legend (1000) to be above
15785		}
15786	});
15787	
15788	/**
15789	 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
15790	 */
15791	var Title = core_element.extend({
15792		initialize: function(config) {
15793			var me = this;
15794			helpers$1.extend(me, config);
15795	
15796			// Contains hit boxes for each dataset (in dataset order)
15797			me.legendHitBoxes = [];
15798		},
15799	
15800		// These methods are ordered by lifecycle. Utilities then follow.
15801	
15802		beforeUpdate: noop$2,
15803		update: function(maxWidth, maxHeight, margins) {
15804			var me = this;
15805	
15806			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
15807			me.beforeUpdate();
15808	
15809			// Absorb the master measurements
15810			me.maxWidth = maxWidth;
15811			me.maxHeight = maxHeight;
15812			me.margins = margins;
15813	
15814			// Dimensions
15815			me.beforeSetDimensions();
15816			me.setDimensions();
15817			me.afterSetDimensions();
15818			// Labels
15819			me.beforeBuildLabels();
15820			me.buildLabels();
15821			me.afterBuildLabels();
15822	
15823			// Fit
15824			me.beforeFit();
15825			me.fit();
15826			me.afterFit();
15827			//
15828			me.afterUpdate();
15829	
15830			return me.minSize;
15831	
15832		},
15833		afterUpdate: noop$2,
15834	
15835		//
15836	
15837		beforeSetDimensions: noop$2,
15838		setDimensions: function() {
15839			var me = this;
15840			// Set the unconstrained dimension before label rotation
15841			if (me.isHorizontal()) {
15842				// Reset position before calculating rotation
15843				me.width = me.maxWidth;
15844				me.left = 0;
15845				me.right = me.width;
15846			} else {
15847				me.height = me.maxHeight;
15848	
15849				// Reset position before calculating rotation
15850				me.top = 0;
15851				me.bottom = me.height;
15852			}
15853	
15854			// Reset padding
15855			me.paddingLeft = 0;
15856			me.paddingTop = 0;
15857			me.paddingRight = 0;
15858			me.paddingBottom = 0;
15859	
15860			// Reset minSize
15861			me.minSize = {
15862				width: 0,
15863				height: 0
15864			};
15865		},
15866		afterSetDimensions: noop$2,
15867	
15868		//
15869	
15870		beforeBuildLabels: noop$2,
15871		buildLabels: noop$2,
15872		afterBuildLabels: noop$2,
15873	
15874		//
15875	
15876		beforeFit: noop$2,
15877		fit: function() {
15878			var me = this;
15879			var opts = me.options;
15880			var minSize = me.minSize = {};
15881			var isHorizontal = me.isHorizontal();
15882			var lineCount, textSize;
15883	
15884			if (!opts.display) {
15885				me.width = minSize.width = me.height = minSize.height = 0;
15886				return;
15887			}
15888	
15889			lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1;
15890			textSize = lineCount * helpers$1.options._parseFont(opts).lineHeight + opts.padding * 2;
15891	
15892			me.width = minSize.width = isHorizontal ? me.maxWidth : textSize;
15893			me.height = minSize.height = isHorizontal ? textSize : me.maxHeight;
15894		},
15895		afterFit: noop$2,
15896	
15897		// Shared Methods
15898		isHorizontal: function() {
15899			var pos = this.options.position;
15900			return pos === 'top' || pos === 'bottom';
15901		},
15902	
15903		// Actually draw the title block on the canvas
15904		draw: function() {
15905			var me = this;
15906			var ctx = me.ctx;
15907			var opts = me.options;
15908	
15909			if (!opts.display) {
15910				return;
15911			}
15912	
15913			var fontOpts = helpers$1.options._parseFont(opts);
15914			var lineHeight = fontOpts.lineHeight;
15915			var offset = lineHeight / 2 + opts.padding;
15916			var rotation = 0;
15917			var top = me.top;
15918			var left = me.left;
15919			var bottom = me.bottom;
15920			var right = me.right;
15921			var maxWidth, titleX, titleY;
15922	
15923			ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour
15924			ctx.font = fontOpts.string;
15925	
15926			// Horizontal
15927			if (me.isHorizontal()) {
15928				titleX = left + ((right - left) / 2); // midpoint of the width
15929				titleY = top + offset;
15930				maxWidth = right - left;
15931			} else {
15932				titleX = opts.position === 'left' ? left + offset : right - offset;
15933				titleY = top + ((bottom - top) / 2);
15934				maxWidth = bottom - top;
15935				rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
15936			}
15937	
15938			ctx.save();
15939			ctx.translate(titleX, titleY);
15940			ctx.rotate(rotation);
15941			ctx.textAlign = 'center';
15942			ctx.textBaseline = 'middle';
15943	
15944			var text = opts.text;
15945			if (helpers$1.isArray(text)) {
15946				var y = 0;
15947				for (var i = 0; i < text.length; ++i) {
15948					ctx.fillText(text[i], 0, y, maxWidth);
15949					y += lineHeight;
15950				}
15951			} else {
15952				ctx.fillText(text, 0, 0, maxWidth);
15953			}
15954	
15955			ctx.restore();
15956		}
15957	});
15958	
15959	function createNewTitleBlockAndAttach(chart, titleOpts) {
15960		var title = new Title({
15961			ctx: chart.ctx,
15962			options: titleOpts,
15963			chart: chart
15964		});
15965	
15966		core_layouts.configure(chart, title, titleOpts);
15967		core_layouts.addBox(chart, title);
15968		chart.titleBlock = title;
15969	}
15970	
15971	var plugin_title = {
15972		id: 'title',
15973	
15974		/**
15975		 * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
15976		 * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
15977		 * the plugin, which one will be re-exposed in the chart.js file.
15978		 * https://github.com/chartjs/Chart.js/pull/2640
15979		 * @private
15980		 */
15981		_element: Title,
15982	
15983		beforeInit: function(chart) {
15984			var titleOpts = chart.options.title;
15985	
15986			if (titleOpts) {
15987				createNewTitleBlockAndAttach(chart, titleOpts);
15988			}
15989		},
15990	
15991		beforeUpdate: function(chart) {
15992			var titleOpts = chart.options.title;
15993			var titleBlock = chart.titleBlock;
15994	
15995			if (titleOpts) {
15996				helpers$1.mergeIf(titleOpts, core_defaults.global.title);
15997	
15998				if (titleBlock) {
15999					core_layouts.configure(chart, titleBlock, titleOpts);
16000					titleBlock.options = titleOpts;
16001				} else {
16002					createNewTitleBlockAndAttach(chart, titleOpts);
16003				}
16004			} else if (titleBlock) {
16005				core_layouts.removeBox(chart, titleBlock);
16006				delete chart.titleBlock;
16007			}
16008		}
16009	};
16010	
16011	var plugins = {};
16012	var filler = plugin_filler;
16013	var legend = plugin_legend;
16014	var title = plugin_title;
16015	plugins.filler = filler;
16016	plugins.legend = legend;
16017	plugins.title = title;
16018	
16019	/**
16020	 * @namespace Chart
16021	 */
16022	
16023	
16024	core_controller.helpers = helpers$1;
16025	
16026	// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
16027	core_helpers();
16028	
16029	core_controller._adapters = core_adapters;
16030	core_controller.Animation = core_animation;
16031	core_controller.animationService = core_animations;
16032	core_controller.controllers = controllers;
16033	core_controller.DatasetController = core_datasetController;
16034	core_controller.defaults = core_defaults;
16035	core_controller.Element = core_element;
16036	core_controller.elements = elements;
16037	core_controller.Interaction = core_interaction;
16038	core_controller.layouts = core_layouts;
16039	core_controller.platform = platform;
16040	core_controller.plugins = core_plugins;
16041	core_controller.Scale = core_scale;
16042	core_controller.scaleService = core_scaleService;
16043	core_controller.Ticks = core_ticks;
16044	core_controller.Tooltip = core_tooltip;
16045	
16046	// Register built-in scales
16047	
16048	core_controller.helpers.each(scales, function(scale, type) {
16049		core_controller.scaleService.registerScaleType(type, scale, scale._defaults);
16050	});
16051	
16052	// Load to register built-in adapters (as side effects)
16053	
16054	
16055	// Loading built-in plugins
16056	
16057	for (var k in plugins) {
16058		if (plugins.hasOwnProperty(k)) {
16059			core_controller.plugins.register(plugins[k]);
16060		}
16061	}
16062	
16063	core_controller.platform.initialize();
16064	
16065	var src = core_controller;
16066	if (typeof window !== 'undefined') {
16067		window.Chart = core_controller;
16068	}
16069	
16070	// DEPRECATIONS
16071	
16072	/**
16073	 * Provided for backward compatibility, not available anymore
16074	 * @namespace Chart.Chart
16075	 * @deprecated since version 2.8.0
16076	 * @todo remove at version 3
16077	 * @private
16078	 */
16079	core_controller.Chart = core_controller;
16080	
16081	/**
16082	 * Provided for backward compatibility, not available anymore
16083	 * @namespace Chart.Legend
16084	 * @deprecated since version 2.1.5
16085	 * @todo remove at version 3
16086	 * @private
16087	 */
16088	core_controller.Legend = plugins.legend._element;
16089	
16090	/**
16091	 * Provided for backward compatibility, not available anymore
16092	 * @namespace Chart.Title
16093	 * @deprecated since version 2.1.5
16094	 * @todo remove at version 3
16095	 * @private
16096	 */
16097	core_controller.Title = plugins.title._element;
16098	
16099	/**
16100	 * Provided for backward compatibility, use Chart.plugins instead
16101	 * @namespace Chart.pluginService
16102	 * @deprecated since version 2.1.5
16103	 * @todo remove at version 3
16104	 * @private
16105	 */
16106	core_controller.pluginService = core_controller.plugins;
16107	
16108	/**
16109	 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
16110	 * effect, instead simply create/register plugins via plain JavaScript objects.
16111	 * @interface Chart.PluginBase
16112	 * @deprecated since version 2.5.0
16113	 * @todo remove at version 3
16114	 * @private
16115	 */
16116	core_controller.PluginBase = core_controller.Element.extend({});
16117	
16118	/**
16119	 * Provided for backward compatibility, use Chart.helpers.canvas instead.
16120	 * @namespace Chart.canvasHelpers
16121	 * @deprecated since version 2.6.0
16122	 * @todo remove at version 3
16123	 * @private
16124	 */
16125	core_controller.canvasHelpers = core_controller.helpers.canvas;
16126	
16127	/**
16128	 * Provided for backward compatibility, use Chart.layouts instead.
16129	 * @namespace Chart.layoutService
16130	 * @deprecated since version 2.7.3
16131	 * @todo remove at version 3
16132	 * @private
16133	 */
16134	core_controller.layoutService = core_controller.layouts;
16135	
16136	/**
16137	 * Provided for backward compatibility, not available anymore.
16138	 * @namespace Chart.LinearScaleBase
16139	 * @deprecated since version 2.8
16140	 * @todo remove at version 3
16141	 * @private
16142	 */
16143	core_controller.LinearScaleBase = scale_linearbase;
16144	
16145	/**
16146	 * Provided for backward compatibility, instead we should create a new Chart
16147	 * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`).
16148	 * @deprecated since version 2.8.0
16149	 * @todo remove at version 3
16150	 */
16151	core_controller.helpers.each(
16152		[
16153			'Bar',
16154			'Bubble',
16155			'Doughnut',
16156			'Line',
16157			'PolarArea',
16158			'Radar',
16159			'Scatter'
16160		],
16161		function(klass) {
16162			core_controller[klass] = function(ctx, cfg) {
16163				return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, {
16164					type: klass.charAt(0).toLowerCase() + klass.slice(1)
16165				}));
16166			};
16167		}
16168	);
16169	
16170	return src;
16171	
16172	})));

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.