PageSourceSearch

https://www.cmcaninde.ce.gov.br/wp-content/plugins/skt-skill-bar/skill_bar/js/Chart.js?ver=3.1

js cmcaninde.ce.gov.br collected 2026-09-25 12:40:53 UTC 445,051 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) {
8typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) :
9typeof 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
13moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment;
14
15function createCommonjsModule(fn, module) {
16	return module = { exports: {} }, fn(module, module.exports), module.exports;
17}
18
19function getCjsExportFromNamespace (n) {
20	return n && n['default'] || n;
21}
22
23var 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: 440,698 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
174var 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
182var reverseKeywords = {};
183for (var key in colorName) {
184	if (colorName.hasOwnProperty(key)) {
185		reverseKeywords[colorName[key]] = key;
186	}
187}
188
189var 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
208for (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
231convert.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
271convert.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
316convert.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
328convert.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 * */
348function 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
356convert.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
383convert.keyword.rgb = function (keyword) {
384	return colorName[keyword];
385};
386
387convert.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
404convert.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
428convert.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
477convert.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
495convert.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
523convert.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
543convert.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
586convert.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
602convert.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
634convert.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
657convert.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
683convert.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
703convert.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
718convert.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
742convert.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
748convert.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
775convert.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
797convert.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
814convert.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
823convert.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
845convert.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
879convert.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
898convert.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
912convert.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
951convert.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
965convert.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
982convert.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
989convert.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
1003convert.apple.rgb = function (apple) {
1004	return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
1005};
1006
1007convert.rgb.apple = function (rgb) {
1008	return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
1009};
1010
1011convert.gray.rgb = function (args) {
1012	return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
1013};
1014
1015convert.gray.hsl = convert.gray.hsv = function (args) {
1016	return [0, 0, args[0]];
1017};
1018
1019convert.gray.hwb = function (gray) {
1020	return [0, 100, gray[0]];
1021};
1022
1023convert.gray.cmyk = function (gray) {
1024	return [0, 0, 0, gray[0]];
1025};
1026
1027convert.gray.lab = function (gray) {
1028	return [gray[0], 0, 0];
1029};
1030
1031convert.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
1039convert.rgb.gray = function (rgb) {
1040	var val = (rgb[0] + rgb[1] + rgb[2]) / 3;
1041	return [val / 255 * 100];
1042};
1043});
1044var conversions_1 = conversions.rgb;
1045var conversions_2 = conversions.hsl;
1046var conversions_3 = conversions.hsv;
1047var conversions_4 = conversions.hwb;
1048var conversions_5 = conversions.cmyk;
1049var conversions_6 = conversions.xyz;
1050var conversions_7 = conversions.lab;
1051var conversions_8 = conversions.lch;
1052var conversions_9 = conversions.hex;
1053var conversions_10 = conversions.keyword;
1054var conversions_11 = conversions.ansi16;
1055var conversions_12 = conversions.ansi256;
1056var conversions_13 = conversions.hcg;
1057var conversions_14 = conversions.apple;
1058var 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
1071function 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
1089function 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
1114function link(from, to) {
1115	return function (args) {
1116		return to(from(args));
1117	};
1118}
1119
1120function 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
1135var 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
1155var convert = {};
1156
1157var models = Object.keys(conversions);
1158
1159function 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
1180function 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
1212models.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
1229var colorConvert = convert;
1230
1231var 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
1385var 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
1404function 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
1473function 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
1489function 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
1505function getRgb(string) {
1506   var rgba = getRgba(string);
1507   return rgba && rgba.slice(0, 3);
1508}
1509
1510function getHsl(string) {
1511  var hsla = getHsla(string);
1512  return hsla && hsla.slice(0, 3);
1513}
1514
1515function 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
1529function 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
1541function 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
1548function 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
1556function 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
1567function 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
1574function 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
1581function 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)
1591function 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
1599function keyword(rgb) {
1600  return reverseNames[rgb.slice(0, 3)];
1601}
1602
1603// helpers
1604function scale(num, min, max) {
1605   return Math.min(Math.max(min, num), max);
1606}
1607
1608function 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
1615var reverseNames = {};
1616for (var name in colorName$1) {
1617   reverseNames[colorName$1[name]] = name;
1618}
1619
1620/* MIT license */
1621
1622
1623
1624var 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
1669Color.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
1977Color.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
1985Color.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
1993Color.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
2009Color.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
2066Color.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
2083Color.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
2100if (typeof window !== 'undefined') {
2101	window.Color = Color;
2102}
2103
2104var chartjsColor = Color;
2105
2106function isValidKey(key) {
2107	return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;
2108}
2109
2110/**
2111 * @namespace Chart.helpers
2112 */
2113var 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
2438var 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 */
2449helpers.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 */
2459helpers.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 */
2470helpers.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 */
2479helpers.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 */
2486var 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
2713var 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 */
2726helpers_core.easingEffects = effects;
2727
2728var PI = Math.PI;
2729var RAD_PER_DEG = PI / 180;
2730var DOUBLE_PI = PI * 2;
2731var HALF_PI = PI / 2;
2732var QUARTER_PI = PI / 4;
2733var TWO_THIRDS_PI = PI * 2 / 3;
2734
2735/**
2736 * @namespace Chart.helpers.canvas
2737 */
2738var 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
2958var 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 */
2969helpers_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 */
2978helpers_core.drawRoundedRectangle = function(ctx) {
2979	ctx.beginPath();
2980	exports$1.roundedRect.apply(exports$1, arguments);
2981};
2982
2983var 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'
2994defaults._set('global', {
2995	defaultColor: 'rgba(0,0,0,0.1)',
2996	defaultFontColor: '#666',
2997	defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
2998	defaultFontSize: 12,
2999	defaultFontStyle: 'normal',
3000	defaultLineHeight: 1.2,
3001	showLines: true
3002});
3003
3004var core_defaults = defaults;
3005
3006var 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 */
3014function 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 */
3029var 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 */
3151var 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
3188var 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 */
3199helpers_core.log10 = exports$2.log10;
3200
3201var 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
3224var 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
3243var getAdapter = function(rtl, rectX, width) {
3244	return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter();
3245};
3246
3247var 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
3261var 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
3269var helpers_rtl = {
3270	getRtlAdapter: getAdapter,
3271	overrideTextDirection: overrideTextDirection,
3272	restoreTextDirection: restoreTextDirection,
3273};
3274
3275var helpers$1 = helpers_core;
3276var easing = helpers_easing;
3277var canvas = helpers_canvas;
3278var options = helpers_options;
3279var math = helpers_math;
3280var rtl = helpers_rtl;
3281helpers$1.easing = easing;
3282helpers$1.canvas = canvas;
3283helpers$1.options = options;
3284helpers$1.math = math;
3285helpers$1.rtl = rtl;
3286
3287function 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
3336var Element = function(configuration) {
3337	helpers$1.extend(this, configuration);
3338	this.initialize.apply(this, arguments);
3339};
3340
3341helpers$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
3395Element.extend = helpers$1.inherits;
3396
3397var core_element = Element;
3398
3399var 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
3410var 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 */
3420Object.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 */
3432Object.defineProperty(exports$3.prototype, 'chartInstance', {
3433	get: function() {
3434		return this.chart;
3435	},
3436	set: function(value) {
3437		this.chart = value;
3438	}
3439});
3440
3441core_defaults._set('global', {
3442	animation: {
3443		duration: 1000,
3444		easing: 'easeOutQuart',
3445		onProgress: helpers$1.noop,
3446		onComplete: helpers$1.noop
3447	}
3448});
3449
3450var 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
3558var resolve = helpers$1.options.resolve;
3559
3560var 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 */
3567function 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 */
3608function 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)
3632var DatasetController = function(chart, datasetIndex) {
3633	this.initialize(chart, datasetIndex);
3634};
3635
3636helpers$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
4126DatasetController.extend = helpers$1.inherits;
4127
4128var core_datasetController = DatasetController;
4129
4130var TAU = Math.PI * 2;
4131
4132core_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
4143function 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
4165function 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
4192function 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
4218var 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
4333var valueOrDefault$1 = helpers$1.valueOrDefault;
4334
4335var defaultColor = core_defaults.global.defaultColor;
4336
4337core_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
4354var 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
4439var valueOrDefault$2 = helpers$1.valueOrDefault;
4440
4441var defaultColor$1 = core_defaults.global.defaultColor;
4442
4443core_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
4459function xRange(mouseX) {
4460	var vm = this._view;
4461	return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
4462}
4463
4464function yRange(mouseY) {
4465	var vm = this._view;
4466	return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
4467}
4468
4469var 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
4527var defaultColor$2 = core_defaults.global.defaultColor;
4528
4529core_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
4540function 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 */
4550function 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
4575function swap(orig, v1, v2) {
4576	return orig === v1 ? v2 : orig === v2 ? v1 : orig;
4577}
4578
4579function 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
4599function 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
4621function 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
4643function 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
4653var 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
4735var elements = {};
4736var Arc = element_arc;
4737var Line = element_line;
4738var Point = element_point;
4739var Rectangle = element_rectangle;
4740elements.Arc = Arc;
4741elements.Line = Line;
4742elements.Point = Point;
4743elements.Rectangle = Rectangle;
4744
4745var deprecated = helpers$1._deprecated;
4746var valueOrDefault$3 = helpers$1.valueOrDefault;
4747
4748core_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
4768core_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 */
4781function 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 */
4804function 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 */
4837function 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
4866var 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
5170var valueOrDefault$4 = helpers$1.valueOrDefault;
5171var resolve$1 = helpers$1.options.resolve;
5172
5173core_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
5206var 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
5336var valueOrDefault$5 = helpers$1.valueOrDefault;
5337
5338var PI$1 = Math.PI;
5339var DOUBLE_PI$1 = PI$1 * 2;
5340var HALF_PI$1 = PI$1 / 2;
5341
5342core_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
5449var 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
5720core_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
5754core_defaults._set('global', {
5755	datasets: {
5756		horizontalBar: {
5757			categoryPercentage: 0.8,
5758			barPercentage: 0.9
5759		}
5760	}
5761});
5762
5763var 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
5779var valueOrDefault$6 = helpers$1.valueOrDefault;
5780var resolve$2 = helpers$1.options.resolve;
5781var isPointInArea = helpers$1.canvas._isPointInArea;
5782
5783core_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
5803function 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
5814function 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
5827function 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
5848var 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
6144var resolve$3 = helpers$1.options.resolve;
6145
6146core_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
6243var 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
6429core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut));
6430core_defaults._set('pie', {
6431	cutoutPercentage: 0
6432});
6433
6434// Pie charts are Doughnut chart with different defaults
6435var controller_pie = controller_doughnut;
6436
6437var valueOrDefault$7 = helpers$1.valueOrDefault;
6438
6439core_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
6452var 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
6648core_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
6678core_defaults._set('global', {
6679	datasets: {
6680		scatter: {
6681			showLine: false
6682		}
6683	}
6684});
6685
6686// Scatter charts use line controllers
6687var 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
6693var 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 */
6711function 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 */
6727function 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 */
6748function 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 */
6768function 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 */
6796function 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
6807function 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 */
6844var 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
7005var extend = helpers$1.extend;
7006
7007function filterByPosition(array, position) {
7008	return helpers$1.where(array, function(v) {
7009		return v.pos === position;
7010	});
7011}
7012
7013function 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
7023function 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
7040function 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
7053function 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
7069function getCombinedMax(maxPadding, chartArea, a, b) {
7070	return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
7071}
7072
7073function 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
7106function 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
7120function 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
7136function 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
7165function 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
7195core_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.
7226var 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
7395var 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
7406var 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
7408var platform_dom$1 = /*#__PURE__*/Object.freeze({
7409__proto__: null,
7410'default': platform_dom
7411});
7412
7413var stylesheet = getCjsExportFromNamespace(platform_dom$1);
7414
7415var EXPANDO_KEY = '$chartjs';
7416var CSS_PREFIX = 'chartjs-';
7417var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor';
7418var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
7419var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
7420var 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 */
7427var 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 */
7448function 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 */
7459function 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 */
7514var 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
7532var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
7533
7534function addListener(node, type, listener) {
7535	node.addEventListener(type, listener, eventListenerOptions);
7536}
7537
7538function removeListener(node, type, listener) {
7539	node.removeEventListener(type, listener, eventListenerOptions);
7540}
7541
7542function 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
7552function 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
7558function 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
7576function 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
7583function 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
7616function 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
7638function 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
7653function 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
7690function 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 */
7707function 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
7720var 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 */
7866helpers$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 */
7877helpers$1.removeEvent = removeListener;
7878
7879// @TODO Make possible to select another platform at build time.
7880var 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 */
7887var 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
7929core_defaults._set('global', {
7930	plugins: {}
7931});
7932
7933/**
7934 * The plugin service singleton
7935 * @namespace Chart.plugins
7936 * @since 2.1.0
7937 */
7938var 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
8096var 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
8134var valueOrDefault$8 = helpers$1.valueOrDefault;
8135var getRtlHelper = helpers$1.rtl.getRtlAdapter;
8136
8137core_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
8233var 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
8306function 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 */
8325function 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 */
8338function 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 */
8363function 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 */
8418function 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 */
8486function 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 */
8559function 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
8610function 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 */
8621function getBeforeAfterBodyLines(callback) {
8622	return pushOrConcat([], splitNewlines(callback));
8623}
8624
8625var 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 */
9155var positioners_1 = positioners;
9156
9157var core_tooltip = exports$4;
9158core_tooltip.positioners = positioners_1;
9159
9160var valueOrDefault$9 = helpers$1.valueOrDefault;
9161
9162core_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 */
9188function 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 */
9228function 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
9247function 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
9264function 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
9285function 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
9298function positionIsHorizontal(position) {
9299	return position === 'top' || position === 'bottom';
9300}
9301
9302function 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
9310var Chart = function(item, config) {
9311	this.construct(item, config);
9312	return this;
9313};
9314
9315helpers$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 */
10258Chart.instances = {};
10259
10260var 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 */
10271Chart.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 */
10280Chart.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 */
10289helpers$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 */
10298helpers$1.scaleMerge = mergeScaleConfig;
10299
10300var 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
10913function 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 */
10937function DateAdapter(options) {
10938	this.options = options || {};
10939}
10940
10941helpers$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
11019DateAdapter.override = function(members) {
11020	helpers$1.extend(DateAdapter.prototype, members);
11021};
11022
11023var _date = DateAdapter;
11024
11025var core_adapters = {
11026	_date: _date
11027};
11028
11029/**
11030 * Namespace to hold static tick generation functions
11031 * @namespace Chart.Ticks
11032 */
11033var 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
11104var isArray = helpers$1.isArray;
11105var isNullOrUndef = helpers$1.isNullOrUndef;
11106var valueOrDefault$a = helpers$1.valueOrDefault;
11107var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault;
11108
11109core_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 */
11167function 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
11179function 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
11206function 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 */
11224function 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
11282function getTickMarkLength(options) {
11283	return options.drawTicks ? options.tickMarkLength : 0;
11284}
11285
11286function 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
11299function 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
11310function 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
11317function 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
11329function 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
11345function 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
11366function 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
11377function 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
11395function 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
11426var 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
12541Scale.prototype._draw = Scale.prototype.draw;
12542
12543var core_scale = Scale;
12544
12545var isNullOrUndef$1 = helpers$1.isNullOrUndef;
12546
12547var defaultConfig = {
12548	position: 'bottom'
12549};
12550
12551var 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
12670var _defaults = defaultConfig;
12671scale_category._defaults = _defaults;
12672
12673var noop = helpers$1.noop;
12674var 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 */
12682function 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
12754var 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
12923var defaultConfig$1 = {
12924	position: 'left',
12925	ticks: {
12926		callback: core_ticks.formatters.linear
12927	}
12928};
12929
12930var DEFAULT_MIN = 0;
12931var DEFAULT_MAX = 1;
12932
12933function 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
12951function 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
12979function 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
12994var 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
13083var _defaults$1 = defaultConfig$1;
13084scale_linear._defaults = _defaults$1;
13085
13086var valueOrDefault$b = helpers$1.valueOrDefault;
13087var 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 */
13095function 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
13135var 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
13145function nonNegativeOrDefault(value, defaultValue) {
13146	return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue;
13147}
13148
13149var 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
13386var _defaults$2 = defaultConfig$2;
13387scale_logarithmic._defaults = _defaults$2;
13388
13389var valueOrDefault$c = helpers$1.valueOrDefault;
13390var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault;
13391var resolve$4 = helpers$1.options.resolve;
13392
13393var 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
13443function 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
13452function 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
13466function 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 */
13488function 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
13568function 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
13578function 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
13592function 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
13600function 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
13631function 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
13670function numberOrZero(param) {
13671	return helpers$1.isNumber(param) ? param : 0;
13672}
13673
13674var 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
13939var _defaults$3 = defaultConfig$3;
13940scale_radialLinear._defaults = _defaults$3;
13941
13942var deprecated$1 = helpers$1._deprecated;
13943var resolve$5 = helpers$1.options.resolve;
13944var valueOrDefault$d = helpers$1.valueOrDefault;
13945
13946// Integer constants are from the ES6 spec.
13947var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
13948var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
13949
13950var 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
13997var UNITS = Object.keys(INTERVALS);
13998
13999function sorter(a, b) {
14000	return a - b;
14001}
14002
14003function 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
14019function getMin(options) {
14020	return helpers$1.valueOrDefault(options.time.min, options.ticks.min);
14021}
14022
14023function 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 */
14042function 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/
14078function 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 */
14110function 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
14124function 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
14160function 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 */
14181function 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 */
14200function 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
14213function 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 */
14227function 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 */
14268function 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
14291function 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
14306function 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
14327var 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
14377var 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
14698var _defaults$4 = defaultConfig$4;
14699scale_time._defaults = _defaults$4;
14700
14701var scales = {
14702	category: scale_category,
14703	linear: scale_linear,
14704	logarithmic: scale_logarithmic,
14705	radialLinear: scale_radialLinear,
14706	time: scale_time
14707};
14708
14709var 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
14722core_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
14775core_defaults._set('global', {
14776	plugins: {
14777		filler: {
14778			propagate: true
14779		}
14780	}
14781});
14782
14783var 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] === '#')
14818function 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
14867function 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
14909function 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
14938function computeBoundary(source) {
14939	var scale = source.el._scale || {};
14940
14941	if (scale.getPointPositionForValue) {
14942		return computeCircularBoundary(source);
14943	}
14944	return computeLinearBoundary(source);
14945}
14946
14947function 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
14978function 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
14993function isDrawable(point) {
14994	return point && !point.skip;
14995}
14996
14997function 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
15029function 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
15079var 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
15145var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter;
15146var noop$1 = helpers$1.noop;
15147var valueOrDefault$e = helpers$1.valueOrDefault;
15148
15149core_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 */
15243function 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 */
15252var 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
15715function 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
15727var 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
15774var noop$2 = helpers$1.noop;
15775
15776core_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 */
15791var 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
15959function 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
15971var 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
16011var plugins = {};
16012var filler = plugin_filler;
16013var legend = plugin_legend;
16014var title = plugin_title;
16015plugins.filler = filler;
16016plugins.legend = legend;
16017plugins.title = title;
16018
16019/**
16020 * @namespace Chart
16021 */
16022
16023
16024core_controller.helpers = helpers$1;
16025
16026// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
16027core_helpers();
16028
16029core_controller._adapters = core_adapters;
16030core_controller.Animation = core_animation;
16031core_controller.animationService = core_animations;
16032core_controller.controllers = controllers;
16033core_controller.DatasetController = core_datasetController;
16034core_controller.defaults = core_defaults;
16035core_controller.Element = core_element;
16036core_controller.elements = elements;
16037core_controller.Interaction = core_interaction;
16038core_controller.layouts = core_layouts;
16039core_controller.platform = platform;
16040core_controller.plugins = core_plugins;
16041core_controller.Scale = core_scale;
16042core_controller.scaleService = core_scaleService;
16043core_controller.Ticks = core_ticks;
16044core_controller.Tooltip = core_tooltip;
16045
16046// Register built-in scales
16047
16048core_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
16057for (var k in plugins) {
16058	if (plugins.hasOwnProperty(k)) {
16059		core_controller.plugins.register(plugins[k]);
16060	}
16061}
16062
16063core_controller.platform.initialize();
16064
16065var src = core_controller;
16066if (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 */
16079core_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 */
16088core_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 */
16097core_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 */
16106core_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 */
16116core_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 */
16125core_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 */
16134core_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 */
16143core_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 */
16151core_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
16170return 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.